I have built a UI that allows you to enter a Master Record ( VocabularyTest ) and a set of Detail Records ( VocabQuestion ) on the same form. I'm pretty happy that the data that is being submitted is correct, but when I try to save the Master record, I get the following error
null id in vocabularytest.VocabQuestion entry
EDIT - The Complete stacktrace is
null id in vocabularytest.VocabQuestion entry (don't flush the Session after an exception occurs). Stacktrace follows:
Message: null id in vocabularytest.VocabQuestion entry (don't flush the Session after an exception occurs)
Line | Method
->> 24 | save in vocabularytest.VocabularyController
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 195 | doFilter in grails.plugin.cache.web.filter.PageFragmentCachingFilter
| 63 | doFilter in grails.plugin.cache.web.filter.AbstractFilter
| 1110 | runWorker in java.util.concurrent.ThreadPoolExecutor
| 603 | run . . . in java.util.concurrent.ThreadPoolExecutor$Worker
^ 722 | run in java.lang.Thread
END EDIT
I'm assuming that this is happening as the Foreign Key reference on my Detail record is null, which wouldn't surprise me unduly, as I'm doing nothing to set it.
The Params that are being received by my controller are as follows
title: Dave New Test
testItems[0].question: Q1
testItems[0].answer: A1
testItems[1].question: Q2
testItems[1].answer: A2
testItems[2].question: Q3
testItems[2].answer: A3
create: Create
My domain Objects are
class VocabularyTest {
static hasMany = [testItems: VocabQuestion]
static constraints = {
}
String title;
List <VocabQuestion>testItems = LazyList.decorate(
new ArrayList(),
FactoryUtils.instantiateFactory(VocabQuestion.class));
}
class VocabQuestion {
static constraints = {
}
String question
String answer
VocabularyTest vocabularyTest
}
The relevant method of my controller is
def save() {
log.debug(params)
def vocabularyTestInstance = new VocabularyTest(params)
if (!vocabularyTestInstance.save(flush: true)) {
render(view: "create", model: [vocabularyTestInstance: vocabularyTestInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'vocabularyTest.label', default: 'VocabularyTest'), vocabularyTestInstance.id])
redirect(action: "index", id: vocabularyTestInstance.id)
}
Am I trying to do something that GORM and Grails will allways struggle to do, or am I just missing something vital?
Try this:
class VocabularyTest {
static hasMany = [testItems: VocabQuestion]
static constraints = {
}
String title
List testItems
}
class VocabQuestion {
static constraints = {
}
static belongsTo = [vocabularyTest: VocabularyTest]
String question
String answer
}
Related
I'm new to Grails and I need to make some calculations when a "has-many" attribute changes, and I think the best place to do this is in the setter of the attribute, given that my attribute is a list the best place should be in the addTo and the removeFrom methods, I tried to override them but didn't worked.
So is this the best way of doing it? what's wrong with my code?
Here's the code:
Cicle.groovy
class Cicle {
String machine
int cicleValue
static hasMany = [measurements:Measurement]
static constraints = {
machine blank:false
cicleValue nullable:false
}
public void addToMeasurements(Measurement measurement){
super.addToMeasurements(measurement)
updateCalculations()
}
public void updateCalculations(){
int sumCicles = 0
measurements.each{ measurement ->
sumCicles += measurement.cicleValue
}
cicleValue = sumCicles / measurements.size()
this.save(failOnError: true)
}
}
This is the exception I get:
No signature of method: com.rpc.mock.app.Cicle.addToMeasurements() is applicable for argument types: (com.rpc.mock.app.Measurement) values: [com.rpc.mock.app.Measurement : (unsaved)]
Possible solutions: addToMeasurements(com.rpc.mock.app.Measurement), addToMeasurements(java.lang.Object), getMeasurements(). Stacktrace follows:
Message: No signature of method: com.rpc.mock.app.Cicle.addToMeasurements() is applicable for argument types: (com.rpc.mock.app.Measurement) values: [com.rpc.mock.app.Measurement : (unsaved)]
Possible solutions: addToMeasurements(com.rpc.mock.app.Measurement), addToMeasurements(java.lang.Object), getMeasurements()
Line | Method
->> 16 | addToMeasurements in com.rpc.mock.app.Cicle
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 43 | $tt__save in com.rpc.mock.app.MeasurementController
| 200 | doFilter . . . . in grails.plugin.cache.web.filter.PageFragmentCachingFilter
| 63 | doFilter in grails.plugin.cache.web.filter.AbstractFilter
| 1145 | runWorker . . . . in java.util.concurrent.ThreadPoolExecutor
| 615 | run in java.util.concurrent.ThreadPoolExecutor$Worker
^ 744 | run . . . . . . . in java.lang.Thread
Thanks
As you're dealing with domain objects, GORM supports the registration of events as methods that get fired when certain events occurs such as deletes, insertsand updates:
beforeInsert - Executed before an object is initially persisted to the database
beforeUpdate - Executed before an object is updated
beforeDelete - Executed before an object is deleted
beforeValidate - Executed before an object is validated
afterInsert - Executed after an object is persisted to the database
afterUpdate - Executed after an object has been updated
afterDelete - Executed after an object has been deleted
onLoad - Executed when an object is loaded from the database
Then, you can add updateCalculations() in your domain object like that:
static constraints = {
machine blank:false
cicleValue nullable:false
}
def beforeUpdate() { updateCalculations() }
As a general good design practice, it's better to keep logic implementation out from domains object and Grails allows to inject services into domains (POGO).
How to import service in Domain?
It has a field where I need to fill a field with a protocol. The protocol is automatically generated and created a service excluvivamente for this generation.
In the field in method 'AfterInsert' was inserted a call to this service that automatically populates the field.
I put on bootstrap the creation of some objects that need to be filled in your field with this protocol. But an error occurs which is apparently due to the use of 'Services' in 'Domain'. Could anyone help me?
class Post {
static transient postService
String conteudo
Date dataCriacao = new Date()
String protocolo
static constraints = {
dataCriacao(nullable:false, blank:false)
conteudo nullable:false, blank: false
protocolo nullable: true, blank: true
}
static mapping = {
conteudo type: 'text'
sort dataCriacao:"desc"
}
def afterInsert(){
if(!this.protocolo){
registraProtocolo()
}
}
protected void registraProtocolo() {
postService.teste(this)
}
}
Error: ERROR hibernate.AssertionFailure - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
Message: null id in com.app.post.Post entry (don't flush the Session after an exception occurs)
Line | Method
->> 105 | doCall in org.grails.datastore.gorm.GormStaticApi$_methodMissing_closure2
Message: null id in com.app.post.Post entry (don't flush the Session after an exception occurs)
Line | Method
->> 105 | doCall in org.grails.datastore.gorm.GormStaticApi$_methodMissing_closure2
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 27 | recInsertProtocolo in com.app.post.PostService
| 83 | teste . . . . . . in ''
| 117 | registraProtocolo in com.app.post.Post
The postService shouldn't be static, it should be simply
transient postService
Problem Solved! It is a matter of logic. The service comes automatically setados as 'transational = true', using AfterInsert the error occurs due to this feature of the service. but if you use a closure 'withNewSession', this problem is solved and is allowed to change the attributes of objects with services once the new session will meet the requirement 'transational'. Just got my domain like this:
AfterInsert def () {
if (! this.protocolo) {
Post.withNewSession
{
registraProtocolo ()
}
}
}
protected void registraProtocolo () {
postService.teste (this)
}
Thank you all for the help
For those who want more information down a JIRA who helped me in this solution (read the comments)
class Post {
def postService
...
}
ref: http://grails.org/doc/2.1.0/guide/single.html#dependencyInjectionServices
I have special case of domain classes structure where three classes presents and they are connected to chain: Event <- Room <- Projector. (All relation ships are one-to-one)
The implementation looks like:
class Event {
Room room
static constraints = {
room(nullable:false)
}
}
class Room {
Projector projector = new Projector()
static belongsTo = [event: Event]
static constraints = {
projector(nullable:false)
}
}
class Projector {
String something = "Something"
static belongsTo = [room: Room]
static constraints = {
room(nullable:false)
}
}
When I want to create Event with new Room I expect that the Projector will be created by default:
class TestController {
def index() {
Room room = new Room()
Event event = new Event(room: room)
event.save(flush: true, failOnError: true)
render event
}
}
I receive following exception
| Error 2012-07-11 16:09:12,541 [http-bio-8080-exec-3] ERROR errors.GrailsExceptionResolver - TransientObjectException occurred when processing request: [GET] /Test/room/index
object references an unsaved transient instance - save the transient instance before flushing: Projector. Stacktrace follows:
Message: object references an unsaved transient instance - save the transient instance before flushing: Projector
Line | Method
->> 46 | onApplicationEvent in org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 18 | index in RoomController
| 1110 | runWorker . . . . in java.util.concurrent.ThreadPoolExecutor
| 603 | run in java.util.concurrent.ThreadPoolExecutor$Worker
^ 722 | run . . . . . . . in java.lang.Thread
It seems that cascading over three classes doesn't save default values. Is there any solution for that? Or what am I doing wrong?
There is an open jira for your issue at http://jira.grails.org/browse/GRAILS-4613?attachmentOrder=desc
Personally I would handle the creation of objects in my controller instead of doing it in the domain. You need to be able to call projector.save(flush:true).
I have a undirectional one to many relationship of two domain classes:
package net.peddn
class User {
static hasMany = [texts: Text]
String username
}
and
package net.peddn
class Text {
long creator
String title
static constraints = {
creator( nullable: true )
}
}
my User controller looks like this:
package net.peddn
class UserController {
static scaffold = true
def delete() {
def userInstance = User.get(params.id)
userInstance.texts.each { text ->
text.creator = null
}
userInstance.delete(flush: true)
}
my BootStrap.groovy looks like this:
import net.peddn.Text
import net.peddn.User
class BootStrap {
def init = { servletContext ->
User user = new User(username: "username")
user.save(flush: true)
Text text1 = new Text(title: "titel 1", creator: user.id)
Text text2 = new Text(title: "titel 2", creator: user.id)
user.addToTexts(text1)
user.addToTexts(text2)
user.save(flush: true)
}
def destroy = {
}
}
When I now try to delete my User I get the following error:
| Server running. Browse to http://localhost:8080/usertexts
| Error 2012-06-17 19:33:49,581 [http-bio-8080-exec-4] ERROR errors.GrailsExceptionResolver - IllegalArgumentException occurred when processing request: [POST] /usertexts/user/index - parameters:
id: 1
_action_delete: Löschen
Stacktrace follows:
Message: null
Line | Method
->> 21 | doCall in net.peddn.UserController$_delete_closure1
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 19 | delete in net.peddn.UserController
| 1110 | runWorker in java.util.concurrent.ThreadPoolExecutor
| 603 | run in java.util.concurrent.ThreadPoolExecutor$Worker
^ 722 | run . . . in java.lang.Thread
If i change the code to
text.creator = 0
in UserController.groovy is works perfectly.
By the way: I used this domain model because I don't want the Text objects to be deleted when a User will be deleted. But I also want to know who created the Text object. If someone has a better solution for this problem PLEASE let me know.
Thanks!
Peter
With your current implementation please try if this works:
def delete() {
def userInstance = User.get(params.id)
userInstance.texts.each { text ->
userInstance.removeFromTexts(text)
}
userInstance.delete(flush: true)
}
If you want bidirectional relation User-Texts and you don't want texts to be deleted when user is deleted you can change this behaviour. Check documentation section on cascades in Hibernate here: custom cascades. You can change mapping like this:
class User {
static hasMany = [texts: Text]
static mapping = {
texts cascade: "save-update"
}
}
Finally I found the right hint. It seems that this is a casting problem. see:
Groovy Primitive Type Cast Gotcha When Upgrading to Grails 2.0
So i changed the primitive "long" property of the Text domain class to its "boxed" version "Long":
package net.peddn
class Text {
Long creator
String title
static constraints = {
creator( nullable: true )
}
}
and now the property can be set to null!
Thank you Tomasz for your help!
This question is in follow up to this post Grails one to many relationship view
The example suggested there is not working and throwing following exception at run time
null id in blog.omarello.Phone entry (don't flush the Session after an exception occurs). Stacktrace follows:
Message: null id in blog.omarello.Phone entry (don't flush the Session after an exception occurs)
Line | Method
->> 43 | doCall in blog.omarello.ContactController$_closure4$$ENLORkU6
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 886 | runTask in java.util.concurrent.ThreadPoolExecutor$Worker
| 908 | run . . in ''
^ 662 | run in java.lang.Thread
I think rather then making the example work can any one help me understand how can I create a GSP which can let me save multiple instances of same domain class. For example, a GSP which can let me insert multiple Book instances at once?
Once again, examine the project I linked on github. It is a demonstration of some of the one of the better practices for doing this. Particularly, look at the question/index, as this is what the view can look like. The actual saving piece is done in the QuestionService, used by the QuestionController. This project does exactly what you're trying to do. Review it.
Change the Contact class in the phone example as follows and it should work fine.
package blog.omarello
import org.apache.commons.collections.list.LazyList;
import org.apache.commons.collections.FactoryUtils;
class Contact {
static constraints = {
firstName(blank:false)
lastName(blank:false)
}
String firstName
String lastName
String nickName
List phones = LazyList.decorate(new ArrayList(),
FactoryUtils.instantiateFactory(Phone.class));
// List phones = new ArrayList()
static hasMany = [ phones:Phone ]
static mapping = {
phones cascade:"all-delete-orphan"
}
// def getPhonesList() {
// return LazyList.decorate(
// phones,
// FactoryUtils.instantiateFactory(Phone.class))
// }
def String toString() {
return "${lastName}, ${firstName}"
}
}