I get the following Exception when saving an instance of Trip in Grails:
2011-01-26 22:37:42,801 [http-8090-5]
ERROR errors.GrailsExceptionResolver
- object references an unsaved transient instance - save the
transient instance before flushing: Rower
org.hibernate.TransientObjectException:
object references an unsaved transient
instance - save the transient instance
before flushing: Rower
The concept is simple: For a boattrip you need some rowers, a coxwain (is also a rower) and a boat:
Trip looks like (shortened):
class Trip {
Boat boat;
Rower coxwain;
static belongsTo = [Rower,Boat]
static hasMany = [rowers:Rower]
}
and Rower (shortened)
class Rower {
String firstname;
String name;
Rower reference;
static hasMany = [trips:Trip];
static mappedBy = [trips:"rowers"]
}
The trip then is saved in the controller like:
def save = {
def trip = new Trip(params)
// adding Rowers to Trip
if (params.rower instanceof String) {
def r = Rower.get(params?.rower)
if (r != null) {
trip.addToRowers(r)
}
} else {
params?.rower?.each{
rowerid ->
def r = Rower.get(rowerid)
log.info("rowerid (asList): " + rowerid)
if (r != null) {
trip.addToRowers(r)
}
}
}
// saving the new Trip -> EXCEPTION IN NEXT LINE
if(!trip.hasErrors() && trip.save(flush:true)) {
// ...
}
// ...
}
I think I have set the relations between the domains correct.
The Rower is not changed while it is added to the Trip. Why does Grails want it to save? why is it a transient instance?
Unfortunately this is an issue with the way GORM handles things, or more specifically the way that it expects that you deal with transients. If you don't persist the contained classes to the database first (Rowers in this case), you will get this exception every single time.
With GORM you have to save and attach in a bottom up fashion or when grails goes to flush the connection for the next instance you will get the transient instance exception. The instance is 'transient' because its just an in-memory reference. To persist the parent, GORM needs to link the parent to the child in the database. Without the child being persisted it has no way to do that, this is where the exception is coming from.
Wish there was better news. Not that its hard, but it gets annoying with complex hierarchies.
The Problem was somehow different.
it's in here:
def trip = new Trip(params)
which references a coxwain (of class Rower), which is not set (id=-1 is returned). This constructs a new Rower instead of a 'null' value. And this is the 'unsaved transient instance'.
If I check first for a valid instance, then it works :-)
Thanks for the help!
Just a quick note for anyone dealing with singular or multiple parameters with the same name, using the params.list("parameterName") helper you can always return a list
...
// adding Rowers to Trip
if (params.rower instanceof String) {
def r = Rower.get(params?.rower)
if (r != null) {
trip.addToRowers(r)
}
} else {
params?.rower?.each{
rowerid ->
def r = Rower.get(rowerid)
log.info("rowerid (asList): " + rowerid)
if (r != null) {
trip.addToRowers(r)
}
}
}
...
could become a bit groovier
...
// adding Rowers to Trip
for(rower in params.list("rower") {
def r = Rower.get(rower)
if(r) trip.addToRowers(r)
}
...
you can find it hiding away under 6.1.12 Simple Type Converters
At first I thought it was to do with cascading saves and belongsTo, as described in The Grails Reference section 5.2.1.3 and Gorm Gotchas part 2. However since the Rowers are already in the DB I think it should work. The domain model is complicated to me, what you need to do is simplify it and run some tests using Grails console (run grails console in your project directory). First create a basic many-to-many between Trip and Rower and get it to execute the desired code. Then add the other parts bit-by-bit, like Rower's reference to itself. I'm not sure that the mappedBy part is necessary at all.
I think you have to save the trip before you add the rover to the trip.
Also it make no sense to check if trip has errors before you validate and/or save it.
Try this:
if(trip.validate() && trip.save(flush:true)) {
if (r != null) {
trip.addToRowers(r)
}
}
In many use cases, you should be able to address this by applying the cascade setting to your collection:
static mapping = {
rowers cascade: 'all-delete-orphan'
}
https://docs.grails.org/latest/ref/Database%20Mapping/cascade.html
Suppose, you are using Trip is hasMany relationship with Rower
class Trip {
static hasMany = [rowers:Rower]
...
}
class Rower{
static belongsTo =[trips:Trip]
...
}
Actually, it's looking for new session. So we need to stop/rollback the current transaction then this error won't raise, For that
Try This,
def row = new Row()
row.save()
NOTE: this save won't affect your database. It's just for rollback the transaction.
I had this problem but the answer for me was much simpler - my first object had not saved properly because it didn't validate successfully.
I somehow didn't notice that the first object had not saved, and so the error was confusing to me.
While debugging the problem, I eventually noticed that the first object was not saving properly, and when I fixed that, the object references an unsaved transient instance problem went away.
Related
Using Grails 2.5.6 here. I'm trying to access a Set of Strings off of my domain class in the beforeDelete GORM event. I'm seeing the deletes for this set getting issued in the database log before even getting to my breakpoint in the beforeDelete.
I'm getting a NullPointerException on my println(strings) below in my domain class.
My test domain class looks like
class DeleteTest {
Integer id
Set<String> stringSet
String prop1
String prop2
static hasMany = [stringSet: String]
static constraints = {
prop1(maxSize: 20)
prop2(maxSize: 20)
}
static mapping = {
stringSet(joinTable: [column: 'delete_test_string_set', length: 15])
}
def beforeDelete() {
withNewSession {
Set<String> strings = this."stringSet"
println(strings)
}
}
}
And I've made a test controller like this.
class DeleteTestController {
def create() {
DeleteTest test = null
DeleteTest.withTransaction {
test = new DeleteTest(
prop1: 'Test',
prop2: 'another test',
stringSet: ['str1', 'str2', 'str3']
).save()
}
render (test as JSON)
}
def delete() {
DeleteTest test = DeleteTest.findByProp1('Test')
DeleteTest.withTransaction {
test.delete()
}
render(test as JSON)
}
}
How can I get my stringSet in the beforeDelete event?
One easy way is to make sure to load stringSet before calling the delete. However, there are clearly some odd behaviors going on here and I'll describe what I have found so far.
Simple Answer
def delete() {
DeleteTest test = DeleteTest.findByProp1('Test')
test.stringSet?.size() // <-- force load here
DeleteTest.withTransaction {
test.delete()
}
render(test as JSON)
}
Other Considerations
I tried making stringSet eager loaded. This did not work as expected and in the beforeDelete code it would often be a single value or blank.
I also tried making StringSet a Set where I defined a single GORM object MyString containing the value. This did work (though I had to make it eagerly fetched), but I did not consider this to be a valid solution for your case since I assume you have data already and can't just replace it.
Based on some debug digging, I'm guessing (but it really is just a guess) that the collection is deleted before the beforeDelete event fires, and so it can't be lazily loaded at that point even in a new transaction. I would expect that someone else could weigh in on whether that's right or not, but grails 2 expertise is getting harder to find these days.
I'm pretty much certain I'm doing something wrong since this obviously works. Simplified classes:
class Person {
String name
static hasMany = [cats:Cat]
}
class Cat {
String name
Person person
static belongsTo = Person
static constraints = {
person(nullable:false)
}
String toString() {
"${person.name}-${name}"
}
}
Simple stuff, a person has many cats, cats must belong to only a single person.
Now when I do the following in a Service class, I get strange results:
delete(Cat cat) {
Person owner = cat.person
log.debug("Cats before removing ${cat} (id=${cat.id}): ${owner.cats} -- ${owner.cats*.id}")
owner.removeFromCats(cat);
log.debug("Removed from owner ${owner}, owner now has ${owner.cats} -- ${owner.cats*.id}")
log.debug("Cat to delete is now: ${cat} and belongs to... ${cat.person}")
cat.delete(flush:true)
}
And the error is "object would be resaved, blah blah"
org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations)
The weird bit is the debug results, when called to remove cat "Fluffy" who's owned by "Bob":
Cats before removing Bob-Fluffy (id=1356): [Bob-Fluffy] -- [1356]
Removed from owner Bob, owner now has [null-Fluffy] -- [1356]
Cat to delete is now: null-Fluffy and belongs to... null
What's going on that "removeFrom" isn't actually removing the object from the collection? I cleaned and recompiled. Pretty much at a loss as to why I can't delete this object.
I would try to remove the field person as Person person and leave only the belongsTo field like this
class Cat {
String name
static belongsTo = [person:Person]
static constraints = {
person(nullable:false)
}
String toString() {
"${person.name}-${name}"
}
}
It looks like what was happening in my case is that cat.person has getting stale somehow, even though it's the first thing in the method. Calling cat.refresh() didn't work, but calling owner.refresh() after extracting it from the cat.
I would change the domain class as such.
class Person {
String name
static hasMany = [cats:Cat]
}
class Cat {
String name
Person person
// no need to add belongs to property here. it creates a join table that you may not need
static constraints = {
person(nullable:false)
}
String toString() {
"${person.name}-${name}"
}
}
In the service class
delete(Cat cat) {
cat.delete(flush:true)
}
Once you make the domain changes, start with a fresh database since the schema will change.
I think that should solve your problem.
I'm taking the example from: grails - using multiple belongsTo, but only one at a time
class Order {
static hasMany = [transactions: MoneyTransaction]
}
class Member {
static hasMany = [payments: MoneyTransaction]
}
and
class MoneyTransaction {
static belongsTo = [order: Order, member: Member]
static constraints = {
order(nullable: true)
member(nullable: true)
}
}
I have a setup similar to those classes, Order, Member, and MoneyTransaction. I am making a call to Member.addToMoneyTransaction(key: value, .... ).
I've used the debugger and I've verified that the addTo operation is creating the MoneyTransaction and it's unsaved. After I call save with failOnError, I'm not getting an error but the payments collection class doesn't persist the save. If I attempt to use the debugger and call save on the individual child it's self, the save is performed without issue.
Why am I not able to get the save able to save the children of the object?
(I'm using Grails 2.4.3)
I have a need to have a method to return Id in case of success and list of errors in case of fail.
ex code snippet:
def save = {
def errors = []
if(Employee.save(flush:true)){
return Employee.id
}else{
errors.add("Can't be saved")
return errors.
}
}
In Service class
ICalling
Employee.save() - .. so how to check if it is error or id that save method returns
Any suggestions around would be appreciated.
I agree with Burk not to return different types, it can lead to unexpected errors.
Another solution to the problem is using Java's exception handling mechanism. You can add a context field to the Exception which will hold the list of validation errors.After catching the exception you can extract the errors.
void save(Employee employee) {
// do save
// ...
// on error:
def errors = [ "terrible error nr. 5" ]
throw new ValidationException(errors)
}
try {
fooService.save(employee)
} catch(ValidationException e) {
def errors = e.erorrs
// do stuff with the errors
}
An additional advantage: When no validation error is expected, the try-catch block can be ommited in Groovy, which makes the code cleaner because you don't have to care about any validation error fields.
Don't do this - even if you can make it somewhat more usable with Groovy, it's a bad idea. In this case though, there are a few simple solutions. If you're just passing the Employee instance and saving it in the service method, you don't need to return anything:
void save(Employee employee) {
employee.save(flush:true)
}
This is because if it's successful, the id will be set on the instance you passed in, and if not there will be one or more validation errors in the errors property (there's no need for you to return a generic error message when there are actually useful error messages available).
For example this would be the code you'd have in a controller calling the service:
def employee = new Employee(...)
fooService.save(employee)
if (employee.hasErrors()) {
// do something with employee.errors
}
else {
// success - use the id if you need via employee.id
}
If you want to pass in the data to create and save the new instance and return an Employee (this is the approach I usually take), it's similar:
Employee save(String name, int foo, boolean bar, ...) {
Employee employee = new Employee(name: name, foo: foo, bar: bar, ...)
employee.save(flush:true)
return employee
}
In this second case it's important to separate the save call and the return, since if there is a validation error save returns null and you want to always return a non-null instance. So do not do this:
return employee.save(flush:true)
If you separate them you can check the errors and/or the id.
Also, make sure that you do not use closures in services like you have in your code (def save = { ...). Only methods will be transactional since the Spring transaction handling doesn't know about Groovy closures - they're just fields that Groovy calls as if they were methods, but they're not.
I'm missing something....
I have a Grails webflow that looks like this:-
def childFlow = {
start {
action {
def targets = []
Target.list().each {target ->
targets.add(new TargetCommand(name: target.name, id: target.id))
}
log.debug "targets are $targets"
[children: targets]
}
on('success').to('selectChild')
}
...
TargetCommand is serializable. but I get this error:-
Caused by: java.io.NotSerializableException: com.nerderg.groupie.donate.Target
For some reason the "target" object that is inside the Target.list().each {} closure is getting put into the flow scope, and I can't figure out how to mark it as transient.
I have some code in a Service that has objects placed in the flow scope when I don't want them to too.
How do I stop local transient variables in closures being put in the flow scope?
Refining the above answer instead of clearing the persistenceContext we simply evict the instances as we finish with them, like so:
Target.list().each {
targets.add(new TargetCommand(name: it.name, id: it.id))
flow.persistenceContext.evict(it)
}
This is still a work-around for not being able to mark the closure variables as transient
The answer to my question is:
the flow object is a map that contains a reference to the "persistenceContext" which is a org.hibernate.impl.SessionImpl so the flow tries to store the entire session, even if the objects are not changed (for context I suppose)
this incorrect example from grails 1.1.x doc gives us a clue what to do:
processPurchaseOrder {
action {
def a = flow.address
def p = flow.person
def pd = flow.paymentDetails
def cartItems = flow.cartItems
flow.clear()
def o = new Order(person:p, shippingAddress:a, paymentDetails:pd)
o.invoiceNumber = new Random().nextInt(9999999) cartItems.each { o.addToItems(it) }
o.save()
[order:o] }
on("error").to "confirmPurchase"
on(Exception).to "confirmPurchase"
on("success").to "displayInvoice"
}
The flow.clear() clears the entire flow map including the persistenceContext or the session, which then makes the whole flow fail due to lack of a session.
so the intermediate "solution" is to use the persistenceContext and in this case clear it. So this works:-
def childFlow = {
start {
action {
sponsorService.updateTargetsFromTaggedContent()
def targets = []
Target.list().each {
targets.add(new TargetCommand(name: it.name, id: it.id))
}
flow.persistenceContext.clear()
[children: targets]
}
on('success').to('selectChild')
on(Exception).to 'finish'
}
The obvious problem with this is that the session is cleared completely, instead of just keeping out things I don't want in the flow.
for want of a better way, here is a generalised solution that removes any non Serializable objects from the persistenceContext of the flow. This could be a service method given the flow:-
def remove = []
flow.persistenceContext.getPersistenceContext().getEntitiesByKey().values().each { entity ->
if(!entity instanceof Serializable){
remove.add(entity)
}
}
remove.each {flow.persistenceContext.evict(it)}
If like me you need to evict all maybe you like to do
flow.persistenceContext.flush()
flow.persistenceContext.persistenceContext.clear()