Access object's properties inside named query - grails

Is it possible to access an object's property inside a named query?
Here is a sample domain class
class Publication {
String title
String author
Date datePublished
Integer numberOfPages
static namedQueries = {
newerPublications {
//Will throw an error, cannot access a variable declared in a dynamic context.
gt 'datePublished', this.datePublished
}
}
}

You can do something like this:
newerPublications { Publication newerThan ->
gt 'datePublished', newerThan.datePublished
}
You would then call it as Publication.newerPublications(referencePublication).list()

Related

How can Grails domain object members have default names based on a computed value?

This is what I'm trying to do:
class Test {
String name
static mapping = {
name defaultValue: "test_${Test.count()}"
}
}
So when a new "test" object is created the name is test_1, test_2, test_3 etc. depending on how many test objects already exist. The above does not work because "test.count was used outside of a Grails application"
You could initialize the property instead of specifying the value via the mapping closure.
class Test {
String name = "test_${Test.count()}"
}
or
class Test {
String name = initName()
private static String initName() {
def count = Test.count()
return "test_" + count
}
}

Adding Dynamic Fields to Domain Object in Grails

I am trying to find a way to add dynamic fields to a grails domain class. I did find the dynamic domain class plugin based on Burt's article, but this is way too much for our needs.
Supposed we have a domain class of person:
class Person extends DynamicExtendableDomainObject {
String firstName
String lastName
static constraints = {
firstName(nullable: false, blank: false, maxSize: 50)
lastName(nullable: false, blank: false)
}
}
Now customer a wants to also have a birthdate field in this. By using some sort of management tool, he adds this extra field in the database.
Customer b wants to also have a field middle name, so he is adding the field middle name to the person.
Now we implemented a DynamicExtendableDomainObject class, which the Person class inherits from. This adds a custom field to each Domain class inheriting from this to store the dynamic properties as JSON in it (kind of like KiokuDB in Perl stores them).
Now when Person is instantiated, we would like to add those dynamic properties to the Person class, to be able to use the standard Grails getter and setter as well as Templating functions for those.
So on customer a we could use the scaffolding and person would output firstName, lastName, birthDate, on customer b the scaffolding would output firstName, lastName, middleName.
The storing of the properties will be implemented by using the saveinterceptor, to serialize those properties to JSON and store them in the special field.
But we have not yet found a way to add these JSON properties dynamically to the domain class during runtime. Is there a good way to handle this? And if so, how to best implement this?
You can try to add the properties at runtime to the DomainClass of type DynamicExtendableDomainObject by expanding getProperty(), setProperty(), setProperties() in the metaClass and then use beforeUpdate(), beforeInsert() and afterLoad() to hook into Persistence.
For example in Bootstrap (or service):
def yourDynamicFieldDefinitionService
for(GrailsClass c in grailsApplication.getDomainClasses()){
if(DynamicExtendableDomainObject.isAssignableFrom(c.clazz)){
Set extendedFields = yourDynamicFieldDefinitionService.getFieldsFor(c.clazz)
//getProperty()
c.clazz.metaClass.getProperty = { String propertyName ->
def result
if(extendedFields.contains(propertyName)){
result = delegate.getExtendedField(propertyName)
} else {
def metaProperty = c.clazz.metaClass.getMetaProperty(propertyName)
if(metaProperty) result = metaProperty.getProperty(delegate)
}
result
}
//setProperty()
c.clazz.metaClass.setProperty = { propertyName , propertyValue ->
if(extendedFields.contains(propertyName)){
delegate.setExtendedField(propertyName, propertyValue)
delegate.blobVersionNumber += 1
} else {
def metaProperty = c.clazz.metaClass.getMetaProperty(propertyName)
if(metaProperty) metaProperty.setProperty(delegate, propertyValue)
}
}
//setProperties()
def origSetProperties = c.clazz.metaClass.getMetaMethod('setProperties',List)
c.clazz.metaClass.setProperties = { def properties ->
for(String fieldName in extendedFields){
if(properties."${fieldName}"){
delegate."${fieldName}" = properties."${fieldName}"
}
}
origSetProperties.invoke(delegate,properties)
}
}
}
with
abstract DynamicExtendableDomainObject {
String yourBlobField
Long blobVersionNumber //field to signal hibernate that the instance is 'dirty'
Object getExtendedField(String fieldName){
...
}
void setExtendedField(String fieldName, Object value){
...
}
def afterLoad(){
//fill your transient storage to support getExtendedField + setExtendedField
}
def beforeUpdate(){
//serialize your transient storage to yourBlobField
}
def beforeInsert(){
//serialize your transient storage to yourBlobField
}
}

