I have a relationship between two domain class: User and Bank, user has many of bank and bank has many of user, the result table I call it as mm_user_banks that keep both table IDs for MM relationship.
class User {
String name
String password
Date createdAt = new Date()
Date loginAt
static hasMany = [banks:Bank]
static fetchMode = [banks: 'eager']
static constraints = {
}
static mapping = {
banks joinTable: [name: 'mm_user_banks', key: 'mm_user_id']
}
}
and Bank...
class Bank {
Long id
String name
static belongsTo = User
static hasMany = [users:User]
static constraints = {
}
static mapping = {
id generator: 'assigned'
users joinTable: [name: 'mm_user_banks', key: 'mm_bank_id'], lazy: false
}
}
I can save users with banks as well, but the problem is when I try to retrieve bank list from an user instance and it only fetch one row even if there is more than one row for this user in mm_user_banks table.
E.g:
User u = new User(name: 'user', password: 'pwd')
u.addToBanks(Bank.findById(1))
u.addToBanks(Bank.findById(2))
u.addToBanks(Bank.findById(3))
u.save(flush: true)
In database it is OK, fetching 3 rows, but when I get the user and see its bank list:
def user = User.findById(1) //OK
println "Size ${user.banks?.size()}" //only 1??? How?
Thanks in advance!
Just tested it out - "user.banks?.size()" returns "3". Are you sure that data in DB is OK?
Anyway, if you want to use eager fetching for collections then define it in "mapping" of User class (instead of "fetchMode" static field):
static mapping = {
banks joinTable: [name: 'mm_user_banks', key: 'mm_user_id'], fetch: 'join'
}
in Bank class use fetch: 'join' in mapping instead of lazy: false
Thanks so much, Sergei, but didn't worked out.
I fixed it in an ugly way, but this is was my remaining way out.
I created another domain class to mapping the MM table, so I did a mm_user_banks class. I use it to fetch records from bank or user and worked OK. To save banks for a user I actually do it in the old way as well, not changed from that. I guess this mighty not be the best way, but like I said, worked out.
I've tried "fetch: 'join'" and "lazy: false" and "static fetchMode = [banks: 'eager']" without success.
The new domain class is:
class UserBanks {
User user
Bank bank
static constraints = {
}
static mapping = {
table name: 'mm_user_banks'
user column: 'mm_user_id'
bank column: 'mm_bank_id'
}
}
I had the similar problem but in my case I had another relation between my equivalents to User and the Bank. This made the hasMany statement to use the wrong relation to map the many to many relation. In my case it was solved by adding a mappedBy statement to the relation:
class Bank{
...
static mappedBy=[users:'banks']
...
}
and
class User{
...
static mappedBy=[banks:'users']
...
}
or as it was in my case, since it was a uni-directional relation:
class ArticleGroup {
String name
static hasMany = [articles:Article]
static mappedBy = [articles: 'none']
static constraints = {
name(nullable:false, blank:false, unique:true, maxSize:100)
}
static mapping = {
table 'article_group'
sort 'name'
articles joinTable: [name:'article_group_article', key: 'articleGroup_id', column:'article_id'], fetch: 'join'
}
Related
I've got 3 classes:
class Author {
static hasMany = [books: Book]
static belongsTo = [company: Company]
String name
}
class Book {
static mapping = {
collection "documents"
id generator: 'assigned',index: true, indexAttributes:[background:true, unique:true, dropDups:true]
}
String id
String name
}
class Company {
static mapping = {
collection "documents"
id generator: 'assigned',index: true, indexAttributes:[background:true, unique:true, dropDups:true]
}
String id
String name
}
I want to use
Author author = Author.getByCompanyAndBook(1,1);
but when running this Grails retrieves all the Book objects from the database.
I need those Books only as identifiers for the Author and I am not going to use those objects.
Is there a way for me to force Grails not to fetch the Books and Companies from the database?
I tried to use:
static mapping = {
books lazy: true
}
but still all of the Books and the Company were loaded.
Edit:
I am using mongo db as my database.
That's weird - collections are lazy by default, so getting an author shouldn't retrieve anything in the books collection until you access the books property. But regardless, even if it worked as expected I recommend that you avoid mapped collections. See this talk which shows why collections are unnecessarily expensive and provides workarounds to minimize the costs.
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
I have 2 domain class with a many to many relationship. When I delete the entity that belongs to the other, I have to remove the relation before in order to avoid a foreign key error. These relations are connected via the third class, third table in MySQL.
class City {
String nameCity
static hasMany = [visits:Visit]
/* FIRST VARIANT. REMOVES ONE VISIT ONLY */
def beforeDelete() {
Visit.withNewSession {
def visitList = Visit.findByCity(this)
visitList.each { it.delete(flush: true) }
}
}
}
//_____________________________________________
class Visit { // it is the relation class
City city
Person person
}
//_____________________________________________
class Person {
String namePerson
static hasMany = [visits:Visit]
}
So when I delete the relation between two classes, it removes one relation only. I mean, if we have 1 City and 1 Person, and try to delete this City, the app functions OK. But if you have more than one Person attached to the City, we will have:
"Cannot delete or update a parent row: a foreign key constraint fails". But one relation is deleted.
If I try to delete the City one more time, the second Person will be deleted. My app behaves like that until the last Person to be deleted. So, beforeDelete() method works great.
My problem is I don't understand how to create a collection of relations and remove them all in a cycle (loop). If I make like this:
class City {
String nameCity
static hasMany = [visits:Visit]
/* SECOND VARIANT. TYPE CAST EXCEPTION */
Collection<Visit> visitList() {
Visit.findByCity(this)
}
def beforeDelete() {
Visit.withNewSession {
visitList().each { it.delete(flush: true) }
}
}
}
I have org.codehaus.groovy.runtime.typehandling.GroovyCastException 'Cannot cast object 'mypackage.Visit : 1' with class 'mypackage.Visit' to class 'java.util.Collection'.
Any thoughts and help highly appreciated.
Have you tried the following? In theory should work...
class City {
String nameCity
static hasMany = [visits:Visit]
class Visit { // it is the relation class
City city
Person person
static belongsTo = [city:City, person:Person]
}
class Person {
String namePerson
static hasMany = [visits:Visit]
}
And just make a normal delete. In this way, if you delete a City or a Person, all its related visits will be deleted
I have a User domain class, and a List one.
Each list must have an author (a user) and each user must have a "primary list". Only some of the lists will have the "primaryList" statute.
So.. somthing like
User:
List primaryList
List:
User author
static belongsTo = User
Of course this does not work as intended because the two relations are mistakenly taken as only one. I should also add a hasMany on the User and other belongsTo to the List..., but I don't want to complicate the example because I want to get the right answer from you.
You may need to use mappedBy to explain how the fields in User and List line up. Here are a couple domains that I wrote that allow a User to author many Lists but only set one to be "primary". There are a couple extra nullable constraints so you can use the scaffolded UI without getting into a chicken-and-egg scenario.
class User {
String name
FooList primaryList
static hasMany = [authoredLists: FooList]
static mappedBy = [primaryList: 'primaryOwner', authoredLists: 'author']
static constraints = {
primaryList nullable: true, unique: true
authoredLists nullable: true
}
String toString() { name }
}
I named this class "FooList" just to avoid confusion with the standard List class:
class FooList {
static belongsTo = [author: User, primaryOwner: User]
static constraints = {
primaryOwner nullable: true, display: false
}
}
Try using the map belongsTo approach:
static belongsTo = [user:User]
That way Grails should see the 2 properties as separate.
I have a problem getting sorting to work with multiple joined tables. For example:
class Account {
static hasMany = [subscriptions: Subscription]
static mapping = {
subscriptions fetch: 'join'
subscriptions sort: 'magazine'
}
}
class Subscription {
static belongsTo = [account:Account, magazine: Magazine]
static maping = {
magazine fetch: 'join'
magazine sort: 'name'
}
}
class Magazine {
String name
static mapping = {
sort name: 'desc'
}
}
When someAccount.subscriptions is called generated query orders by magazine.id. Is there any way to get order by magazine.name? I tried changing to subscriptions sort: 'magazine.name' but get an error there is no such property.
Following: http://grails.org/doc/latest/guide/single.html#5.5.3 Default Sort Order I tried to move sort to association level by removing sort from Account and keeping sort in Subscription only but that ended up removing "order by" from resulting query completely. Any ideas? Thanks.
Try implementing a compareTo method in your Magazine class:
class Magazine implements Comparable {
String name
int compareTo(obj) {
name.compareTo(obj.name)
}
}
Then one in Subscription
class Subscription implements Comparable {
static belongsTo = [account:Account, magazine:Magazine]
static constraints = {
}
int compareTo(obj) {
return magazine.compareTo(obj.magazine)
}
}
Then make the subscriptions a SortedSet in Account:
class Account {
SortedSet subscriptions
static hasMany = [subscriptions:Subscription]
}