Dónal
Dónal

Reputation: 187379

Grails one-to-many relationship with index property

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

Answers (4)

demon101
demon101

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

Bob McCracken
Bob McCracken

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

tim_yates
tim_yates

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?

Edit

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 ;-)

Edit 2

Of course, another alternative is:

  Integer getIndex() {
    parent.children.indexOf( this ) + 1
  }

Upvotes: 1

rascio
rascio

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

Related Questions