Grails cannot clear a hasMany set using cascade - grails

I am trying to clear out the data from the userDeptses set but when I call the clear() method and try to save I get the following error
Caused by: java.lang.UnsupportedOperationException: queued clear cannot be used with orphan delete
I have the cascading set properly, I have even tried using just delete-orphan but still have issues. All relevant classes have equals and hashCode methods implemented: AppSystemUser UserDepts Department
All documentation and articles I have read online say that using the combination of clear() and all-delete-orphan is supposed to work, but not for me. Any help is greatly appreciated.
Grails 3.1.4
Controller:
AppSystemUser user = AppSystemUser.findBySystemUserid(cmd.netid);
user.userDeptses.clear();
userMgmtService.saveAppSystemUser(user);
AppSystemUser:
class AppSystemUser {
String systemUserid
String email
String fullName
Date lastLogin
Boolean active
Set appSystemUserRoles = [];
Set userCollegeses = [];
Set userDeptses = [];
static hasMany = [appSystemUserRoles: AppSystemUserRole,
applicationExtensions: ApplicationExtension,
userCollegeses: UserColleges,
userDeptses: UserDepts]
static mapping = {
version false
fullName column: 'fullName'
lastLogin column: 'lastLogin'
id name: "systemUserid", generator: "assigned"
appSystemUserRoles cascade: "save-update, all-delete-orphan"
userCollegeses cascade: "save-update, all-delete-orphan"
userDeptses cascade: "save-update, all-delete-orphan"
}
....
UserDept:
class UserDepts {
Boolean active
AppSystemUser appSystemUser
Department department
static belongsTo = [AppSystemUser, Department]
static mapping = {
version false
appSystemUser column: "system_userid"
}
....
UserMgmtService:
#Transactional
class UserMgmtService {
def saveAppSystemUser(AppSystemUser user) {
user.save();
}
}

I was not able to find out how to successfully use the clear() method of the Set so I just created a simple workaround that does the trick
def static hibernateSetClear(Set data) {
if(data) {
Iterator i = data.iterator();
while (i.hasNext() && i.next()) {
i.remove();
}
}
}
Just iterate through the Set and remove each item individually. This works perfect and I just call this method instead of clear() whenever I need to clear a Set

Related

How to detect which field has been updated in afterUpdate|beforeUpdate GORM methods

i use afterUpdate method reflected by GORM API in grails project.
class Transaction{
Person receiver;
Person sender;
}
i want to know which field modified to make afterUpdate behaves accordingly :
class Transaction{
//...............
def afterUpdate(){
if(/*Receiver is changed*/){
new TransactionHistory(proKey:'receiver',propName:this.receiver).save();
}
else
{
new TransactionHistory(proKey:'sender',propName:this.sender).save();
}
}
}
I can use beforeUpdate: and catch up the object before updating in global variable (previous as Transaction), then in afterUpdate, compare previous with the current object.
Could be?
Typically this would be done by using the isDirty method on your domain instance. For example:
// returns true if the instance value of firstName
// does not match the persisted value int he database.
person.isDirty('firstName')
However, in your case if you are using afterUpdate() the value has already been persisted to the database and isDirty won't ever return true.
You will have to implement your own checks using beforeUpdate. This could be setting a transient value that you later read. For example:
class Person {
String firstName
boolean firstNameChanged = false
static transients = ['firstNameChanged']
..
def beforeUpdate() {
firstNameChanged = this.isDirty('firstName')
}
..
def afterUpdate() {
if (firstNameChanged)
...
}
...
}

BootStrap.groovy content doesn't update the content

I'm new to Grails and study it via InfoQ's "Getting Started With Grails" book.
Running through it I've faced a problem with the BootStrap.groovy file:
I've edited it as the book says.
Run-app it - and the data I've entered into BootStrap.groovy has been shown.
I've updated the bootstrapping values but they are not changed.
No errors are shown in the console.
What I've tried:
exchanging def's places;
set Runner's class value to nullables;
changed .save(failOnError:true) to .save(flush:true).
Even after I've changed the Race class' bootstrapping values it looks like it hasn't been changed.
Looks like the value is taken from somewhere else than the BootStrap.groovy?
I use Grails 2.2.1 with a PostgreSQL DB.
The classes' code is below:
package racetrack
class Runner {
static constraints = {
firstName(blank:false)
lastName(blank:false)
dateOfBirth(nullable:true)
gender(inList:["M","F"])
address(nullable:true)
city(nullable:true)
state(nullable:true)
zipcode(nullable:true)
email(email:true)
}
static hasMany = [registrations:Registration]
/*static mapping = {
sort "email"
}*/
String firstName
String lastName
Date dateOfBirth
String gender
String address
String city
String state
String zipcode
String email
String toString(){
"${firstName} ${lastName} (${email})"
}
}
and
package racetrack
class BootStrap {
def init = { servletContext ->
def begun = new Runner(firstName:"Marathon",
lastName:"Runner",
dateOfBirth:"",
gender:"M",
address:"",
city:"",
state:"",
zipcode:"",
email:"me#me.ru"
)
begun.save(flush:true)
def zabeg = new Race(name:"Run SPB",
startDate:new Date() + 360*30,
city:"Moscow",
state:"Moscow",
cost:10.0,
distance:42.0,
maxRunners:10)
zabeg.save(flush:true)
}
def destroy = {
}
}
EDIT: could this be happening due to any generate-* scripts been run?
Race.groovy:
package racetrack
class Race {
static hasMany = [registrations:Registration]
String name
Date startDate
String city
String state
BigDecimal distance
BigDecimal cost
Integer maxRunners
static constraints = {
name(blank:false, maxSize:50)
startDate(validator: {
return (it >= new Date())
}
)
city()
state(inList:["Moscow", "Volgograd", "SPb", "NN"])
distance(min:0.0)
cost(min:0.0, max:100.0)
maxRunners(min:0, max:100000)
}
static mapping = {
sort "startDate"
}
BigDecimal inMiles(){
return distance*0.6214
}
String toString(){
return "${name}, ${startDate.format('MM/dd/yyyy')}"
}
}
BootStrap should be in grails-app/conf instead. Refer this.
Try to print the errors of the new instance you save by adding
print begun.errors
See if somethings comes up.
You need to remove
def index() { }
from the controllers.
I had the same issue. Now it is fixed.

Grails: querying hasMany association

I know there are several questions on this subject but none of them seem to work for me. I have a Grails app with the following Domain objects:
class Tag {
String name
}
class SystemTag extends Tag {
// Will have additional properties here...just placeholder for now
}
class Location {
String name
Set<Tag> tags = []
static hasMany = [tags: Tag]
}
I am trying to query for all Location objects that have been tagged by 1 or more tags:
class LocationQueryTests {
#Test
public void testTagsQuery() {
def tag = new SystemTag(name: "My Locations").save(failOnError: true)
def locationNames = ["L1","L2","L3","L4","L5"]
def locations = []
locationNames.each {
locations << new Location(name: it).save(failOnError: true)
}
(2..4).each {
locations[it].tags << tag
locations[it].save(failOnError: true)
}
def results = Location.withCriteria {
tags {
'in'('name', [tag.name])
}
}
assertEquals(3, results.size()) // Returning 0 results
}
}
I have validated that the data is being created/setup correctly...5 Location objects created and the last 3 of them are tagged.
I don't see what's wrong with the above query. I would really like to stay away from HQL and I believe that should be possible here.
Welcome to hibernate.
The save method informs the persistence context that an instance should be saved or updated. The object will not be persisted immediately unless the flush argument is used
if you do not use flush it does the saves in batches so when you setup your query right after the save it appears that the data is not there.
you need to add
locations[it].save(failOnError: true, flush:true)
You should use addTo* for adds a domain class relationship for one-to-many or many-to-many relationship.
(2..4).each {
locations[it].addToTags(tag)
}

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 Enum Mapping

in Grails, Is there a way to limit the size of the column to which the enum is mapped. In the following example, i would like the column type to be char(2)
enum FooStatus {
BAR('br'), TAR('tr')
final static String id
}
class Foo {
FooStatus status
static constraints = {
status(inList:FooStatus.values()*.id,size:2..2)
}
}
both inList and size do not have any effect when exporting the schema, the column type keeps its default value (varch(255))
Maybe i could do that if i define a new UserType. Any idea ?
Thank you
-ken
I don't think it's directly possible given the way enums are mapped internally in GORM. But changing the code to this works:
enum FooStatus {
BAR('br'),
TAR('tr')
private FooStatus(String id) { this.id = id }
final String id
static FooStatus byId(String id) {
values().find { it.id == id }
}
}
and
class Foo {
String status
FooStatus getFooStatus() { status ? FooStatus.byId(status) : null }
void setFooStatus(FooStatus fooStatus) { status = fooStatus.id }
static transients = ['fooStatus']
static constraints = {
status inList: FooStatus.values()*.id
}
static mapping = {
status sqlType: 'char(2)'
}
}
Adding the transient getter and setter allows you to set or get either the String (id) or enum value.
Grails ships with an undocumented (as far as I can tell anyway) custom Hibernate mapping for enums. The class is org.codehaus.groovy.grails.orm.hibernate.cfg.IdentityEnumType. It won't let you set the column size but does make it easy to change what is stored in the DB for each enum value without having to add transient fields to your model.
import org.codehaus.groovy.grails.orm.hibernate.cfg.IdentityEnumType
class MyDomainClass {
Status status
static mapping = {
status(type: IdentityEnumType)
}
enum Status {
FOO("F"), BAR("B")
String id
Status(String id) { this.id = id }
}
}
You can run an 'alter table' in Bootstrap.groovy to shrink the column:
DataSource dataSource
...
Sql sql = new Sql(dataSource)
sql.execute("alter table my_domain_class change column status status varchar(1) not null")
Even easier (works at least in Grails 2.1.0+)
class DomainClass {
Status status
static mapping = {
status(enumType: "string")
}
}
enum Status {
OPEN ("OPEN"),
CLOSED ("CLOSED"),
...
String name
Status (String name) {
this.name = name
}
}
Since GORM 6.1 identity enum mapping can be enabled with such construct
static mapping = {
myEnum enumType:"identity"
}

Resources