Using static "hasOne" property in Grails controller class - grails

Hopefully this will be an easy one to answer. I created a class in Grails called player which has this information:
class Player {
String steamId
String name
String portrait
static hasMany = {playerStatistics:PlayerStatistics}
static hasOne = {playerForumProfile:PlayerForumProfile}
}
For clarification, a Player object can have one PlayerForumProfile object, but the player object is always created BEFORE the PlayerForumProfile object. My issue is with accessing the playerForumProfile object associated with the "hasOne" property within the controller of the PlayerForumProfile class. I had assumed that doing this:
def playerForumProfileInstance = new PlayerForumProfile()
def playerInstance = Player.get(params.id)
playerForumProfileInstance = playerInstance.playerForumProfile
would result in pulling the PlayerForumProfile object associated with the playerInstance object into the playerForumProfileInstance variable, however when I try this, Grails throws an error telling me there is no such property as playerForumProfile. Is it possible to access the hasOne properties' object in such a manner or do I need to do something else?
Edit: I also tried modifying the Player class so it included a variable called playerForumProfile and editing PlayerForumProfile so it had a belongsTo declaration, but this kept resulting in a null pointer exception when running my app.
Edit: A little more info, I created a new grails app from scratch and created the relationship the way it appears in the Grails documentation and it ran with no problem so I'm thinking it may be easier just to start a new app and copy the files over.

There is hasOne feature in GORM: http://grails.org/doc/latest/ref/Domain%20Classes/hasOne.html

This answer is no longer correct for grails 2.X and greater, it was true in 2009 when originally answered.
There isn't a "hasOne" property in GORM, it's either belongsTo:
static belongsTo = [playerForumProfile: PlayerForumProfile]
or just a regular typed definition of the attribute name if there isn't a cascading relationship implied by belongsTo:
PlayerForumProfile playerForumProfile
See the one-to-one GORM documentation for details.

Related

Grails, GORM, relationship. Optional child records

For example, I've parent class Cafee:
class Cafee {
String name
static hasMany = [
admin: Person
]
}
and a child class Person:
class Person {
String name
static belongsTo = [cafee: Cafee]
}
I've done some records to Cafee using:
def user = new Person(name: "Andrew")
def a = new Cafee(name: "Tarelka")
.addToAdmin(user)
.save()
Adding child to parent works fine, but when I trying to create Person-instance separately, for example:
def visitor = new Person(username: 'testerUser', password:'password', firstName:'Иван', lastName:'Иванов', email:'ivanov#gmail.com', isAdminCafee: false)
visitor.save(flush:true)
I get an error:
ERROR context.GrailsContextLoaderListener - Error initializing the application: object references an unsaved transient instance - save the transient instance before flushing: restorator.auth.Person; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: restorator.auth.Person
Message: object references an unsaved transient instance - save the transient instance before flushing: restorator.auth.Person; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: restorator.auth.Person
How to fix it?
There are two things going on here that aren't entirely obvious. One is that this form of belongsTo
static belongsTo = [cafee: Cafee]
is bidirectional, as compared to
static belongsTo = [Cafee]
which is undirectional; in both variants a Cafee can access its associated Person instances via its admin collection, but using the second syntax there's no direct way for a Person to know which Cafee it's associated with.
Declaring a hasMany like you did creates a Set of Persons in the class, and its name is the key you used in the hasMany map, in this case admin; it's as if you had added
Set<Person> admin
but you shouldn't because it's redundant - an AST transform adds that property to the bytecode.
Similarly, when declaring
static belongsTo = [cafee: Cafee]
a field of type Cafee with the name cafee is added to the class. It's as if you added
Cafee cafee
but again, please don't manually add either, just be aware that they're there.
So the problem is that persistent properties are not-null by default unless you override with nullable: true, so if you printed out the errors for that Person instance you'd see at least one complaining that the non-nullable cafee property is null. This works with addToAdmin because that method does a lot. If instantiates the set to a new Set interface implementation (probably just a HashSet) if it's null (which should only ever occurr when the domain class instance is new; a persistent set will never be null, just empty or containing items), then adds the Person to the collection, and finally if the relationship is bidirectional, it sets the backreference on the Person to the owning Cafee.
So all you're missing is setting the Cafee manually, either as part of the map constructor
user = new Person(cafee: a, username: 'testerUser', ...)
or later in the workflow (but before validation or saving)
user = new Person(username: 'testerUser', ...)
...
user.cafee = a
...
user.save()

Grails Saving Object With Composite Key Throws Error

