Multiple many-to-many associations in one Grails domain class - grails

I am using Grails 3.0.6 and am struggling with a complicated and highly interconnected domain model. I have classes with multiple many-to-many associations to other classes and I am left with no choice but to have multiple belongsTo associations on at least one class. I am unable to figure out the syntax to represent this.
My domain model was quite complicated, but I was able to reduce my problem to this simplified example:
class Graph {
static hasMany = [vertices: Vertex]
}
class OtherClass {
static hasMany = [vertices: Vertex]
}
class Vertex {
static hasMany = [graph: Graph, other: OtherClass]
}
In this simplified example, I could get around the problem by declaring the ownership between the domain classes on Graph and OtherClass... In my complicated domain model, I don't have this choice because there are too many classes with multiple many-to-many associations.
I have tried this:
class Vertex {
static hasMany = [graphs: Graph, others: OtherClass]
static belongsTo = Graph, OtherClass
}
but I get an NPE.
I have tried this:
class Vertex {
static hasMany = [graphs: Graph, others: OtherClass]
static belongsTo = [graphs: Graph, others: OtherClass]
}
but I still get "GrailsDomainException: No owner defined between domain classes [Graph] and [Vertex]"
Is there something I could do with mappedBy to correctly represent this?
In many of my many-to-many associations, cascading saves are not actually wanted (although they won't hurt), so I don't need belongsTo (or an "owner") for that purpose. This makes me wonder if associations on the domain classes are really how I should be modeling these relationships. Is there something else I could be doing?

per Burt Beckwith's comment, I created an additional domain class to represent the join table. Now, one many-to-many association is broken down into two one-to-many associations and the problem does not arise.
Example:
class Graph {
static hasMany = [graphVertexRelations: GraphVertexRelation]
}
class OtherClass {
static hasMany = [vertices: Vertex]
}
class Vertex {
static hasMany = [graphVertexRelations: GraphVertexRelation, others: OtherClass]
static belongsTo = OtherClass
}
class GraphVertexRelation {
static belongsTo = [graph: Graph, vertex: Vertex]
static GraphVertexRelation create(Graph graph, Vertex vertex, boolean flush = false) {
new GraphVertexRelation(graph: graph, vertex: vertex).save(flush: flush, insert: true)
}
}

The exception that you're seeing "GrailsDomainException: No owner defined between domain classes [Graph] and [Vertex]" means that the ORM can't figure out what the base class is, and there is a cyclical relationship between Graph and Vertex.
If you want to maintain the relationship to see what Graph a Vertex is in, you can do a backwards lookup using a criteria.
class Graph {
static hasMany = [vertices: Vertex]
}
class OtherClass {
static hasMany = [vertices: Vertex]
}
class Vertex {
static transients = ['graphs']
static hasMany = [other: OtherClass]
List<Graph> getGraphs() {
// Backwards link, using the graph table
Graph.withCriteria() {
vertices {
inList("id", [this.id.toLong()])
}
}
}
}

Related

GORM One to Many relationship across multiple datasources

I see there is a strategy for a one to one mapping for domain objects across different databases. But I am trying to associate two Domain objects that are in different datasources and have a one to many relationship.
class DomainA {
// default data source
}
class DomainB {
static hasmany = [domainA: DomainA]
static mapping = {
datasource 'ds2'
}
}
Any suggestions on how to make this work? Or a workaround?
Found a solution to this and it works pretty well. Solution is to create a join table in the schema you own.
E.g.
class DomainA {
// default data source
}
class DomainB {
List<DomainA> domainAList
static transients = ['domainAList']
static hasmany = [domainAIds: Integer]
static mapping = {
datasource 'ds2'
domainAIds joinTable: [name: 'DOMAINB_DOMAINA', key: 'DOMAINB_ID', column: 'DOMAINA_ID']
}
List<DomainA> getDomainAList(){
domainAList = domainAIds.collect { DomainA.get(it) }
domainAList
}
}

grails: multiple belongsTo with back reference

Is it possible to have a domain class that belongs to multiple domain classes with back reference? For instance:
class Person {
List<Book> books
static hasMany = [books: Book]
}
class Organization {
List<Books> books
static hasMany = [books: Book]
}
class Book {
def owner // what's the type?
static belongsTo = [Person, Books]
}
A Book can belong to a Person or an Organization, but not both.
Person and Organization have separate sequence IDs.
The solution I came up with is:
class Book {
Long ownerID
String ownerClass
static belongsTo = [Person, Books]
static transients = ['owner']
static constraints = {
ownerId(nullable:false, blank:false)
ownerClass(nullable:false, blank:false)
}
public BookOwner getOwner() {
grailsApplication.getArtefact("Domain", ownerClass)?.getClazz()?.get(ownerId)
}
}
where BookOwner is an Interface implemented by Person and Organization. So calling a bookInstance.owner will return a Person or Organization instance, both BookOwner.
My solution works well, but it doesn't feel right - a sure sign that I am not fully understanding what I'm doing. What's the best way to implement this? Should I completely give up on having the extremely convenient back reference?
Thank you
I guess, you should have made Owner superclass. Grails will create Owner table with field class meaning child class names (in your case: Person, Organization).

belongsTo array plus hasMany ownership

I have a Grails class that both has got a many-to-many relationship (with the other side being the owner) as well as a n:1 relationship.
I could not find an answer on the web how to put the ownership into the belongsTo clause.
This is the code:
class PanelType {
static hasMany = [elements: LabValueType]
}
class LabValueType {
static belongsTo = [labUnit: LabUnit]
// This is what would be needed to have a bidirectinal n:m relationship
// belongsTo = PanelType
// static hasMany = [panelTypes: PanelType]
}
If I leave it like this, the application builds the database correctly, but I won't be able to navigate from LabValueType to PanelType.
I found one answer (from 2008!) that said I should write:
static belongsTo = [PanelType, LabUnit]
BUT this way, the field lab_unit_id is not created in the database, so it does not seem to be correct.
I have found that I can work around this problem by declaring the relationships like this:
LabUnit labUnit
static belongsTo = PanelType
static hasMany = [panelTypes: PanelType]
But somehow it is not really 100% satisfying.

Grails Domain Class : hasOne, hasMany without belongsTo

I am new to Grails.
Can I use "hasOne" or "hasMany" without using "belongsTo" to another domain-class?
Thanks in advance.
Yes, you can. See examples in Grails doc: http://grails.org/doc/2.3.8/guide/GORM.html#manyToOneAndOneToOne
hasMany (without belongsTo) example from the doc:
A one-to-many relationship is when one class, example Author, has many
instances of another class, example Book. With Grails you define such
a relationship with the hasMany setting:
class Author {
static hasMany = [books: Book]
String name
}
class Book {
String title
}
In this case we have a unidirectional one-to-many. Grails will, by
default, map this kind of relationship with a join table.
hasOne (without belongsTo) example from the doc:
Example C
class Face {
static hasOne = [nose:Nose]
}
class Nose {
Face face
}
Note that using this property puts the foreign key on the inverse
table to the previous example, so in this case the foreign key column
is stored in the nose table inside a column called face_id. Also,
hasOne only works with bidirectional relationships.
Finally, it's a good idea to add a unique constraint on one side of
the one-to-one relationship:
class Face {
static hasOne = [nose:Nose]
static constraints = {
nose unique: true
}
}
class Nose {
Face face
}
Yes you can, but it behave differently
class Author {
static hasMany = [books: Book]
String name
}
class Book {
String title
}
In this case if you delete Author the books still existing and are independent.
class Author {
static hasMany = [books: Book]
String name
}
class Book {
String title
static belongsTo = [author: Author]
}
In this other case if you delete the Author it will delete all the books pf that author in cascade.
Many-to-one/one-to-one: saves and deletes cascade from the owner to the dependant (the class with the belongsTo).
One-to-many: saves always cascade from the one side to the many side, but if the many side has belongsTo, then deletes also cascade in that direction.
Many-to-many: only saves cascade from the "owner" to the "dependant", not deletes.
http://grails.org/doc/2.3.x/ref/Domain%20Classes/belongsTo.html
yes very easy like a class defintion but only specify hasMany but no need for hasOne
class Student {
String name
User userProfile
static hasMany =[files:File]
}
class User {
String uname
Student student
}
class File {
String path
Student student // specify the belongs to like this no belong to
}
Done!!

How to maintain order in grails many-many relationship

My project requires me to maintain the insertion and retrieval order in a many-many relationship. By default, groovy saves the elements as a Set in many-many relationship. I want to do it in a List. I am not sure how to update the relationship to use List instead of Set. Any help would be appreciated.
class Course{
static belongsTo = Teacher
static hasMany = [teacher:Teacher]
static mapping = {
teacher joinTable : [name: TeacherCourse]
}
}
class Teacher{
static hasMany = [course:Course]
static mapping = {
course joinTable : [name: TeacherCourse]
}
}
save() call on either Teacher or Course also inserts a new row in TeacherCourse table. It works with no issues. In Database there the tables are:-
Teacher (PK: Id)
Course (PK: Id)
TeacherCourse(PK: [Teacher_id,Course_id])
Is there a way I can maintain the order of insertion and retrieval in many-many relationship?
Thank you..
Edit
In controller save()
def courseInstance = new Course()
List <Teacher> teacherList= []
teacherList.add(Teacher.findById(65))
teacherList.add(Teacher.findById(36))
courseInstance.courseUnits = teacherList
courseInstance.save(flush:true)
Try this:
class Course {
List teachers
static belongsTo = Teacher
static hasMany = [teachers:Teacher]
static mapping = {
teachers joinTable : [name: TeacherCourse]
}
}
class Teacher {
List courses
static hasMany = [courses:Course]
static mapping = {
courses joinTable : [name: TeacherCourse]
}
}
Reference

Resources