my own id in GORM - grails

I tried to change the standard 'id' in grails:
calls Book {
String id
String title
static mapping {
id generator:'assigned'
}
}
unfortunately, I soon noticed that this breaks my bootstrap. Instead of
new Book (id:'some ISBN', title:'great book').save(flush:true, failOnError:true)
I had to use
def b = new Book(title:'great book')
b.id = 'some ISBN'
b.save(flush:true, failOnError:true)
otherwise I get an 'ids for this class must be manually assigned before calling save()' error.
but that's ok so far.
I then encountered the same problem in the save action of my bookController. But this time, the workaround didn't do the trick.
Any suggestions?
I known, I can rename the id, but then I will have to change all scaffolded views...

That's a feature of databinding. You don't want submitted data to be able to change managed fields like id and version, so the Map constructor that you're using binds all available properties except those two (it also ignores any value for class, metaClass, and a few others).
So there's a bit of a mismatch here since the value isn't managed by Hibernate/GORM but by you. As you saw the workaround is that you need to create the object in two steps instead of just one.

I can't replicate this problem (used Grails 2.0.RC1). I think it might be as simple as a missing equal sign on your static mapping = { (you just have static mapping {)
Here's the code for a domain object:
class Book {
String id
String name
static mapping = {
id generator:'assigned'
}
}
And inside BootStrap.groovy:
def init = { servletContext ->
new Book(name:"test",id:"123abc").save(failOnError:true)
}
And it works fine for me. I see the id as 123abc.

You need to set the bindable constraint to true for your id prop, e.g.
class Employee {
Long id
String name
static constraints = {
id bindable: true
}
}

Related

Grails : id generator using assigned and falling back to sequence if unassigned

I am working with Grails 3.2.8. I would like to allow both options when generating an id. Is there a way to use assigned and falling back to sequence if unassigned? I tried getting the next id within a constructor and setting id there but running into issues. Any help/guidance would be most appreciated.
class Foo {
static mapping = {
id generator:'assigned'
}
}
vs
class Foo {
static mapping = {
id generator:'sequence'
}
}
Ive tried using mapping set to assigned and setting the id within the domain constructor of beforeValidate function. Neither are working for me. Examples below.
class Foo{
Foo(){
def id = Foo.find("from Foo order by id desc")
id = id ? id : 0
this.id = id
}
static mapping = {
id generator:'assigned'
}
}
class Foo{
def beforeValidate() {
def id = Foo.find("from Foo order by id desc")
id = id ? id : 0
this.id = id
}
static mapping = {
id generator:'assigned'
}
}
Thanks in advance for your help.
Is there a way to use assigned and falling back to sequence if
unassigned?
No, not directly anyway. You could use assigned and when you wanted to fall back on the sequence you could send a query to the database to retrieve the next sequence value and then assign it yourself. I think that is probably as close as you can probably get.
First of all, you need to understand HQL; in your examples you are returning Foo not id, so it's clearly wrong.
Second, you shouldn't depend on the table for next value of id, because it's not guaranteed to be correct -- you might face id is not unique kinda errors.
Third, you aren't incrementing the id, this way you will end up getting the same, most likely 0, every time.
Here is what you can do, use database sequence -- depends on the database.
"select id_seq.nextval from dual" // for Oracle
Finally, it's a very bad idea -- unless you are having a very good reason for it.

Object Id set as null when saving - AjaxDependencySelection