So, my team has been having multiple issues while upgrading our existing app from Grails 1.3.7 to 2.1.0. The latest headache occurs when trying to save a domain class object that has a composite key based on two other domain objects.
We are hanging Grails on a legacy database which we cannot readily change, so all of the domain classes have custom mappings to hook up with it. Below is a quick, slimmed down version of the domain classes in question.
Class Product {
Short prodKey
String name
static hasMany = [groupProduct: GroupProduct]
//Also includes mapping to legacy db and simple constraints
}
Class Group {
Short groupKey
String name
static hasMany =[ groupProduct: GroupProduct]
//This domain class has several other mappings and variables, but they are not relevant
}
Class GroupProduct {
Group group
Product product
Character indicator
static belongsTo = [Product,Group]
static mapping = {
id composite: ["group", "product"]
group lazy:false, column:"GROUP_KEY", joinTable:"GROUP"
product lazy:false, column:"PROD_KEY", joinTable:"PRODUCT"
version false
}
//Only constraint is indicator is Y or N
}
In the app a user is able to select multiple products for a group to turn on or off via a checkbox list. The parameters contain the groupKey and a list of all checked products. The controller gets an instance of the specified group and then a list of all Products. The products are matched against the list in the parameters, every time a match is found a GroupProduct object is made with the indicator set to 'Y', otherwise a GroupProduct object is made with the indicator set to 'N'.
Class GroupProductController{
//allowedMethods and other actions...
def update = {
def groupInstance = Group.get(params.GroupId)
def groupProducts= []
def products= Products.list()
products.each{
def indicator = ...//code to get value of check box for this Product. Returns either Y or N, works as expected
def groupProduct= new GroupProduct(group:groupInstance ,
product:it,
indicator: indicator)
groupProducts.add(groupProduct)
}
groupInstance.discard()
groupProducts.each{
it.save(failOnError: true, flush:true)//This line throws a DB2 SQL error. SQLCODE=-407
}
}
}
Resulting error is:
org.hibernate.util.JDBCExceptionReporter|DB2 SQL Error: SQLCODE=-407, SQLSTATE=23502, SQLERRMC= , DRIVER=3.50.152
org.codehaus.groovy.grails.orm.hibernate.events.PatchedDefaultFlushEventListener|Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: could not update: [GroupProduct#component[group,product]{product=Product#1, group=Group#938926168}]
at com.controllers.ProductGroupController$_closure2_closure8.doCall(ProductGroupController.groovy:86)
at com.nationwide.nas.beam.controllers.ProductGroupController$_closure2.doCall(ProductGroupController.groovy:79)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)
Caused by: com.ibm.db2.jcc.b.lm: DB2 SQL Error: SQLCODE=-407, SQLSTATE=23502, SQLERRMC= , DRIVER=3.50.152
at com.ibm.db2.jcc.b.wc.a(wc.java:575)
at com.ibm.db2.jcc.b.wc.a(wc.java:57)
at com.ibm.db2.jcc.b.wc.a(wc.java:126)
at com.ibm.db2.jcc.b.tk.b(tk.java:1593)
at com.ibm.db2.jcc.b.tk.c(tk.java:1576)
at com.ibm.db2.jcc.t4.db.k(db.java:353)
at com.ibm.db2.jcc.t4.db.a(db.java:59)
at com.ibm.db2.jcc.t4.t.a(t.java:50)
at com.ibm.db2.jcc.t4.tb.b(tb.java:200)
at com.ibm.db2.jcc.b.uk.Gb(uk.java:2355)
at com.ibm.db2.jcc.b.uk.e(uk.java:3129)
at com.ibm.db2.jcc.b.uk.zb(uk.java:568)
at com.ibm.db2.jcc.b.uk.executeUpdate(uk.java:551)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
... 5 more
The error occurs when trying to save the GroupProduct objects. According to IBM the error code -407 is caused by AN UPDATE, INSERT, OR SET VALUE IS NULL, BUT THE OBJECT COLUMN column-name CANNOT CONTAIN NULL VALUES. However, none of the variables for the GroupProducts are actually null. The Group and Product instances are pulled straight from the database, which means they have already been validated and shouldn't have any constraint violations, and I can see that the indicator field is being set correctly.
There is also no problem when running this code under the original 1.3.7 version of the project. If anyone could shed some light on this I'd be very grateful. Thanks
After much debugging and hunting through code, I managed to find the issue. All of our domain classes extend an abstract base domain, which has a createdTimestamp and updatedTimestamp field. Before doing an insert we set both fields, and before doing an update we update the updatedTimestamp.
The issue was that when we were saving, the new object had a null createdTimestamp field, which was throwing the error. Added in code to check if the GroupProduct object we were making already existed, and if so set the new objects createdTimestamp field to the existing object's before saving. Now everything works as expected.
Its strange that Grails 1.3.7 did not have any issues with this code, though. Only thing I can figure is that it automatically associated the new objects with existing ones in the DB. Probably the strangest behavior change I've found during the upgrade process. Hope this helps anyone running into a similar issue.

How to dynamically add a property / field to a domain class in Grails?

