Grails - associations by referencing id only - grails

I'm trying to employ Domain Driven Design into my application, and in order to limit deep traversal between aggregate roots, the advice is to associate them using only their identities. Say I have the following domain classes,
Person
class Person {
Integer age
String name
}
and PhoneBill
class PhoneBill {
// reference to Person
Long accountHolderId
BigDecimal amount
}
As it is, this setup can already work. However, I want to add foreign key constraint to accountHolderId just to make sure any value set to it already exists in the data store. The official Grails docs don't seem to cover this.

Assuming your Person object still has an id, you can add a custom validator which returns an error if the id isn't valid.
class PhoneBill {
// reference to Person
Long accountHolderId
BigDecimal amount
static constraints = {
accountHolderId validator: {
!Person.exists(it) ?: 'your.custom.error.message.key'
}
}
}
Note that .exists() is only available for Grails 2.3.x, I believe. If you're using an older version, you can call .get() just as well.

Related

Grails criteria duplicate association path error

I am trying to use aliases along with the normal association names in a criteria which is giving me "duplicate association path error" my classes are as follows
class FlightReservation{
Flight flight
User usr
String title
}
class Flight {
String flightNumber
Category category
}
class Category {
String name
}
Criteria query
FlightReservation.createCriteria().list(){
createAlias("flight", "flt", CriteriaSpecification.LEFT_JOIN)
flight{
location{
eq("name", "abc")
}
}
order("flt.flightNumber", "asc")
}
Now as i think about it, it seems obvious and perhaps a Hibernate limitation
so i want to know if there's an alternate approach to achieve this
I know i can use fetchMode to load the flight association
but eliminating alias from the query would make things difficult for order clause( which is going to be dynamic and nesting closures would make things ugly)
One might say why can't I use "flt" (alias) in both the places? Actually this other criteria which uses the nested closure instead of alias comes from some other part of the code and I am supposed to reuse that code.
Let me know, if the question isn't clear enough, any insights on this error would be really helpful.

How to dynamically add a property / field to a domain class in Grails?

