I have a table with a self-referencing field:
Class Book{
Integer id
String name
Book version
}
When I add books without the "version", the version field is null
now I have to query the Book table for records that don't have version (their version field is null), the following code won't work:
def results = Book.withCriteria{
eq("version", "null")
}
But I'm getting this exception:
org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of Book.id
what query should i use?
version is a keyword in GORM used for optimistic locking. Modify your domain and the criteria as below to make the criteria return appropriate results.
//Domain
class Book {
Integer id
String name
Book bookVersion
}
//Criteria
def book = new Book(name: "test", version: null)
book.id = 1
book.save(flush: true)
def results = Book.withCriteria{
isNull("bookVersion")
}
assert results && results[0] instanceof Book
Also note, bookVersion in the question is of type Book, it cannot be compared to String null.
Related
I have two domain classes with many to many relationship with extra column.I created below domain classes by following the logic from the forums and still face an issue in saving the data in additional domain class.Roylaty is the additional column to save the value in the mapping table.
Below are the 3 domain classes:
class AuthorBook implements Serializable {
Author author
Book book
String royalty
boolean equals(other) {
if (!(other instanceof AuthorBook)) {
return false
}
other.author?.id == author?.id &&
other.book?.id == book?.id
}
int hashCode() {
def builder = new HashCodeBuilder()
if (author) builder.append(author.id)
if (book) builder.append(book.id)
builder.toHashCode()
}
static AuthorBook get(long authorId, long bookId) {
find 'from AuthorBook where author.id=:authorId and book.id=:bookId',
[authorId: authorId, bookId: bookId]
}
static AuthorBook create(Author author, Book book, boolean flush = false) {
new AuthorBook(author: author, book: book).save(flush: flush, insert: true)
}
}
class Author implements Serializable{
string name(nullable:false,unique:true)
Set<Book> getBooks() {
AuthorBook.findAllByAuthor(this).collect { it.book } as Set
}
}
class Book implements Serializable{
string title(nullable:false,unique:true)
Set<Author> getAuthors() {
AuthorBook.findAllByBook(this).collect { it.author } as Set
}
}
In one of my controllers i wrote the below logic:
def author1 = new Author("ABC")
author.save(flush:true)
def book1= new Book("GORM")
book.save(flush:true)
def authorBook = new AuthorBook(royalty:100,author:author1,book:book1)
authorBook.save(flush:true)
For both author and book, it works as expected i.e it won't allow duplicates and in the mapping table too. it won't allow duplicates but I want the output to be as below in the mapping table
Author AuthorBook Book
id Name id author_id book_id royalty id title
1 XYZ 1 1 1 500 1 Gorm
2 1 1 1000
It won't save this value as it is considering the combination of author_id and book_id to be unique even though I did not set any composite key on id's in the mapping table.
What should I change in the mapping table to allow duplicates?
Can you manually insert that row into the database? I suspect this is caused by your implementation of equals and hashcode on AuthorBook.
These two objects are the same:
author=1;book=1;royalty=100 and author=1;book=1;royalty=500 because your equality methods are only comparing author and book.
this is the code for my domains.
class Btr {
Date dateBreak
int timeBreak
String typeBreak
User usuario
static constraints = {
}
static mapping = {
}
}
class User {
String name
String user
String password
String confirmPassword
String state
String extent
String movileNumber
String email
String address
Rol rol
static constraints = {
}
static mapping = {
}
}
This is the code for my controller.
def df = new SimpleDateFormat("yyyy-MM-dd HH:mm")
def startDate = params.startDate
def stopDate = params.stopDate
resultado = Btr .executeQuery("select dateBreak, timeBreak, typeBreak,
user, usuario.rol from Btr inner join User on user = usuario.rol where
dateBreak between :startDate" and :stopDate", [startDate:
df.parse(startDate), stopDate: df.parse(stopDate)])
render (view: "data", model: [result: resultado])
This is my view.
<g:each in="${result}" var="results" status="i">
<tr><td>{results.dateBreak}</td><td>{results.timeBreak}</td><td>
{results.typeBreak} </td><td>${results.usuario.rol}</td></tr>
</g:each>
Then i get this error when i submit the form.
in the GSP, when i am printing data,
Exception evaluating property 'dateBreak' for java.util.Arrays$ArrayList, Reason: groovy.lang.MissingPropertyException: No such property: dateBreak for class: java.sql.Timestamp
could someone please tell me how to join tables in grails with executeQuery and also would be nice to learn to do it with, withCriteria
resultado = Btr .executeQuery("select dateBreak, timeBreak, typeBreak,
user, usuario.rol from Btr inner join User on user = usuario.rol where
dateBreak between :startDate" and :stopDate", [startDate:
df.parse(startDate), stopDate: df.parse(stopDate)])
Should be
resultado = Btr .executeQuery("""select new map (btr.dateBreak as dateBreak, btr.timeBreak as timeBreak, btr.typeBreak as typeBreak,
u as user, user.usuario.rol as rol) from Btr btr join btr.user u where
btr.dateBreak between :startDate and :stopDate""", [startDate:
df.parse(startDate), stopDate: df.parse(stopDate)])
what you have is raw sql and not HQL which is a slight variation and uses actual domain objects to join
Use left join for hasMany where it may be null join for typical one to one relationship
Also use left join if one to one relationship can be null
Beyond that you could have put your actual query as a raw sql query like so
def sql=new Sql(dataSource)
return sql.rows(query,whereParams)
I have an entity, Student, defined in Student.groovy as:
#EqualsAndHashCode(includes = ['id'])
class Student {
Long id
String name
String type
University university
static mapping = {
university column : 'UNIVERSITY_ID'
}
}
and a University entity, defined in University.groovy as:
class University {
Long id
String name
static mapping = {
id column : 'id', generator : 'assigned'
}
}
I've been trying to switch from calling
Student.list(sort: ..., order: ...)
to calling:
Student.findAll("from Student s where type = :type ", [type : 'T'], [ sort : 'name' ])
This fails to order correctly by the name field. The previous version, using list worked fine.
I've also tried calling something like
Student.findAll(sort : 'name') { type == "T" }
which worked fine like this, but when trying to sort by the university.name
Student.findAll(sort : 'university.name') { type == 'T" }
it raised an error regarding the university.name field not being found.
Anybody have any idea on how to do this properly?
Thank you.
Use executeQuery instead of findAll - they should function the same, but I've found that executeQuery is for some reason a more direct caller of the HQL, and findAll fails or returns unexpected results in some cases.
So that first query would be
Student.executeQuery(
'select s from Student s where s.type = :type order by s.name',
[type : 'T'])
and ordering by university name would be
Student.executeQuery(
'select s from Student s where s.type = :type order by s.university.name',
[type : 'T'])
I like HQL and tend to use it a lot, but it couples you to Hibernate and relational databases - if you want to switch to a NoSQL database these queries will fail. Criteria queries, "where" queries and finders all use criteria queries internally, and those are converted to native query API calls by the GORM implementation.
The equivalent criteria queries would be
Student.withCriteria {
eq 'type', 'T'
order 'name', 'asc'
}
and
Student.withCriteria {
eq 'type', 'T'
university {
order 'name', 'desc'
}
}
Some unrelated notes:
You shouldn't use id in equals or hashCode calculations; if you have a persistent Student and a new non-persistent instance with the same name, type, and University, they should be considered equal, but since the non-persistent instance's id will be null they'll be considered different.
You don't need to specify the id property - Grails adds it and the version field to the bytecode via an AST transformation during compilation.
There's no need to map the column name of the university property to 'UNIVERSITY_ID' - that's what it would be anyway.
You can omit the redundant column setting in the id mapping.
Here's the Student class with cruft removed:
#EqualsAndHashCode(includes = ['name', 'type', 'university'])
class Student {
String name
String type
University university
}
and University:
class University {
String name
static mapping = {
id generator: 'assigned'
}
}
I am using Grails 2.2.4 and having one Domain contains value as map and I want to find domain object using key of map. Please help me to resolve this issue.
Student.groovy
package com.grails
import java.util.Map;
class Student {
String firstName
String lastName
Map address
static constraints = {
}
}
When My application are run I can see that Grails application create tables in database are as follow:
1) first table
student
id
version
first_name
last_name
indexes
2) second table
student_address
address
addres_idx
addres_elt
When I save Domain as:
def std = new Student()
std.firstName = 'Piyush'
std.lastName = 'Chaudhari'
std.address = [city:'Surat',state:'Gujarat',pincode:'38001']
std.save(flash:true)
values are insert in database as follow:
student table
ID VERSION FIRST_NAME LAST_NAME
1 0 Piyush Chaudhari
student_address table
ADDRESS ADDRESS_IDX ADDRESS_ELT
1 city Surat
1 state Gujarat
1 pincode 38001
Now, I want data or row using GORM like Student.findBy_____ or Student.findAllBy______
where 'city' = surat
Any one can help me to resolved this issue?
You can use:
Student.findBy<FieldName1>And<FieldName2> (<FieldNameParameter1>, <FieldNameParameter2>)
Or Either:`
Student.list().find { it.address.city == 'Surat' }
Student.list().findAll { it.address.city == 'Surat' }
`
I don't think that you can search things like this using maps.
Maybe you can do this:
def students = Student.list()
def result = students.each { student -> student.address.city == 'Surat' }
println("Resultado" + result)
But this is a very bad way to do this kind of things
Define an address class, and then add an address field to the student class (this will change how your tables are mapped in the database):
class Student {
String firstName
String lastName
Address address
static constraints = {
}
}
class Address {
String city
String state
String pincode
}
Address should be another entity in your domain, not a map of values. Remember that Grails GROM is an ORM, so you should design your domain using a OOP model in order to take advantage of the dynamic finders and criterias for doing queries.
With those changes in place, you can now use a simple criteria:
def students = Student.withCriteria{
'address'{
eq('city', 'surat')
}
}
More information about criterias in the grails docs:
http://grails.org/doc/latest/ref/Domain%20Classes/withCriteria.html
http://grails.org/doc/latest/guide/single.html#criteria
If you want to use Dynamic finders, you will have to get all the address with city = 'surat' and then use a findByAddressInList(...). But i think that in this case, criterias is a better approach
class Book {
String title
Date releaseDate
String ISBN
static belongsTo = [person:Person] // it makes relationship bi-directional regarding the grails-docs
}
class Person {
Book book; // it will create person.book_id
String name
Integer age
Date lastVisit
static constraints = {
book unique: true // "one-to-one". Without that = "Many-to-one".
}
}
There is a test which test if it is real bidirectional or not. As i understand it.
public void testBidirectional() {
def person = new Person(name:"person_c1", age: 99, lastVisit: new Date())
def book = new Book(
title:"somebook_c1",
ISBN: "somebook_c1",
releaseDate: new Date()
)
person.setBook (book)
assertNotNull(person.save())
def bookId = person.getBook().id
Book thatBook = Book.get(bookId)
assertNotNull(thatBook.person) // NULL !!!
}
So, i save a person with a book, and then i got that book from db by id. Then from that book i try to get back the person which book should refer to (because it should be bidirectional, right?). Eventually i got null instead of an instance of the person.
The questing is: how to make that test working?
i have found the solution how to get it working, but still can not understand why it does not work without 'refresh', see below:
public void testBidirectional() {
def person = new Person(name:"person_c1", age: 99, lastVisit: new Date())
def book = new Book(
title:"somebook_c1",
ISBN: "somebook_c1",
releaseDate: new Date()
)
person.setBook (book)
def p = person.save()
assertNotNull p
person.refresh() //load the object again from the database so all the changes made to object will be reverted
//person = Person.get(p.id) // BUT this also gets the object from db ...?
def bookId = person.getBook().id
assertNotNull bookId
def thatBook = Book.get(bookId)
assertNotNull(thatBook.person)
}
So, here as you can see i use 'refresh' to get it working, but why it does not work without 'refresh' but with the following line after 'refresh' - this one:
person = Person.get(p.id) // BUT this also gets the object from db ...?
If i just want to get object from database by id, then it would be without bidirectional?
Your problem is probably caused by the way that Hibernate works. Grails used Hibernate under the hood.
Even when you call "save", the object person may (and usually) not saved in database. That's because Hibernate is programmed to optimize the query, so it often waits to perform all query at then end of the Hibernate session.
That means if you don't call "refresh", the book-person relation (person.setBook) is still in memory, but not saved in database. Hence you can't get the book.person from book.
To enforce the save, you can use "refresh" like the previous answer, or use flush:true.
I still not try, but it's very likely that you will produce desired results with:
person.save(flush:true)