When a domain class is used as a command object and there is an id request parameter, the framework will retrieve the instance of the domain class from the database using the id request parameter.
How can i disable this feature, i do not want the framework go to the database to retrieve the domain instance
You can still do databinding from request params with a slightly more verbose style; change
def myAction(MyDomainClass myDomainClass) {
...
}
to
def myAction() {
MyDomainClass myDomainClass = new MyDomainClass(params)
...
}
This will not trigger a database call if there's an id in params and will ignore id and version properties since they're not bindable by default.
Related
In Spring Data Elasticsearch is there a way of detecting that the mapping on the index does not match the mapping created from the Entity object?
i.e.
I allowed Spring Data Elasticsearch to create the mapping originally from the entity model annotated with #Document and #Field
At a later point I add a new field to the model, but do not update the index mapping, this new field then won't be correctly configured until I re-index to the new mapping
So is there a way to detect such discrepancies so I will know which indexes need to have their mappings re-created, and documents re-indexed?
This is an interesting question, but the answer is not too complicated.
Let's assume we have a Foo entity class to be stored in an foo index and a FooRepository that uses this class. On
application startup, when the index does not exist, it will be created with the mapping derived from the entity.
In order to detect changes in the maping derived from the class and the one stored in Elasticsearch you can use an
approach like this:
#Component
public class FooMappingValidator {
private static final Logger LOGGER = LoggerFactory.getLogger(FooMappingValidator.class);
private final ObjectMapper objectMapper = new ObjectMapper();
private final ElasticsearchOperations operations;
public FooMappingValidator(ElasticsearchOperations operations) {
this.operations = operations;
}
#Autowired
public void checkFooMapping() {
var indexOperations = operations.indexOps(Foo.class);
if (indexOperations.exists()) {
LOGGER.info("checking if mapping for Foo changed");
var mappingFromEntity = indexOperations.createMapping();
var mappingFromEntityNode = objectMapper.valueToTree(mappingFromEntity);
var mappingFromIndexNode = objectMapper.valueToTree(indexOperations.getMapping());
if (!mappingFromEntityNode.equals(mappingFromIndexNode)) {
LOGGER.info("mapping for class Foo changed!");
indexOperations.putMapping(mappingFromEntity);
}
}
}
}
This is a Spring component that has ElasticsearchOperations injected and that has a method that is annotated with
#Autowired. An autowired method is executed after all the dependencies have been injected in the beans. This means
it runs before the normal application logic is started.
In this method we first get an IndexOperations instance for our entity class. Next we check if the index exists,
if it doesn't, we do not need to check.
In the next step we get the current mapping from the entity and convert it to a JsonNode and do the same with the
mapping we retrieve from Elasticsearch. We use JsonNodes here because the have an equals() method that
does the comparison we need.
If we detect that the both mappings are different, we write the new one to the index with the putMapping() method.
NOTE:
This only works when new properties are added to the entity, as existing mappings cannot be changed in Elasticsearch,
there you'd need reindexing.
Suppose I have Employee domain class, I want to create object of domain class from params map coming from UI side.
I can create object in two ways as follows
Normal way
Employee employee = new Employee(name: params.name, rollNo:
params.rollNo)
and so on. If domain class has 20 variables, then we need to write all variables in above constructor.
Following is best way to create object
Employee employee = new Employee(params)
Above constructor will populate object with matching params. Right.
Now my question comes here.
If suppose I have existing domain class object fetched from DB, Now I want to update this object from params map coming from UI.
What is best way to do this (like we do in above second option).
I think it is best to use command objects and bind it to the Employee.
here is sample pseudo code:
class EmployeeMgmtController {
def editEmp(EmployeeCmd cmd){
Employee editEmp = Employee.get(1)
editEmp.properties = cmd
editEmp.save()
}
}
class EmployeeCmd{
String id
static constraints = {
id blank:false,nullable:false
}
}
or,
you if your on controller, and still want to use params (and exclude any fields that you don't want to bind):
bindData(editEmp, params, [exclude:['firstName', 'lastName']])
If you want to achieve that in a service class, make your service implement grails.web.databinding.DataBinder then use the bindData method as demonstrated below.
import grails.web.databinding.DataBinder
class MyAwesomeService implements DataBinder {
/**
* Updates the given instance of a domain class to have attribute values specified
* in "newData" map.
*/
MyDomain updateMyDomainAttributes(MyDomain myDomianInstance, Map newData) {
bindData(myDomianInstance, newData)
myDomianInstance.save(flush: true)
}
}
In my Grails 2.3.8 app, I've defined the following controller action
class RegisterController {
def register(User user) {
render text: "User name is '$user.name'"
}
}
The user argument is a domain class instance. If I invoke this controller with the URL
http://localhost:8080/myapp/register/register
I get a NullPointerException. However my understanding of databinding is that if this action is invoked without any parameters, the argument should be assigned a new User()
However my understanding of databinding is that if this action is
invoked without any parameters, the argument should be assigned a new
User()
That is not necessarily the case. For domain class command objects if no parameters are present a new instance is only created for POST requests.
From http://grails.org/doc/2.4.0.RC1/guide/theWebLayer.html#commandObjects...
If the command object's type is a domain class and there is no id
request parameter then null will be passed into the controller action
unless the HTTP request method is "POST", in which case a new instance
of the domain class will be created by invoking the domain class
constructor.
That text may be missing from the 2.3.8 docs. I will verify that and add it if necessary.
What if you modify:
'$user.name'
To be:
'${user?.name}'
I have two domain objects:
Customer
CustomerConfig
Customer has a 1-1 association with CustomerConfig. There is a default CustomerConfig with default settings for Customers who do not have an explicitly saved CustomerConfig e.g.
def getConfig() {
if (!config) {
return new CustomerConfig() //the default settings
} else {
return config
}
}
The problem I am having is that when I return the default setting GORM saves the CustomerConfig instance to the database as it appears to GORM that it has changed.
In fact I do not want to save it to the database as I want to be able to control the default settings for customer and make updates for customers until they have an explicitly saved config.
I also am trying avoid using conditional logic as follows:
def config = customer.config?:new CustomerConfig()
And encapsulate it in the Customer domain object. It seems like there's a different pattern I should be following. Would welcome any advice.
Thanks,
cowper
IMHO, it's never a good idea to change behavior of default getter/setter as those are managed by GORM.
You can do something like this
class Customer {
static transients = ['setting']
public CustomerConfig getSetting(){
return getConfig()?:new CustomerConfig()
}
What's the best/easiest way to get a list of the persistent properties associated with a given GORM domain object? I can get the list of all properties, but this list contains non-persistent fields such as class and constraints.
Currently I'm using this and filtering out the list of nonPersistent properties using a list I created:
def nonPersistent = ["log", "class", "constraints", "properties", "errors", "mapping", "metaClass"]
def newMap = [:]
domainObject.getProperties().each { property ->
if (!nonPersistent.contains(property.key)) {
newMap.put property.key, property.value
}
}
There seems like there must be a better way of getting just the persistent properties.
Try this:
import org.codehaus.groovy.grails.commons.DefaultGrailsDomainClass
...
def d = new DefaultGrailsDomainClass(YourDomain.class)
d.persistentProperties
Here's a link to the Grails API for GrailsDomainClass (it's a link to an older version; I couldn't find a newer one after some quick searches). It's got a getPersistentProperties() (used in the code snippet above). You can traverse the API documentation to see what other methods might be useful to you.
If you want an example, do a grails install-templates and then look at src/templates/scaffolding/create.gsp. There's a block in there where it iterates over the persistent domain properties.
Now (strarting Grails 2.x) you don't even have to instantiate new DefaultGrailsDomainClass(...) and avoid unnecessary code executions. All domain class objects have injected property domainClass:
def domainObject = new YourDomain()
domainObject.domainClass.persistentProperties
Or, if you haven't domain class object, you can get DefaultGrailsDomainClass from application context by domain class name - each domain class has a DefaultGrailsDomainClass registered as a Spring bean. So you can use, for example, Holders (assuming your domain class name is 'Foo'):
def defaultGrailsDomainClass = Holders.applicationContext.getBean("FooDomainClass")
defaultGrailsDomainClass.persistentProperties
As of grails 3.3.0
All code that uses the GrailsDomainClass or GrailsDomainClassProperty classes should be re-written to use the mapping context api.
To get started, inject the grailsDomainClassMappingContext bean. See the api documentation for more information on the MappingContext, PersistentEntity (GrailsDomainClass), and PersistentProperty(GrailsDomainClassProperty)
For example:
class MyService {
def grailsDomainClassMappingContext //inject
def accessDomainProperties(Class clazz) {
PersistentEntity entityClass = grailsDomainClassMappingContext.getPersistentEntity(clazz.name)
List<PersistentProperty> persistentPropertyList = entityClass.persistentProperties
persistentPropertyList.each { property ->
println property.name
}
}
}
Hope this helps someone.