For a project I'm currently working on I need to dynamically add properties to a domain class and persist them later in the database. In general, I need a key/value store attached to a "normal" domain class. Sadly I cannot use a NoSQL database (e.g. Redis).
My approach would be to handle the additional properties on a save() by identifying them within afterInsert or afterUpdate and writing them to another table - I would prefer not to use a map property within the domain class but an additional "Field" table (to better support searches).
I tried to add properties using the metaClass approach:
person.metaClass.middlename = "Biterius"
assert person.middlename == "Biterius" // OK
This works and I can identify the additional properties in the afterInsert/afterUpdate methods but it seems that I cannot change the value thereafter - i.e., the following does not work:
person.middlename = "Tiberius"
assert person.middlename == "Tiberius" // FAIL
Then I tried an Expando approach by extending the Person class by the Expando class (directly ("Person extends Expando") and via an abstract intermediate class ("Person extends AbstractPerson" and "AbstractPerson extends Expando")).
def person = new Person()
assert person in Person // OK
assert person in AbstractPerson // OK
assert person in Expando // OK
Both variants did not work - I could assign values to arbitrary "properties" but the values were not stored!
person.mynewproperty = "Tiberius" // no MissingPropertyException is thrown
println person.mynewproperty // returns null
So how can I add properties to a domain class programmatically during runtime, change them and retrieve them during afterInsert or afterUpdate in order to "manually" store them in a "Fields" table?
Or am I doing something completely wrong? Are there other / simpler ways to do this?
What about turning your DB into a "NoSQL" one?
In one of my projects, I just used a String-property to store a map as JSON-Object.
For Groovy it's not a big problem to convert between a map and a JSON-Object. And since you can access a map just like an object with properties, I found this solution very convenient.
Only drawback: you have to plan the size of your String-property in advance...
Update: sorry, just read that you want to support searches...
what about
class Person {
...
static hasMany = [extProperties:KeyValue]
...
def invokeMethod(String name, args) {
if (name.startsWith('get')) {
//an unknown properties's getter is called
}
//add same for setter
}
}
class KeyValue {
String key
String value
}
I guess such a schema would give you all freedom you need. Even without the hasMany, you can make use of invokeMethod to handle your external tables...
The getter and setter can save your values in a transient string propertie (static transients = ['myTransientProperty']). This property should be available in the afterInsert / `afterUpdate´ events.
Why don't you just create a map of strings on the domain object and store your extra data there manually? Unless you're storing complex data you should be able to cast anything you need to/from a string.

GORM mapping: make an index unique

I'm feeling a little slow today. I'm trying to do something that I think is very simple. I have a Domain class with a property called 'name'. I want 'name' to have an index, and I want the index to require that the 'name' is unique. I've set the unique constraint and tried creating an index. I can't make sense out of the Gorm docs as to how I add the unique attribute to the index. Here's some code:
class Project {
String name
static hasMany = [things:Things]
static mapping = {
name index:'name_idx'
}
static constraints = {
name(unique:true)
}
}
All is well with the above, except when do "show indexes from project" in mysql it shows my name key as not unique. I know the problem is that I am not specifying unique in the mapping, but quite frankly the docs for gorm are making my head hurt. I see all kinds of stuff about columns, but I can't find a single example anywhere on the web that shows what I want to do. I don't need complex mappings or compound keys, I just want to know the syntax to add the unique attribute to the mapping declaration above. Any advice welcome.
I also did a grails export-schema and see the following:
create index name_idx on project (name);
Nothing in that to indicate this index requires unique values
A related followup question would be once I succeed in making that index unique, what type of error should I expect when I go to save a Project instance and the name is not unique? Is there a specific exception thrown? I realize that even if I check that a given 'name' is unique there's still a possibility that by the time I save it there may be a row with that name.
I'm quite sure the syntax to do what I want is simple but I just can't find a simple example to educate myself with. I've been to this page but it doesn't explain HOW the uniqueness is enforced. I'd like to enforce it at the name index level.
The indexColumn allows additional options to be configured. This may be what you're looking for.
static mapping = {
name indexColumn:[name:'name_idx', unique:true]
}
Grails Documentation for indexColumn
If you put only the unique constraint the GORM send DDL to create an unique index on database.
static constraints = {
name nullable: false, unique: true
}

db4o - unique constraints on a group of fields

I would like to have unique constraints operate on more than 1 field for several entities. How can I achieve this with db4o?
Thanks,
Walter
Currently db4o doesn't support the UniqueConstrain on multiple fields. You can set unique-constrains only field by field, but not combine them.
#Ladlestein Well the intention is good, but it doesn't work. db4o manageds objects by it referencial identity. When you apply the unique-constrain on the Foo.name, you ensure that the reference is unique. So no other object can have the same reference to a name object. But you're not interested that the reference is unique, but you want to have a unique content of the names.
Sounds like a composite key. Can you create a new class that contains the key-constituting fields, and use a member variable with that type in place of the key-constituting fields in your original class?
i.e. where you had
class Foo {
String given_name;
String family_name;
}
instead use
class Name {
String given_name;
String family_name;
}
class Foo {
Name name;
}
and make Foo.name the unique field?

What is the best way to declare sorted association in grails domain classes?

It seems that there are two different ways of declaring sorted associations in Grails :
Method 1 (see here) using default sort order
class Book {
String title
}
class Author {
static hasMany = [books : Book]
static mapping = { books sort: "title"}
}
Method 2 (see here) using SortedSet
class Book implements Comparable {
String title
int compareTo(obj) {
title <=> obj.title
}
}
class Author {
SortedSet books
static hasMany = [books : Book]
}
I am not sure which one to use and what is the difference (if any), pros and cons between using one against the other.
I would appreciate any clarification.
Thank you
I started to dig into how this all works and then found that method 1 is actually busted in current versions of grails (tested in both 1.2.1 and 1.3). When you actually try to retrieve an author and look at it's books, it throws an exception
There is an open defect for it (4089) which has been open for quite a while.
Here's the exception that gets thrown:
ERROR util.JDBCExceptionReporter - Column not found: BOOKS0_.TITLE in statement [select books0_.author_books_id as author1_0_, books0_.book_id as book2_0_ from author_book books0_ where books0_.author_books_id=? order by books0_.title]
If and when they finally fix it, the differences between the two methods are that in method one, sorting is done at the database level. As you can see in the exception above, GORM was trying to do an "order by books0_.title" which would use any database index on the book.title field and return the objects in that order.
The second method would sort the objects in memory at the time that they get inserted into the set (using the compareTo method that was defined).
Until the current bug is fixed, I'd use method 2 because it's the only thing that works. It should be fine for relatively small collections of things. After it's fixed, I'd potentially prefer method 1 as the database should be quicker at doing the sorting with an index on the sort field.

Resources