Deleting the object and its mapping in one-to-many in grails - grails

I have 2 domain classes as follows -
class A {
static hasMany = [B] // Just trying to show my mapping
}
class B {
// This table doesn't belongs to A
}
Now I have a object of class A which contains a set of objects B. I want to delete object A and its association with B, but I don't want to delete object B.
I have tried cascade for delete, delete-all & delete-all-orphan but it seems to be trying to delete the associated records from table B which isn't what I want.
This is what I am doing right now -
objectTypeB.each { b ->
a.removeFromB(b)
}
and then
a.delete()
but getting error
deleted object would be re-saved by cascade (remove deleted object from associations)

Your example is almost correct, I think what you need is tell A what collection to store B's in (see the hasMany)
class A {
String name
static hasMany = [b: B]
}
class B {
String name
}
Then it should be possible to do this (I tried it in a blank project, where my domains also have a String name property):
def b1 = new B(name: 'b1').save(flush: true)
def b2 = new B(name: 'b2').save(flush: true)
def b3 = new B(name: 'b3').save(flush: true)
def a = new A(name: 'a')
a.addToB(b1)
a.addToB(b2)
a.save(flush: true)
def x = A.get(1)
println x.b
x.delete(flush: true)
assert A.count() == 0
Since there is no relation from B to A, there is no need to remove the b's from A before deleting the A. If you want, I can send you my code sniplet

Related

Grails hasMany delete behavior

I have 2 domain classes
class A {
static hasMany = [ b : B ]
}
class B {
static belongsTo = A
}
I would like to keep the cascading saves, so when I save A, it updates the B's, but when I do a delete of A, I would like it to fail if any B's exist that are associated with that A. So you would have to explicitly delete all of the B's first.
I'm not sure the easiest way to do this in Grails. I could put a check in A before I delete it to verify that there are no B's - simple enough. But is there a way to control this through cascading or relationship behaviors so I don't have to put the logic in there?
Specify the cascade behavior for the collection
class A {
static hasMany = [ b : B ]
static mapping = {
b cascade: 'save-update'
}
}
It will cascade save and update but not delete.

Grails fails to delete orphan on collection in Unit tests, if composite id is used

