Reputation: 187379
In my Grails app, I have two domain classes with a one-to-many relationship, e.g.
class Parent
static hasMany = [children: Child]
}
class Child {
Integer index
static belongsTo = [parent: Parent]
}
I would like index
to record the relative order in which the children were created, such that the first child of a parent will have index 1, the next will have 2, etc. The indices don't have to be consecutive, because a child could be deleted, but they should always reflect the relative order of creation.
I considered doing something like the following to set the index
property
class Child {
Integer index
static belongsTo = [parent: Parent]
def beforeValidate() {
def maxIndex = Child.createCriteria().get {
projections {
max('index')
}
eq('parent', parent)
}
this.index = maxIndex + 1 ?: 1
}
}
But of course this doesn't work because it will assign the same index to two transient Child
instances. What is the simplest way to maintain this index
property?
FWIW, I'm not too concerned about preventing any other code from setting index
but if there is some way to do that, it'd be a bonus.
Upvotes: 1
Views: 1548
Reputation: 566
You can use indexColumn grails mapping feature
https://docs.grails.org/latest/ref/Database%20Mapping/indexColumn.html
Class Parent(){
static hasMany = [children: Child]
List children // Generates column `book_idx` in table for Book.
static mapping = {
children indexColumn: [name: "index", type: Integer]
}
}
Upvotes: 0
Reputation: 516
You should just be able to use the id property automatically created for any domain class in Grails. By default, this value will be incremented for each child that is saved to the DB. So...
parent.children.sort {a,b -> a.id <=> b.id}
Will return the parent's children in the order they were saved to the database.
If you absolutely need a property named "index", I believe you can do something like:
Integer index
static mapping = {
id name: 'index'
}
Be warned, however, that index is probably a reserved word in many DBs, so you won't be able to have a column named index and this will fail. Consider a different name for your property.
Upvotes: 0
Reputation: 171154
I thought you could do:
class Parent
List children
static hasMany = [children: Child]
}
And then the children are kept in a List, so implicitly have an index and order. I guess you actually want the elements to have an index field though rather than this implied ordering?
This works (it seems) but will hit the database every time you query the index
class Child {
def grailsApplication
static belongsTo = [ parent: Parent ]
Integer getIndex() {
grailsApplication.mainContext.sessionFactory.currentSession.with { sess ->
createSQLQuery( 'SELECT c.children_idx FROM child c where c.id = :id' )
.setBigInteger( "id", this.id )
.list()
.head() + 1
}
}
String toString() {
"Child @$index"
}
}
And makes me feel a little queasy ;-)
Of course, another alternative is:
Integer getIndex() {
parent.children.indexOf( this ) + 1
}
Upvotes: 1
Reputation: 9279
Use an id with auto increment...once a child is created it will never use another id equals but greater...if you just need to know the order of creation you can use the id or add a:
class Child{
Date dateCreated;
static belongsTo = [parent: Parent]
}
It will set automatically the date of creation and you can use it to know the order :)
Upvotes: 0