grails. Domain class. 1:m - grails

i have domain classes:
package test
class Credit {
String name;
static hasMany = [debts : Debt]
static constraints = {
}
}
and
package test
class Debt {
Integer amount;
Date date;
static belongsTo =[credits: Credit]
static constraints = {
}
}
Need: select max: 10; order: "desc"; sort: "date" rows of Debt associated with the Сredit.get(id)
How can i do it?
solution:
Debt.findAllByCredits(Credit.get(params.id),[max:10, sort:"date",order:"desc"])
but next question about this example:
why, this code work:
def ok = Debt.findAllByCredits(Credit.get(params.id),[max:10, sort:"date",order:"desc"])
println "true:" + ok
but this code not work correct:
def dd = new Debt(credits: Credit.get(params.id))
def wrong =Debt.findAll(dd)
println "no: "+ wrong
all time return all records in table, why?

you could do something like
def hql = "select d from Debt d where credits = ? order by d.date desc"
Debt.findAll(hql, [credit], [max:10])
you might have to tweak that, but something similar should work. Also note, I am assuming that you have an instance of credit that is the parent of the Debts.
You can also use the methods Grails generates dynamically at runtime, based on the properties of your classes
Debt.findAllByCredit(credit, [max:10,sort:"date",order:"desc"]
again, you will need a reference to a credit object.
Check out the docs at
http://grails.org/doc/latest/
specifically the section on findAll and findAllBy under Domain Classes in the left hand nav.

Related

How to save addTo in order by given date/id?

I need some help on my API, when I'm on web, the order is saving correct, but when its on API, it goes all wrong:
def test = parseJSON.sort { a, b -> a.ID <=> b.ID } //or dateTime, will print the same
//order when I print each of them
[IDWeb:0, conductivity:0, ReportId:2, dissolvedOxygen:0, levelWater:1, ID:1, ph:0, redoxPotential:0, temperature:0]
[IDWeb:0, conductivity:0, ReportId:2, dissolvedOxygen:0, levelWater:0, ID:2, ph:0, redoxPotential:0, temperature:0]
[IDWeb:0, conductivity:0, ReportId:2, dissolvedOxygen:0, levelWater:0, ID:3, ph:0, redoxPotential:0, temperature:0]
[IDWeb:0, conductivity:0, ReportId:2, dissolvedOxygen:0, levelWater:4, ID:4, ph:0, redoxPotential:0, temperature:0]
test.each{
def sample = new SampleWater()
sample.levelWater = it.levelWater
sample.conductivity = it.conductivity
sample.dissolvedOxygen = it.dissolvedOxygen
sample.redoxPotential = it.redoxPotential
sample.ph = it.ph
sample.temperature = it.temperature
water.addToSamples(sample)
}
return water
My problem is that addTo is not saving in order. How can I solve this?
Make sure you have defined the type of samples as a List in your Water domain class so that we can maintain the insertion order:
class Water {
static hasMany = [samples: Sample]
List<Sample> samples = []
}
class Sample {
def levelWater
}
By default implementation of hasMany is of type Set which does not maintain the insertion order but is responsible for uniqueness.
Since, now you samples will be saved in the same order as they are inserted.
You have to specify with order you want to apply to the list of SampleWater in the "water" domain class.
i.e:
class BlogCategory {
static hasMany = [
entries : BlogEntry
]
static mapping = {
entries: sort:'dateCreated', order:'desc'
}
}
In this example BlogEntry will be ordered respect dateCreated.

Grails HasMany on Long

I have an class with an hasMany on a Long:
class Test {
static hasMany = [longList:Long]
}
I want to filter on longList with Criteria:
Test.createCriteria().list{
'in'('longList',[Long.valueOf('1')])
}
I get an SQLException: No value specified for parameter 1.
The SQL looks like this:
select * from test this_ where this_.id in (?)
i tried things like:
createAlias('labours', 'l')
eq('l',Long.valueOf(filter.labourId)) )
or
eq('labours.value', Long.valueOf(filter.labourId))
But i can't get it working.
For a workaround i would make another domain Class:
class Test {
statis hasMany=[longList:TestLongList]
}
class TestLongList{
Long longListItem
static belongsto = [test:Test]
}
This should be working but i must always create an TestLongList Instance if I create an Test Object, so the code would turn from:
test.addToLongList(Long.valueOf('22'))
to
TestLongList tll = new TestLongList
tll.test= test
tll.longListitem = Long.valueOf('22')
tll.save()
test.addToLongList(tll)
Is there a way to stay with the Long-List without HQL?
You have to rewrite the criteria to look like this:
Test.createCriteria().list{
createAlias('longList', 'l')
'in' ('l.elements', [1L])
}
This boils down to the property name under which Hibernate stores collection (elements).

