Starting with a github repo that demonstrates how to use GORM outside of Grails, I am attempting to use dynamic finders so that I can look up a particular domain object by one of its properties. In this example, we have this person object in groovy as such:
package domain
import grails.gorm.annotation.Entity
import org.grails.datastore.gorm.GormEntity
#Entity
class Person implements GormEntity<Person> {
String firstName
String lastName
static mapping = {
firstName blank: false
lastName blank: false
}
}
Now lets say I want to look up a person by last name. I should be able to use the GORM-enhanced Person entity method findByLastName. I'm able to compile the code that attempts this, but when I call it at runtime, the method is not found.
I added a test method to PersonSpec.groovy that looks like this:
#Rollback
def "person can be found by last name"() {
when:
def p = new Person(firstName: 'Scott', lastName: 'Ericsson')
p.save(flush: true)
def foundPerson = p.findByLastName('Ericsson')
then:
foundPerson.firstName == 'Scott'
}
I get this error when the test is run:
domain.PersonSpec > person can be found by last name FAILED
groovy.lang.MissingMethodException at PersonSpec.groovy:32
The test method above it creates and saves a person record successfully, so some aspects of GORM functionality are working. But dynamic finder functionality is not being properly applied at run time, even though the compiler thinks everything looks good.
My entire build.gradle is this:
apply plugin: 'groovy'
repositories {
jcenter()
}
dependencies {
compile "org.hibernate:hibernate-validator:5.3.4.Final"
compile "org.grails:grails-datastore-gorm-hibernate5:7.0.0.RELEASE"
runtime "com.h2database:h2:1.4.192"
runtime "org.apache.tomcat:tomcat-jdbc:8.5.0"
runtime "org.apache.tomcat.embed:tomcat-embed-logging-log4j:8.5.0"
runtime "org.slf4j:slf4j-api:1.7.10"
testCompile 'org.spockframework:spock-core:1.1-groovy-2.4'
}
Does anyone know what I am missing?
So I've been struggling with this for a couple of days, and wouldn't you know as soon as I post the question I figure it out almost instantly. It's simple--I need to use findByLastName method statically on the Person object instead of a person instance. The code that works in PersonSpec looks like this now:
#Rollback
def "person can be found by last name"() {
when:
def p = new Person(firstName: 'Scott', lastName: 'Ericsson')
p.save(flush: true)
def foundPerson = Person.findByLastName('Ericsson')
then:
foundPerson.firstName == 'Scott'
}
Related
Given the following domain class:
class Dog {
Object name // changing the type to String fixes it
}
And this unit test:
import grails.test.mixin.Mock
import grails.test.mixin.TestMixin
import grails.test.mixin.support.GrailsUnitTestMixin
import spock.lang.*
/**
* See the API for {#link grails.test.mixin.support.GrailsUnitTestMixin} for usage instructions
*/
#TestMixin(GrailsUnitTestMixin)
#Mock([Dog])
class DogSpec extends Specification {
def setup() {
}
def cleanup() {
}
void "test something"() {
Dog dog = new Dog(name:"sparky")
// dog.name = "sparky" // adding this line also fixes it
expect:"fix me"
dog.name == "sparky"
}
}
Running grails test-app fails, but if you change the type of Dog.name to String, it works fine. Debugging brings me to realize that Dog.name never gets assigned and is null. If I were to set dog.name via regular assignment after constructing it as above, the test passes.
This issue does not occur in Groovy Script using the same map constructor assignment.
I want my type to be Object as it varies depending on the use case.
Any idea why this is happening? Is it a bug in Grails?
I did some debugging and I found out that when a field accepts an Object, grails framework doesn't do databinding on this property.
More specifically, the Dog#name field doesn't belongs to the whitelist of properties when It is declared as Object.
That's why this doesn't work.
If you want to debug, see the
grails.web.databinding.DataBindingUtils#getBindingIncludeList(final Object object)
If you put a breakpoint there, you will see how grails generates the whitelist of properties.
I think this makes a lot of sense since It's related to security!
See more about it here
If you wanna bind on an Object field nevertheless, this code may help you:
class Dog {
Object name
static constraints = {
name(bindable: true)
}
}
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.
Updated post:
In a Controller if I do this:
def obj = new Test(name:"lol")
obj.save(flush:true)
obj.name = "lol2"
//a singleton service with nothing to do with obj
testService.dostuff()
/*
"obj" gets persisted to the database right here
even before the next println
*/
println "done"
Can anyone please explain me why is this happening with Grails 1.3.7 and not with Grails 2? What is the reason?
I know I could use discard() and basically restructure the code but I am interested in what and why is happening behind the scenes. Thanks!
Old post:
I have a test Grails application. I have one domain class test.Test:
package test
class Test {
String name
static constraints = {}
}
Also I have a service test.TestService:
package test
class TestService {
static scope = "singleton"
static transactional = true
def dostuff() {
println "test service was called"
}
}
And one controller test.TestController:
package test
class TestController {
def testService
def index = {
def obj = new Test(name:"lol")
obj.save(flush:true)
obj.name = "lol2"
testService.dostuff()
println "done"
}
}
So what I do:
Create a domain object
Change one of it's properties
Call a singleton service method
What I would expect:
Nothing gets persisted to the db unless I call obj.save()
What happens instead:
Right after the service call Grails will do an update query to the database.
I have tried the following configuration from this url: http://grails.1312388.n4.nabble.com/Turn-off-autosave-in-gorm-td1378113.html
hibernate.flush.mode="manual"
But it didn't help.
I have tested it with Grails 1.3.7, Grails 2.0.3 does not have this issue.
Could anyone please give me a bit more information on what is exactly going on? It seems like the current session has to be terminated because of the service call and because the object is dirty it is getting automatically persisted to the database after the service call. What I don't understand that even with the manual flush mode configuration in Hibernate does not help.
Thanks in advance!
I'm not sure what about that thread you linked to made you think it would work. They all said it wouldn't work, the ticket created has been closed as won't fix. The solution here is to use discard() as the thread stated.
I've been getting a strange error that has had me hung up all morning. I have a Grails application with a Person class that looks like this:
class Person {
String id
Date lastUpdated
String note
String lastName
String firstName
String middleName
String facility
...
}
In my controller, I have a closure to obtain the model:
def personDetail = {
Person person = new Person()
...
List<Person> personSearchList = session.getAttribute("searchResults")
Person selectedSearchPerson = selectedSearchPersonList.find { it.id == selectedID }
person.firstName = selectedSearchPerson.firstName
person.lastName = selectedSearchPerson.lastName
person.middleName = selectedSearchPerson.middleName
person.facility = selectedSearchPerson.facility
...
return [person:person]
}
Now, this code was working fine yesterday. This morning however, without making any modifications (I have even tried reverting to older svn submissions) I am getting the following error when I click the g:link to display the detailController gsp:
groovy.lang.MissingPropertyException: No such property: facility for class: org.icf.Person
at org.bjc.icf.DetailController$_closure3.doCall(DetailController.groovy:33)
at org.bjc.icf.DetailController$_closure3.doCall(DetailController.groovy)
at java.lang.Thread.run(Thread.java:619)
I've tried looking up a solution to what might be causing this error online but I can't seem to find anything. Does anyone have any idea why I might all of a sudden be getting MissingPropertyExceptions on previously working code (and yes, I have checked to make sure the property is still in the class).
Try running grails clean - sometimes incremental compilation fails so forcing a full compile will often make weird issues like this go away.
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)]
...
}