sorting problem with multiple joins - grails

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]
}

Related

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

Groovy/Grails many-to-many delete relation

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

Grails many to many using a third 'join' class

I read that a m:m relationship often means there is a third class that isn't yet required. So I have m:m on User and Project, and I created a third domain class, ProjectMembership
The three domains are as follows (minimized for illustration purposes):
User
class User {
String name
static hasMany = [projectMemberships : ProjectMembership]
}
Project Membership
class ProjectMembership {
static constraints = {
}
static belongsTo = [user:User, project:Project]
}
Project:
class Project {
String name
static hasMany = [projectMemberships : ProjectMembership]
static constraints = {
}
}
If I have the ID of the user, how can I get a list of Project objects that they are assigned to?
There are a handful of ways - here are a couple:
def user = User.get(userId)
ProjectMembership.findAllByUser(user).collect { it.project }
or to avoid the query for the User:
ProjectMembership.withCriteria {
user {
eq('id', userId)
}
}.collect { it.project }
Be wary of queries that'll return large result sets - you'll end up with a huge in-memory list of project objects.

many-to-many retrieve only one row

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'
}

Defining default sort-order in Grails/GORM

Let's say I have definied a User object using GORM. Each user can have zero or more Login:s. Each Login has a timestamp. When retrieving user.logins I want the logins to be sorted based on the value of login.date. What is the correct Grails way to achieve this?
Example: I want the following code to list all the user's logins in ascending order.
<g:each var="login" in="${user.logins}">
<tr>
<td>${login.date}</td>
</tr>
</g:each>
These are the referenced classes:
class User {
...
def hasMany = [logins: Login]
static fetchMode = [logins: "eager"]
}
class Login {
Date date
...
def belongsTo = [User]
}
I'm running Grails 1.0.4 which is the latest stable release.
They show how to do this on the GORM page in the reference guide (section 5). The bit you want is near the bottom of that document is the section you want. They have two simple examples:
class Airport {
…
static mapping = {
sort "name"
}
}
class Airport {
…
static mapping = {
sort name:"desc"
}
}
They also have an example of sorting on an association:
class Airport {
…
static hasMany = [flights:Flight]
static mapping = {
flights sort:'number'
}
}
Just make the Login Class implement the Comparable interface:
class Login implements Comparable {
// ...
Date date
public int compareTo(def other) {
return date <=> other?.date // <=> is the compareTo operator in groovy
}
}
and declare the relation to be a SortedSet:
class User {
...
def hasMany = [logins: Login]
SortedSet logins
static fetchMode = [logins: "eager"]
}
The handling of default sort order in Grails/GORM seems to have been radically simplified in Grails 1.1:
Grails 1.1 release notes (search for "Default Sort Order")

Resources