Grails named query

I have two domain classes, Alpha and Beta.
class Beta{
String betaName
int age
}
class Alpha{
String name
static belongsTo=[creator:Beta]
static namedQueries = {
alphaByUser{ param ->
eq creator.betName,param.betaName
}
alphaByAge {param ->
eq creator.age,param
}
}
}
Now when I call for example Alpha.alphaByUser(betaUser).list() I keep getting things like:
java.lang.NoSuchMethodException: Unknown property 'create' on class 'class org.codehaus.groovy.grails.commons.DefaultGrailsDomainClass'
Or
object is not an instance of declaring class
I just can't seem to get past it...
Any suggestions?
Your criteria syntax is a bit off. Try this:
static namedQueries = {
alphaByUser{ param ->
creator {
eq 'betName', param.betaName
}
}
}

Transient property in Grails domain

I have a Grails domain called People, and I want to check that each People has childs or not. Childs are other People objects. Here is my domain structure:
class People implements Serializable {
static constraints = {
name (nullable : false, unique : true)
createdBy (nullable : false)
creationDate (nullable : false)
}
static transients = ['hasChild']
static mapping = {
table 'PEOPLE'
id generator: 'sequence', params : [sequence : 'SEQ_PK_ID']
columns {
id column : 'APEOPLE_ID'
parentPeople column : 'PARENT_PEOPLE_ID'
}
parentPeople lazy : false
}
People parentPeople
String name
String description
Boolean hasChild() {
def childPeoples = People.createCriteria().count {
eq ('parentPeople', People)
}
return (childPeoples > 0)
}
}
But I cannot call people.hasChild() at anywhere. Could you please helpe me on this? Thank you so much!
It's because in eq ('parentPeople', People), Grails can't understand what "People" is (it's a class). You should replace "People" by this. For example:
static transients = ["children"]
def getChildren() {
def childPeoples = People.findAllByParentPeople(this, [sort:'id',order:'asc'])
}
Another way to get the same result is to use Named Queries. It seems more concise and was created specifically for this purpose. I also like it because it fits the pattern of static declarations in a domain model and it's essentially a criteria, which I use throughout my applications. Declaring a transient then writing a closure seems a bit of a work-around when you can declare named queries ... just my opinion.
Try something like this:
static namedQueries = {
getChildren {
projections {
count "parentPeople"
}
}
}

Grails: use domain method in named query

in my domain model, I have a method that does something with my data.
e.g.
class Person {
String lastname
String firstname
String bigname() {
return lastname.toUpperCase()
}
static namedQueries = {
withBigname { name ->
eq(this.bigname(), name)
}
}
}
I want to use this method like a property in the named query, but this.bigname() only throws a java.lang.IncompatibleClassChangeError-Exception.
Does anyone know how to use domain methods in criteria and named queries?
Update: I now tried this:
class Person {
String lastname
String firstname
String bigname
static transients = [ 'bigname' ]
def getBigname() {
return lastname.toUpperCase()
}
static namedQueries = {
withBigname { name ->
eq('bigname', name)
}
}
}
But it only results in a "could not resolve property: bigname"-exception...
You cannot use class methods in queries, because queries are actually translated to SQL.
You might be able to get what you need by using writing the complexity in SQL a "SQL Restriction". Search for "SQL Restriction" on http://grails.org/doc/2.0.x/guide/GORM.html
HTH
Try to name the method in JavaBean getter and setter notation.
Rename method bigname() to getBigname().
On a static closure, you don't have a this. You'll either need to store the bigname in the Database, do some sort of case insensitive criteria.
looks like you are trying to accomplish this:
class Person {
String lastname
String firstname
static namedQueries = {
withName { name ->
eq('lastname', name, [ignoreCase: true])
}
}
}

Resources