Criteria search

I ran into some problems while trying to count items.
Imagine the following domain classes
class Book {
String name
}
class Author {
String name
static hasMany = [books:Book]
}
How do I get a list of Authors sorted by number of Books?
here's my try:
def c = Author.createCriteris()
c.list {
projections {
count 'books', 'numBooks'
groupProperty 'id'
}
order 'numBooks', 'desc'
}
but somehow I get only unusable results... and I don't know how to join the Author objects to the rsult list.... :-(
Havent tried it, but couldn't you do something like:
class Author {
String name
static hasMany = [books:Book]
static namedQueries = {
sortByMostBooks {
books {
order('size', 'desc')
}
}
}
}
And then get access by the cleaner named query
Author.sortByMostBooks.list();
In addition, you may want to include a belongsTo in you Book domain class:
static belongsTo = Author;
or:
static belongsTo = [authors:Author];
if a book is likely to have multiple authors
got something!
I still don't know how to do it with a criteria, but by switching to HQL, I succeeded.
So if someone comes up with a criteria solution, he will still get the bonus for the correct answer :-)
here is my query:
Author.executeQuery("""
select
a, size(a.books) as numBooks
from
Author a
group by
id
order by
numBooks DESC
""",[max:20])
This query isn't efficient, since it fetches all Authors in a loop, but that's ok for now.

How To Put A Constraint On Relationships In A Domain Class, In Grails?

I have a domain three domain class like this :
Tasks.groovy
class Tasks {
static belongsTo = [ user : User ]
//other fields
Date startDate
Date endDate
}
User.groovy
class User {
//relationships. . . .
static belongsTo = [ company : Company, role : Role, resource : Resource]
static hasMany = [ holidays : Holiday, tasks : Tasks]
//other fields
}
Holiday.groovy
class Holiday {
static belongsTo = User
Date startDate
Date endDate
//other fields
}
Now when I create a Tasks instance, I want to put a constraint such that the Tasks startDate and endDate doesn't fall within the User's Holidays startDate and endDate. And throw and error if it has.
I want a way to put this constraint on my domain class itself(i.e on Tasks).
Is it possible to do so?
Thanks in advance.
You can accomplish this using a custom validator.
startDate(validator: {val, obj->
obj.user.holidays.every{holiday-> val <= holiday.startDate || val >= holiday.endDate }
})
You can encapsulate your custom validation logic as a closure. You will have to add similar logic for endDate as well.
I would go with having a createTask function in the User domain, since Task should not be fiddling with variables that are so far removed (self -> User -> Holiday -> startDate).
It is quite clear that the User knows when its holiday starts and ends and thus would easily validate the given start and end dates for a new Task.

How to set uniqueness at DB level for a one-to-many association?