I am using Grails 2.4.4. I would like to test
the persistence in Unit Test Class with an in-memory database.
I have a Parent class with a oneToMany relation with Child. The Child is ownedBy the Parent and has a composite key involving the parent. When I try to delete one of the Children in the collection inside Parent, I get an error if I flush, no delete is fired if I omit the 'flush: true' parameter.
class Parent implements Serializable {
String name
static hasMany = [children : Child]
static mapping = {
children cascade: 'all-delete-orphan'
}
}
class OtherParent implements Serializable {
String name
}
class Child implements Serializable {
String name
static belongsTo = [ owner : Parent, secondOwner : OtherParent]
static mapping = {
id composite : ['owner', 'secondOwner']
}
}
I would like to test relations in unit test classes annotated like this
#Domain([Parent, OtherParent, Child])
#TestMixin(HibernateTestMixin)
class ChildSpec extends Specification {
def "Parents and Children can be created, saved and deleted"() {
given: "we have a clean database at the start"
Parent.count() == 0
OtherParent.count() == 0
Child.count() == 0
and:
Parent a = new Parent()
a.name = "Parent"
OtherParent secondParent = new OtherParent ()
secondParent.name = 'Second Parent'
Child b = new Child()
b.name = "Child"
b.otherOwner = secondParent
a.addToChildren(b)
when: "we save Parent"
secondParent.save(flush: true, failOnError: true)
a.save(flush: true, failOnError: true)
then: "Parent saves and Child is saved too"
Parent.count() == 1
Child.count() == 1
def savedA = Parent.findByName("Parent")
savedA.name == "Parent"
savedA.children.size() == 1
def savedB = savedA.children.getAt(0)
savedB.name == "Child"
def foundB = Child.findByName("Child")
foundB.name == "Child"
when: "we remove Child from Parent, we can still save Parent"
savedA.removeFromChildren(savedB)
savedB.delete(flush: true)
savedA.save(failOnError: true, flush: true)
then: "we've got an Parent with no Bs and no exception is thrown"
notThrown(Exception)
Child.count() == 0
}
}
But an Exception is thrown
Expected no exception of type 'java.lang.Exception' to be thrown, but got it nevertheless
at spock.lang.Specification.notThrown(Specification.java:106)
at eu.europa.ec.comp.redda.test.ParentSpec.Parents and Chilren can be created, saved and deleted(ParentSpec.groovy:56)
Caused by: org.springframework.orm.hibernate4.HibernateOptimisticLockingFailureException: Object of class [eu.europa.ec.comp.redda.test.Child] with identifier [eu.europa.ec.comp.redda.test.Child : (unsaved)]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [eu.europa.ec.comp.redda.test.Child#eu.europa.ec.comp.redda.test.Child : (unsaved)]
at org.springframework.orm.hibernate4.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:200)
at org.codehaus.groovy.grails.orm.hibernate.GrailsHibernateTemplate.convertHibernateAccessException(GrailsHibernateTemplate.java:593)
at org.codehaus.groovy.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:183)
at org.codehaus.groovy.grails.orm.hibernate.GrailsHibernateTemplate.execute(GrailsHibernateTemplate.java:123)
at org.codehaus.groovy.grails.orm.hibernate.InstanceApiHelper.delete(InstanceApiHelper.java:36)
at org.codehaus.groovy.grails.orm.hibernate.HibernateGormInstanceApi.delete(HibernateGormInstanceApi.groovy:228)
at eu.europa.ec.comp.redda.test.ParentSpec.Parents and Chilren can be created, saved and deleted(ParentSpec.groovy:52)
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [eu.europa.ec.comp.redda.test.Child#eu.europa.ec.comp.redda.test.Child : (unsaved)]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2541)
at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:3403)
at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:3630)
at org.hibernate.action.internal.EntityDeleteAction.execute(EntityDeleteAction.java:114)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:463)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:349)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1222)
at org.codehaus.groovy.grails.orm.hibernate.InstanceApiHelper$1.doInHibernate(InstanceApiHelper.java:40)
at org.codehaus.groovy.grails.orm.hibernate.InstanceApiHelper$1.doInHibernate(InstanceApiHelper.java:36)
at org.codehaus.groovy.grails.orm.hibernate.GrailsHibernateTemplate.doExecute(GrailsHibernateTemplate.java:179)
... 4 more
Your unit test needs some data to be set up for your test. If you want to test that you can create and save your objects, then try this (you're also missing a name property for the A domain class, and let's also assume there's one for the B class too):
def "As and Bs can be created, saved and deleted"() {
expect: "we have a clean database at the start"
A.count() == 0
B.count() == 0
given:
A a = new A()
a.name = "A"
B b = new B()
b.name = "B"
a.addToBs(b)
when: "we save A"
a.save(flush: true, failOnError: true)
then: "A saves and B is saved too"
A.count() == 1
B.count() == 1
def savedA = A.findByName("A")
savedA.name == "A"
savedA.bs.size() == 1
def savedB = savedA.bs.getAt(0)
savedB.name == "B"
def foundB = B.findByName("B")
foundB.name == "B"
when: "we remove B from A, we can still save A"
savedA.removeFromBs(savedB)
savedB.delete(flush: true)
savedA.save(flush: true)
then: "we've got an A with no Bs and no exception is thrown"
notThrown(Exception)
savedA.bs.count() == 0
}
EDIT to reflect change to the question:
Your domain model means that a Child must belong to both a Parent and an OtherParent; it is not an either/or relationship. Are you sure this is what you want?
If it is what you want, I still don't understand why you want a composite id? Also, you haven't included OtherParent in the #Domain annotation. The code here will work if you remove the composite id mapping.

How can I get dateCreated in Has-Many Relation in Grails?

I have the following domain classes:
class A {
hasMany = [bs : B]
}
class B { }
Note that B has no backward relation to a. GORM creates a join table in my MYSQL database a_b. This table has two columns the id of a and the id of b.
How can I get a dateCreatedin the join table?
Create a model of the join table yourself and add whatever properties you want on it. Simple, and done.
For example:
class A {
static hasMany = [bs: JoinB]
}
class JoinB {
static belongsTo = [a: A]
B b
Date dateCreated
static mapping = {
autoTimestamp true // default, but I like to be explicit about it.
}
}
class B {
String whatever
}
(Careful of typos etc. I just did that off the top of my head)

GORM where query on an embedded object

I have domain classes A and B as follows:
class A {
String prop1
String prop2
B prop3
static embedded = ['prop3']
}
class B {
String prop4
String prop5
}
When I want to query like this:
def q = A.where { prop3.prop4 == 'bla' }
def list = q.list()
I get the following exception:
Cannot get property 'javaClass' on null object. Stacktrace follows:
on the "def q = A.where ..." line.
Any clue what's the problem? I've checked this:
http://grails.1312388.n4.nabble.com/GORM-embedded-object-issue-td1379137.html
but how to "just call them directly" is not quite clear to me. Any other way of querying the embedded objects in GORM?
I finally gave up on the where query and went with the DetachedCriteria approach. Gives me the same flexibility as the where queries, but works with embedded domain objects:
def criteria = new DetachedCriteria(A).build {
eq 'prop1', 'bla2'
}
criteria = criteria.build {
eq 'prop3.prop4', 'bla'
}
def list = criteria.list()
What do you get if you do (assuming B is in src/groovy)
def q = A.where { prop3 == new B(prop4: 'bla') }
def list = q.list()
Embedded components are persisted inside the main domain class (owner) itself. It can be accessed directly using any dynamic finder as you do directly on a domain object.
The above can also be represented in dynamic finders as:
A.findAllByProp3(new B(prop4: 'bla'))

Criteria on a one-to-many relation

My domain contains a one-to-many relationship like this:
class A {
B b
}
class B {
String name
}
I want to create a criteria query on A which will look for A to have the B object with the given name. It may return multiple entries. So... compare a given string with the "name" field from B and return the list of entries of type A for which B matches the name.
Thx!
Unless I am missing something that should be pretty easy:
def instanceList = A.withCriteria {
b {
eq('name','whatever')
}
}

Resources