For a project I'm currently working on I need to dynamically add properties to a domain class and persist them later in the database. In general, I need a key/value store attached to a "normal" domain class. Sadly I cannot use a NoSQL database (e.g. Redis).
My approach would be to handle the additional properties on a save() by identifying them within afterInsert or afterUpdate and writing them to another table - I would prefer not to use a map property within the domain class but an additional "Field" table (to better support searches).
I tried to add properties using the metaClass approach:
person.metaClass.middlename = "Biterius"
assert person.middlename == "Biterius" // OK
This works and I can identify the additional properties in the afterInsert/afterUpdate methods but it seems that I cannot change the value thereafter - i.e., the following does not work:
person.middlename = "Tiberius"
assert person.middlename == "Tiberius" // FAIL
Then I tried an Expando approach by extending the Person class by the Expando class (directly ("Person extends Expando") and via an abstract intermediate class ("Person extends AbstractPerson" and "AbstractPerson extends Expando")).
def person = new Person()
assert person in Person // OK
assert person in AbstractPerson // OK
assert person in Expando // OK
Both variants did not work - I could assign values to arbitrary "properties" but the values were not stored!
person.mynewproperty = "Tiberius" // no MissingPropertyException is thrown
println person.mynewproperty // returns null
So how can I add properties to a domain class programmatically during runtime, change them and retrieve them during afterInsert or afterUpdate in order to "manually" store them in a "Fields" table?
Or am I doing something completely wrong? Are there other / simpler ways to do this?
What about turning your DB into a "NoSQL" one?
In one of my projects, I just used a String-property to store a map as JSON-Object.
For Groovy it's not a big problem to convert between a map and a JSON-Object. And since you can access a map just like an object with properties, I found this solution very convenient.
Only drawback: you have to plan the size of your String-property in advance...
Update: sorry, just read that you want to support searches...
what about
class Person {
...
static hasMany = [extProperties:KeyValue]
...
def invokeMethod(String name, args) {
if (name.startsWith('get')) {
//an unknown properties's getter is called
}
//add same for setter
}
}
class KeyValue {
String key
String value
}
I guess such a schema would give you all freedom you need. Even without the hasMany, you can make use of invokeMethod to handle your external tables...
The getter and setter can save your values in a transient string propertie (static transients = ['myTransientProperty']). This property should be available in the afterInsert / `afterUpdate´ events.
Why don't you just create a map of strings on the domain object and store your extra data there manually? Unless you're storing complex data you should be able to cast anything you need to/from a string.

Telling GORM not to persist a property

Is there a way of telling GORM not to persist a property? I'm planning to define a confirm password property on my User class that I'll use for validation, but shouldn't be persisted.
Using transient key word GORM can be directed not to persist specific property.
Following code snippets shows the use of transient proerties
class Book {
static transients = [ "digitalCopy" ]
static constraints = {
releaseDate(nullable: true)
}
String author
String title
Date releaseDate
File digitalCopy
}
digitalCopy property included in transient declaration notifies GORM not to persist digitalCopy
OK - just managed to answer my own question with some more searching. Should have been more patient.
A static transients property "defines a list of property names that should not be persisted to the database. This is often useful if you have read-only getters that include logic."
http://grails.org/doc/latest/ref/Domain%20Classes/transients.html

org.springframework.dao.InvalidDataAccessApiUsageException when using grails dynamic finder

Using Grails i'm trying a dynamic finder like this one
Policy.findAllByResourceAndUser(resource,user)
But When i call this, grails raise this exception
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: object references
an unsaved transient instance - save the transient instance before flushing: gmedia.User;
nested exception is org.hibernate.TransientObjectException: object references an unsaved
transient instance - save the transient instance before flushing: gmedia.User
Do we need to save the parameter of the finder?
Where i'm wrong?
http://www.grails.org/DomainClass+Dynamic+Methods#findAllBy*
Policy.findAllByResourceAndUser(resource,user)
capital "B" in "By" is the first thing I see wrong? Is that a type on the question?
def res = new Resource(name:"resource name").save()
def user = new User(name:"My Name").save()
def policy = new Policy( user:user, resource:res, right: "right string").save()
println Policy.findAllByResourceAndUser(res,user)
not elegant, but you get the idea, there must be a problem in the way you are saving your objects
your user object will "never" get saved with that code... you have to specify values for all of you properties or define your constraints appropriately. I think you should review the documentation for Domain Objects in Grails because it appears there are fundamental problems in your approach see http://www.grails.org/GORM+-+Creating+a+domain+class
#Aaron Saunders
these are two domain class
class Resource{
static contraints={}
}
class User extends Resource{
String name
String password
String email
Date creationDate
Date lastModicationDate
}
class Policy{
Resource resource
User user
String right
static mapping={
user cascade:'all-delete-orpahn'
resource cascade:'all-delete-orpahn'
}
Maybe it a consequence of the inheritance between User and Resource
i faced the same issue today..i found the solution...there is a foreign key relation which is getting set as NULL.....so when it tries to save null it throws this exception..so make sure all the values are not null...else u may need to change the cascade definition in ur mapping xml..

Resources