So I decided to use AjaxDependencySelection Plugin for Grails, and it has proven to be very useful. However, I am trying to implement autoComplete boxes, and it does not seem to be saving the object id when using an Autocompleted selection. Here is my implementation in my gsp
<g:selectPrimary id="template" name="template"
domain='dms.nexusglobal.Template'
searchField='templateName'
collectField='id'
domain2='dms.nexusglobal.Tag'
bindid="template.id"
searchField2='tagName'
collectField2='id'
hidden="hiddenNew"
noSelection="['': 'Please choose Template']"
setId="tag"
value="${documentPartInstance?.template}"/>
<g:selectSecondary id="tag" name="tag"
domain2='dms.nexusglobal.Subtag'
bindid="tag.id"
searchField2='subtagName'
collectField2='id'
autocomp="1"
noSelection="['': 'Please choose Tag']"
setId="subtag"
value="${documentPartInstance?.tag}"/>
<g:autoCompleteSecondary id="subtag" name="subtagId"
domain='dms.nexusglobal.Subtag'
primarybind='tag.id'
hidden='tag'
hidden2='hidden5'
searchField='subtagName'
collectField='id'
value='${documentPartInstance?.subtag}'/>
<input type=hidden id="hidden5" name="subtagId" value="${documentPartInstance?.subtag}"/>
However, everytime I save it, I am presented with this error Column 'subtag_id' cannot be null . Here is my domain class definition for Subtag
class Subtag {
static scaffold = true
String subtagName
static belongsTo = [tag : Tag]
public Subtag()
{
}
public Subtag(String s)
{
subtagName = s
}
static constraints = {
}
String toString(){
subtagName
}
}
Tag hasMany subtags as well
It seems to be creating new Subtag instances when using the autoselect box (as an error shows up saying Could not find matching constructor for:packagename.Subtag(java.lang.String) Although this is a feature I am looking to implement in my application at later stages (being able to create new Subtags on the fly when creating a document Part), right now, all I would like to be able to do is just choose from my already existing subtags.
When I add in a string constructor, it comes back with the error that Column subtag_id cannot be null
I have developed it so will try help you through your issue.
The problem is that you are trying to push a value from selectSecondary and update the elementId of g:autocomplete which is actually a seperate entity.
I will update the plugin with a new method, need to test it out first.. Also take a look at g:selectAutoComplete. Although this method would only work if your secondary was the primary task... so no good in that case either..
hang on and look out for 0.37 release
Released 0.37 documentation on how to do such a thing here: https://github.com/vahidhedayati/ajaxdependancyselection/wiki/from-selection-to-autocomplete---how-to

Basic problems to use Vaadin 7 + Grails 2.3 (Persistence, domain class design, get Hibernate Session)

I'm using Vaadin 7 + Grails 2.3, there is some questions
My domain classes
class Base {
private static final Date NULL_DATE = new Date(0)
Date createdAt = NULL_DATE;
Date updatedAt = NULL_DATE;
def beforeInsert(){
createdAt = new Date();
updatedAt = new Date();
}
def beforeUpdate(){
updatedAt = new Date();
}
static mapping = {
autoTimestamp true
}
}
abstract class Person extends Base{
String name;
String name2;
String phone1;
String phone2;
static constraints = {
name2 nullable:true
phone1 nullable:true
phone2 nullable:true
}
}
class Customer extends Person {
double credit;
}
THE PROBLEMS
PROBLEM 1
In my Vaadin class UI, if I try this
class MyUI extends UI {
#Override
protected void init(VaadinRequest vaadinRequest) {
Customer customer = new Customer()
customer.name="RODRIGO"
customer.save()
}
}
Show this error
Servlet.service() for servlet [VaadinServlet 0] in context with path [/AgileWeb] threw exception [com.vaadin.server.ServiceException: groovy.lang.MissingPropertyException: No such property: name for class: agileweb.Customer
Possible solutions: all] with root cause
Message: No such property: name for class: agileweb.Customer
Possible solutions: all
there is no "name" property? The class Customer extends Person that has this property.
PROBLEM 2
If I try this
Customer customer = new Customer()
Customer.setName("RODRIGO")
Customer.save()
Show thos error : Message: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
I have seach about this error but I didn't understand to fix it, maybe I'm new with Grails
PROBLEM 3 - ACTUALLY IS A QUESTION
I know that I can use BeanItemContainer, just from List object, that is possible with no problem, but BeanItemContainer is not lazy load, I'd like to use HbnContainer addon (https://vaadin.com/directory#addon/hbncontainer) becase it just need a hibernate session, so How Can I get the "Hibernante session", is there any example and how to do it?
PROBLEM 4 - ACTUALLY IS A QUESTION (AGAIN)
Following this tutorial https://vaadin.com/wiki/-/wiki/Main/Vaadin%20on%20Grails%20-%20Database%20access
It works to save object in the database, but the questions
- Is it really necessary to create a service for each domain class? I have read that it's recomend to put domain logical in the services, I agree with this, but what about simple domain that no need any logical?
so, is there possible to create something like DAO for services? is there any service design to avoid repeted code just to save objects?
I know that are many questions but I think these questions are the same of others, I really want to use Vaadin + Grails to enjoy the better of both, but is not easy to me at the moment!
Before I start answering your question, let me question you domain model. Base class is generally fine, but I want to talk about Person and Customer. You may have good reasons why you picked up inheritance, but please think of composition. Maybe you could have Person that contains a reference to an enum, that states type of the person. You could start here with that: http://en.wikipedia.org/wiki/Composition_over_inheritance
I think you have a typo there. Call save() method on 'customer' not 'Customer', which is a class
When there is a request coming to Grails application, it opens a session and the session is available during that request. There is not this kind of behavior like that in Vaadin. You need to put it into a Service. Yes, you can make generic service to save an object
class GenericService { def save(def domain) { domain.save(failOnError:true) }}
You can get the session factory like this
import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
def ctx = AH.application.mainContext
def sessionFactory = ctx.sessionFactory
or
ApplicationContext applicationContext = grailsApplication.mainContext
ConfigurableLocalSessionFactoryBean factory = applicationContext.getBean('&sessionFactory')
As I wrote, you could create GenericService or a service per domain object. Just to keep in mind that GenericService should only save an object and contain no other logic that would be specific for a domain object.

Grails GORM MissingMethodException with 1:N relationship

I have such domain classes:
class ServicesGroup {
Long id
String name
String description
String toString(){
return name
}
static mapping = {
version false
table 'root.services_groups'
id column:'group_id'
name column:'group_name'
description column:'group_desc'
}
}
and
class Step {
Long id
ServicesGroup service
String stepType
Integer stepFrom
Integer stepTo
static constraints = {
stepType(inList:['operator', 'client'])
}
static mapping = {
version false
table 'bill.steps'
service column:'service_group_id'
}
}
The relationship is - one ServicesGroup entry can have multiple Step instances.
However, when in my controller I try to
Step.findByService(3)
I get:
"org.codehaus.groovy.runtime.InvokerInvocationException: groovy.lang.MissingMethodException: No signature of method: Step.findByService() is applicable for argument types: (java.lang.Integer) values: {3}"
However, when I change Step domain class field
ServicesGroup service
to simply
Long service
it works.
What's going on here?
Try it that way:
Step.findByService(ServicesGroup.get(3))
Try
grails clean
grails run-app
Then try again.
Something like Step.findByService([id: 3]) may work. It only cares about the ID anyway for the purposes of the SQL generation. In a lot of cases like this you can toss a fake map into there rather than the real thing, and save yourself some performance.
On the other hand, the abstraction breaks down a bit when you do this.

Setting Grails domain id in Bootstrap.groovy

Is it possible to explicitly set the id of a domain object in Grails' Bootstrap.groovy (or anywhere, for that matter)?
I've tried the following:
new Foo(id: 1234, name: "My Foo").save()
and:
def foo = new Foo()
foo.id = 1234
foo.name = "My Foo"
foo.save()
But in both cases, when I print out the results of Foo.list() at runtime, I see that my object has been given an id of 1, or whatever the next id in the sequence is.
Edit:
This is in Grails 1.0.3, and when I'm running my application in 'dev' with the built-in HSQL database.
Edit:
chanwit has provided one good solution below. However, I was actually looking for a way to set the id without changing my domain's id generation method. This is primarily for testing: I'd like to be able to set certain things to known id values either in my test bootstrap or setUp(), but still be able to use auto_increment or a sequence in production.
Yes, with manually GORM mapping:
class Foo {
String name
static mapping = {
id generator:'assigned'
}
}
and your second snippet (not the first one) will do the job (Id won't be assigned when passing it through constructor).
What I ended up using as a workaround was to not try and retrieve objects by their id. So for the example given in the question, I changed my domain object:
class Foo {
short code /* new field */
String name
static constraints = {
code(unique: true)
name()
}
}
I then used an enum to hold all of the possible values for code (which are static), and would retrieve Foo objects by doing a Foo.findByCode() with the appropriate enum value (instead of using Foo.get() with the id like I wanted to do previously).
It's not the most elegant solution, but it worked for me.
As an alternative, assuming that you're importing data or migrating data from an existing app, for test purposes you could use local maps within the Bootstrap file. Think of it like an import.sql with benefits ;-)
Using this approach:
you wouldn't need to change your domain constraints just for
testing,
you'll have a tested migration path from existing data, and
you'll have a good data slice (or full slice) for future integration tests
Cheers!
def init = { servletContext ->
addFoos()
addBars()
}
def foosByImportId = [:]
private addFoos(){
def pattern = ~/.*\{FooID=(.*), FooCode=(.*), FooName=(.*)}/
new File("import/Foos.txt").eachLine {
def matcher = pattern.matcher(it)
if (!matcher.matches()){
return;
}
String fooId = StringUtils.trimToNull(matcher.group(1))
String fooCode = StringUtils.trimToNull(matcher.group(2))
String fooName = StringUtils.trimToNull(matcher.group(3))
def foo = Foo.findByFooName(fooName) ?: new Foo(fooCode:fooCode,fooName:fooName).save(faileOnError:true)
foosByImportId.putAt(Long.valueOf(fooId), foo) // ids could differ
}
}
private addBars(){
...
String fooId = StringUtils.trimToNull(matcher.group(5))
def foo = foosByImportId[Long.valueOf(fooId)]
...
}

Resources