My problem is simple but I could not find any GORM syntax for this.
Consider the following class:
class Article {
String text
static hasMany = [tags: String]
static constraints= {
tags(unique: true) //NOT WORKING
}
}
I want to have one unique tag name per article defined in my constraints but I cannot make it with the above syntax.
Clearly I need in DB schema something like:
create table article_tags (article_id bigint, tags_string varchar(255), unique (article_id , tags_string))
How can I do that?
PS: I am also stuck for setting constraints on tag minimum and maximum size
FYI, you can also use a custom validator in domain classes:
static constraints = {
tags(validator: {
def valid = tags == tags.unique()
if (!valid) errors.rejectValue(
"tags", "i18n.message.code", "default message")
return valid
})
At the database level, you can customize DDL generation by having the following code in grails-app/conf/hibernate/hibernate.cfg.xml:
<hibernate-mapping>
<database-object>
<create>
ALTER TABLE article_tags
ADD CONSTRAINT article_tags_unique_constraint
UNIQUE(article_id, tags_string);
</create>
<drop>
ALTER TABLE article_tags
DROP CONSTRAINT article_tags_unique_constraint;
</drop>
</database-object>
</hibernate-mapping>
Initially I looked at the joinTable mapping to see if it would support a unique key, but it won't.
The best solution I can think of is the following combination:
Manually run the SQL statement to add the unique constraint. If you have some sort of database management tool (e.g. Liquibase), that would be the ideal place.
Explicitly declare the association as a Set. This should avoid Hibernate ever running into the unique constraint, anyway.
class Article {
static hasMany = [tags: String]
Set<String> tags = new HashSet<String>()
}
An alternate solution would be to explicitly declare your child domain (Tag) and set up a many-to-many relationship, adding the unique key to the join table there using constraints. But that's not really a great solution, either. Here's a primitive example:
class Article {
static hasMany = [articleTags: ArticleTag]
}
class Tag {
static hasMany = [articleTags: ArticleTag]
}
class ArticleTag {
Article article
Tag tag
static constraints = {
tag(unique: article)
}
}
With this, though, you have to explicitly manage the many-to-many relationship in your code. It's a bit inconvenient, but it gives you full control over the relationship as a whole. You can find out the nitty gritty details here (the Membership class in the linked example is akin to the ArticleTag in mine).
Perhaps one of the gurus more familiar with GORM will chime in with a more elegant solution, but I can't find anything in the docs.
EDIT: Note that this approach does not consider a unique(article_id , tags_id) constraint. It also raises an issue with two Articles having the same tags. - Sorry.
While this is not officially documented (see the relevant parts of the Grails Reference Documentation here and here) constraints on one-to-many associations are simply ignored by GORM. This includes unique and nullable constraints, and probably any.
This can be proved by setting dbCreate="create" and next, by looking at the database schema definition. For your Article sample and the PostgreSQL database, this would be:
CREATE TABLE article_tags
(
article_id bigint NOT NULL,
tags_string character varying(255),
CONSTRAINT fkd626473e45ef9ffb FOREIGN KEY (article_id)
REFERENCES article (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT article0_tags_article0_id_key UNIQUE (article_id)
)
WITH (
OIDS=FALSE
);
As can be seen above, there are no constraints for the tags_string column.
In contrast to constraints on association fields, constraints on "normal" instance fields of domain classes do work as expected.
Thus, we'll want to have some kind of Tag, or TagHolder, domain class, and we'd need to find a pattern that still provides the Article with a clean public API.
First, we're introducing the TagHolder domain class:
class TagHolder {
String tag
static constraints = {
tag(unique:true, nullable:false,
blank:false, size:2..255)
}
}
and associate it with the Article class:
class Article {
String text
static hasMany = [tagHolders: TagHolder]
}
In order to provide a clean public API, we're adding the methods String[] getTags(), void setTags(String[]. That way, we can also call the constructor with named parameters, like, new Article(text: "text", tags: ["foo", "bar"]). We're also adding the addToTags(String) closure, which mimicks GORM's corresponding "magic method".
class Article {
String text
static hasMany = [tagHolders: TagHolder]
String[] getTags() {
tagHolders*.tag
}
void setTags(String[] tags) {
tagHolders = tags.collect { new TagHolder(tag: it) }
}
{
this.metaClass.addToTags = { String tag ->
tagHolders = tagHolders ?: []
tagHolders << new TagHolder(tag: tag)
}
}
}
It's a workaround, but there's not too much coding involved.
A drawback, we're getting an additional JOIN table. Nevertheless, this pattern allows for applying any available constraints.
Finally, a test case could look like this one:
class ArticleTests extends GroovyTestCase {
void testUniqueTags_ShouldFail() {
shouldFail {
def tags = ["foo", "foo"] // tags not unique
def article = new Article(text: "text", tags: tags)
assert ! article.validate()
article.save()
}
}
void testUniqueTags() {
def tags = ["foo", "bar"]
def article = new Article(text: "text", tags: tags)
assert article.validate()
article.save()
assert article.tags.size() == 2
assert TagHolder.list().size() == 2
}
void testTagSize_ShouldFail() {
shouldFail {
def tags = ["f", "b"] // tags too small
def article = new Article(text: "text", tags: tags)
assert ! article.validate()
article.save()
}
}
void testTagSize() {
def tags = ["foo", "bar"]
def article = new Article(text: "text", tags: tags)
assert article.validate()
article.save()
assert article.tags.size() == 2
assert TagHolder.list().size() == 2
}
void testAddTo() {
def article = new Article(text: "text")
article.addToTags("foo")
article.addToTags("bar")
assert article.validate()
article.save()
assert article.tags.size() == 2
assert TagHolder.list().size() == 2
}
}
Try this:
http://johnrellis.blogspot.com/2009/09/grails-constraints-across-relationships.html
The only way I've found to do this is to write a custom constraint and do a database check for the duplication. I don't think there is a built-in way to use a GORM constraint to accomplish this.

Resources