Grails one-to-many mapping with joinTable - grails

I have two domain-classes. One is a "Partner" the other is a "Customer". A customer can be a part of a Partner and a Partner can have 1 or more Customers:
class Customer {
Integer id
String name
static hasOne = [partner:Partner]
static mapping = {
partner joinTable:[name:'PartnerMap',column:'partner_id',key:'customer_id']
}
}
class Partner {
Integer id
static hasMany = [customers:Customer]
static mapping = {
customers joinTable:[name:'PartnerMap',column:'customer_id',key:'partner_id']
}
}
However, whenever I try to see if a customer is a part of a partner, like this:
def customers = Customer.list()
customers.each {
if (it.partner) {
println "Partner!"
}
}
I get the following error:
org.springframework.dao.InvalidDataAccessResourceUsageException: could not execute query; SQL [select this_.customer_id as customer1_162_0_, this_.company as company162_0_, this_.display_name as display3_162_0_, this_.parent_customer_id as parent4_162_0_, this_.partner_id as partner5_162_0_, this_.server_id as server6_162_0_, this_.status as status162_0_, this_.vertical_market as vertical8_162_0_ from Customer this_]; nested exception is org.hibernate.exception.SQLGrammarException: could not execute query
It looks as if Grails is thinking partner_id is a part of the Customer query, and it's not... It is in the PartnerMap table, which is supposed to find the customer_id, then get the Partner from the corresponding partner_id.
Anyone have any clue what I'm doing wrong?
Edit: I forgot to mention I'm doing this with legacy database tables. So I have a Partner, Customer and PartnerMap table. PartnerMap has simply a customer_id and partner_id field.

Given the way 1-many works when you want a join table, I think it's not possible with standard GORM to make it bidirectional and access a Customer's Partner. But you can map the join table with a domain class and access things that way:
Customer:
class Customer {
String name
def getPartner() {
PartnerMap.findByCustomer(this)?.partner
}
}
Partner:
class Partner {
String name
def getCustomers() {
PartnerMap.findAllByPartner(this)*.customer
}
}
PartnerMap:
import org.apache.commons.lang.builder.HashCodeBuilder
class PartnerMap implements Serializable {
Partner partner
Customer customer
boolean equals(other) {
if (!(other instanceof PartnerMap)) {
return false
}
other.partner?.id == partner?.id &&
other.customer?.id == customer?.id
}
int hashCode() {
def builder = new HashCodeBuilder()
if (partner) builder.append(partner.id)
if (customer) builder.append(customer.id)
builder.toHashCode()
}
static PartnerMap get(long partnerId, long customerId) {
find 'from PartnerMap where partner.id=:partnerId and customer.id=:customerId',
[partnerId: partnerId, customerId: customerId]
}
static PartnerMap create(Partner partner, Customer customer, boolean flush = false) {
new PartnerMap(partner: partner, customer: customer).save(flush: flush, insert: true)
}
static boolean remove(Partner partner, Customer customer, boolean flush = false) {
PartnerMap instance = PartnerMap.findByPartnerAndCustomer(partner, customer)
instance ? instance.delete(flush: flush) : false
}
static void removeAll(Partner partner) {
executeUpdate 'DELETE FROM PartnerMap WHERE partner=:partner', [partner: partner]
}
static void removeAll(Customer customer) {
executeUpdate 'DELETE FROM PartnerMap WHERE customer=:customer', [customer: customer]
}
static mapping = {
id composite: ['customer', 'partner']
version false
table 'PartnerMap'
}
}
Since you're not using hasMany, you lose the addToXXX dynamic method, but you can call PartnerMap.create() to relate two instances. You also lose the collection and back-ref in the domain classes, but I added utility methods for those.

Related

Grails taglib that returns a list

I'm trying to create (my first ever) taglib in grails and I sort of got it down except that I'm trying to return a list/array of strings that has to be displayed in a table but all I'm getting is the first value of the list. Here's what I got:
Taglib:
def roles = { attrs, body ->
def user = User.get(attrs.user)
if(user) {
def roles = Role.findAllById(UserRole.findByUser(user).roleId)
def realRoles = []
roles.each { role ->
realRoles.add(role.authority.substring(5))
}
out << realRoles
out << body()
}
}
Html:
<table class="table table-bordered table-hover" style="background-color: #FFFFFF;">
<thead>
<tr style="background-color: #007FB2;color: #FFFFFF;">
<th>ID</th>
<th>Name</th>
<th>Surname</th>
<th>Email</th>
<th>Roles</th>
<th>Delete</th>
</tr>
</thead>
<g:each in="${users}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.firstName}</td>
<td>${user.surname}</td>
<td>${user.username}</td>
<td><g:roles user="${user.id}" /></td>
<td><button type="button" class="gen-btn btn-red" style="width: 100%;" onclick="confDelete(${user.id}, '${user.firstName}');">Delete</button></td>
</tr>
</g:each>
</table>
What's supposed to happen is when the taglib gets called it's supposed to go get all the roles associated with the user id then take each role's authority property and cut the preceding "ROLE_" off of it. So ROLE_ADMIN just becomes ADMIN and ROLE_USER just becomes USER, etc. What I'd ideally like is a list that I can loop through in the gsp but I realise that's alot to ask so if I can just get help getting a comma separated list roles back from my taglib I'd really appreciated it.
To make it more clear, what I'm currently getting back is literally [ROLE_ADMIN] regardless of whether the user has more roles than that. What I want is a complete list of roles, e.g. ROLE_ADMIN, ROLE_MANAGER, ROLE_WHATEVER.
Thanks in advance
EDIT Here are my User, Role and UserRole domain classes. I am using Spring security to generate these classes.
User:
package fake.package.name
class User {
transient springSecurityService
String username
String password
String firstName
String surname
Date dateCreated
boolean enabled
boolean accountExpired
boolean accountLocked
boolean passwordExpired
static constraints = {
username blank: false, unique: true
password blank: false
firstName blank: false
surname blank: false
}
static mapping = {
password column: '`password`'
}
Set<Role> getAuthorities() {
UserRole.findAllByUser(this).collect { it.role } as Set
}
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
password = springSecurityService.encodePassword(password)
}
String toString(){
if((firstName)&&(surname))
firstName+ " " +surname
else
username
}
}
Role:
package fake.package.name
class Role {
String authority
static mapping = {
cache true
}
static constraints = {
authority blank: false, unique: true
}
}
UserRole:
package fake.package.name
import org.apache.commons.lang.builder.HashCodeBuilder
class UserRole implements Serializable {
User user
Role role
boolean equals(other) {
if (!(other instanceof UserRole)) {
return false
}
other.user?.id == user?.id &&
other.role?.id == role?.id
}
int hashCode() {
def builder = new HashCodeBuilder()
if (user) builder.append(user.id)
if (role) builder.append(role.id)
builder.toHashCode()
}
static UserRole get(long userId, long roleId) {
find 'from UserRole where user.id=:userId and role.id=:roleId',
[userId: userId, roleId: roleId]
}
static UserRole create(User user, Role role, boolean flush = false) {
new UserRole(user: user, role: role).save(flush: flush, insert: true)
}
static boolean remove(User user, Role role, boolean flush = false) {
UserRole instance = UserRole.findByUserAndRole(user, role)
if (!instance) {
return false
}
instance.delete(flush: flush)
true
}
static void removeAll(User user) {
executeUpdate 'DELETE FROM UserRole WHERE user=:user', [user: user]
}
static void removeAll(Role role) {
executeUpdate 'DELETE FROM UserRole WHERE role=:role', [role: role]
}
static mapping = {
id composite: ['role', 'user']
version false
}
}
Do note that because this a work project and not a personal one I'm not allowed to edit the code in here. Also, I changed the package names to fake.package.name, that's not the actual names of the packages.
I don't know, where your User<->Role Association comes from (Spring Security Framework?) or if it's coded by yourself. And you gave no examples of your domain-classes.
Let me explain another solution a bit:
If you design your domain-classes somehow like the following:
package xyz
class User {
String name
static hasMany = [authorities: Role]
static constraints = {
}
}
---------------
package xyz
class Role {
static belongsTo = User
String name
static hasMany = [user: User]
static constraints = {
}
}
a third table 'user_roles' will be created for saving the many-to-many relation/association
With this configuration, you could shorten your taglib to:
def roles = { attrs, body ->
def user = User.get(attrs.user)
if(user) {
def realRoles = []
user.authorities.each { r ->
realRoles.add(r.authority.substring(5))
}
out << realRoles.join(', ')
out << body()
}
}
Thanks to susi I managed to figure this thing out. Here: def roles = Role.findAllById(UserRole.findByUser(user).roleId) I only returned the first UserRole so I couldn't possibly bring back more than one Role. This is what my taglib method looks like now. I realize it might not be the most efficient and if someone would like to improve on it feel free.
def roles = { attrs, body ->
def user = User.get(attrs.user)
if(user) {
def userRoles = UserRole.findAllByUser(user)
def roles = []
def realRoles = []
userRoles.each { ur ->
roles.add(Role.findById(ur.roleId))
}
roles.each { r ->
realRoles.add(r.authority.substring(5))
}
realRoles = realRoles.join(", ")
println(realRoles)
out << realRoles
out << body()
}
}
Starting after the line if(user) {:
I find all the userRoles associated with the user Id
Then I loop through the list of userRoles and add each ur to a roles list
Then I loop through the list of Roles and add the formatted role's authority to the realRoles list
Lastly I use realRoles = realRoles.join(", ") to remove the square brackets around eachrealRoles` item
the root cause is the line
def roles = Role.findAllById(UserRole.findByUser(user).roleId)
because the UserRole.findByUser(user) just returnes 1 UserRole, roles is just 1 Role object inside a List, instead of all the User's Role objects.
change it to
def roles = Role.findAllByIdInList(UserRole.findAllByUser(user)*.roleId)
as you seem to be relatively new to Grails, a quick explanation: you can use dynamic finders (find*) in 2 ways: findBy* and findAllBy*. as the naming of the methods suggest, findBy* returns 1 object or null. findAllBy* returns a collection of objects.
Additionally, you can combine the property-names with operations (in our case "InList") to specify the type of search operation.
In this example we first retrieve a list of UserRole objects, transform this list into a list of role ids (using the spread operator "*.") and with these ids retrieve all Role objects having one of these IDs.
as the UserRole class contains a Role property directly, of course we can directly write
def roles = UserRole.findAllByUser(user)*.role
keep in mind, that this list could possibly contain null if the property role on UserRole class was nullable (it's not the case here, but if any reader has a similar problem but with a nullable association).
summarized, what you are doing in the taglib at the moment, could be written as:
out << UserRole.findAllByUser(user)*.role.collect({ it.substring(5) }).join(', ')
and just a little comment to the previous answer from susi:
that code shows a direct many-to-many association between User and Role.
as Burt Beckwith wrote some time ago https://mrpaulwoods.wordpress.com/2011/02/07/implementing-burt-beckwiths-gorm-performance-no-collections/
this can have impact on performance as every new User that gets added to a Role requires hibernate to load all existing User-Role-Connections, which could cause trouble when you have lots of users.
That's why the scaffolded domain classes of spring-security-core (since 2.x version iirc) now explicitely define the UserRole-class.

Grails: Legacy DB - Record with composite Id won't update

I'm having problems trying to update a record on a Grails 2.3.7 Project
I don't want to modify the fields that are part of the composite Id, I just want to update the other fields.
Just one class, few properties, but every time I try to update, it throws me the "not unique error" when this lines runs:
personInstance.validate()
if (personInstance.hasErrors()) {
respond personInstance.errors, view:'create'
return
}
My class looks like this:
class Person implements Serializable {
static constraints = {
name(unique: lastName)
}
static mapping = {
id generator: 'assigned'
id composite: ["name", "lastName"]
}
//Override equals and hashcode methods
boolean equals(other) {
if (!(other instanceof Person)) {
return false
}
other.name == name && other.lastName == lastName
}
int hashCode() {
def builder = new HashCodeBuilder()
builder.append name
builder.append lastName
builder.toHashCode()
}
String name
String lastName
String description
}
And the controller action:
def update() {
def personInstance = Person.get(new Person(name:params.name, lastName:params.lastName))
personInstance.properties = params
personInstance.validate()
if (personInstance.hasErrors()) {
respond personInstance.errors, view:'create'
return
}
personInstance.save flush:true
request.withFormat {/*etc...*/}
}
When I use validate(), it throws me a Grails unique key error, when I avoid validation its a BD not unique PK error.
Is like Grails didn't know if I want to do an insert or an update when I personInstance.validate().
Is there a way to manage this in a correct way that I'm not seeing?
Or am I forced to avoid validation?
am I doing something wrong?
Thanks in advance.
I believe the GORM mapping DSL expects just one id definition. Try combining your two id lines into just this one:
id generator: 'assigned', composite: ["name", "lastName"]
Also, in addition to implementing Serializable, your domain class should override equals and hashCode, as described under "Composite Primary Keys" here: http://grails.org/doc/latest/guide/single.html#identity

Grails inheritance and conflicting one-to-many and many-to-many relationship

I'm learning grails by trying to create a simple twitter copy. I'm currently trying to incorporate followers and groups. I originally came up with a very basic database structure, and I've had no luck in implementing it. The design for relationships is as follows:
Person:
has many: Groups, Tweets, (Person as followers through User2Person)
Group:
has many: (Person as followers through User2Person)
belongs to: Person as owner
User2Person:
belongs to: (Person or Group)
belongs to: Person
Basically, I want Person and Group to be an instance of User, and then create a table that maps User to Person. This way, only one table is created/used for the relationship between Group2Person and Person2Person.
More information: A Group is created by a Person and so it should have an "owner" (person_id). It also has many followers (i.e. members). Group cannot follow other groups, but a Person can follow either another Person or a Group.
Below is how I implemented this in grails:
User
abstract class User {
static hasMany = [followers: Person]
static mappedBy = [followers: "followed"]
String name
Date dateCreated
Date lastUpdated
static constraints = {
name shared: "mustFill", size: 3..20
}
}
Person
class Person extends User {
static belongsTo = [followed: User]
static hasMany = [tweets: Tweet, groups: Group]
static mappedBy = [groups: "owner"]
String username
String email
static constraints = {
username shared: "mustFill", unique: true, size: 4..15
email shared: "mustFill", email: true
}
static mapping = {
tweets sort: 'dateCreated', order: 'desc'
}
}
Group
class Group extends User {
Person owner
String description
def getTweets() {
return followers.tweets.flatten()
}
static transients = {
tweets
}
}
Tweet (Just in case?)
class Tweet {
static belongsTo = [author: Person]
String text
Date dateCreated
static constraints = {
text shared: "mustFill", maxSize: 140
}
}
When I run the cmd grails schema-export, I get the following error: "| Error Error loading plugin manager: Domain classes [class tweeter.Group] and [class tweeter.Person] cannot own each other in a many-to-many relationship. Both contain belongsTo definitions that reference each other. (Use --stacktrace to see the full trace)"
I was able to get the database to create almost the correct schema. Unfortunately, the join table's primary key for User2Person (a.k.a. followers) used (user_id, person_id). That meant that I could not have two records such as: (1, 2) and (2, 1) (e.g. two users are following each other). Below is the updated classes (commit):
User
class User {
static belongsTo = Person
static hasMany = [followers: Person]
String name
Date dateCreated
Date lastUpdated
static constraints = {
name shared: "mustFill", size: 3..20
}
}
Person
class Person extends User {
static hasMany = [tweets: Tweet, groups: Group, follows: User]
static mappedBy = [tweets: "author", groups: "owner"]
String username
String email
static constraints = {
username shared: "mustFill", unique: true, size: 4..15
email shared: "mustFill", email: true
}
static mapping = {
tweets sort: 'dateCreated', order: 'desc'
}
}
The follower table in the schema looked like:
create table user_follows (
user_id bigint,
follows__id bigint,
primary_key(user_id, follows__id)
)
I scoured the web for information about changing the primary key for a join table. The best I could find was about using code like:
static mappedBy = { followers joinTable: [name:"someName", ...] }
Unfortunately, I had a hard time finding good documentation on the joinTable mapping, and most sources seemed to indicate that it was not possible to change the primary key of join tables easily. I then decided to use a separate domain class to define the join table following this guide: Many-to-Many Mapping without Hibernate XML. Below is the final updated code (commit):
User
class User {
static belongsTo = Person
static hasMany = [people: UserFollower]
static mappedBy = [people: "followed"]
String name
Date dateCreated
Date lastUpdated
static constraints = {
name shared: "mustFill", size: 3..20
}
static transients = {
followers
}
def getFollowers() {
return people.collect { it.follower }
}
void addToFollowers(Person person) {
UserFollower.link(this, person)
}
void removeFromFollowers(Person person) {
UserFollower.unlink(this, person)
}
}
Person
class Person extends User {
static hasMany = [tweets: Tweet, groups: Group, users: UserFollower]
static mappedBy = [tweets: "author", groups: "owner", users:"follower"]
String username
String email
static constraints = {
username shared: "mustFill", unique: true, size: 4..15
email shared: "mustFill", email: true
}
static mapping = {
tweets sort: 'dateCreated', order: 'desc'
}
static transients = {
follows
}
def getFollows() {
return users.collect { it.followed }
}
void addToFollows(User user) {
UserFollower.link(user, this)
}
void removeFromFollows(User user) {
UserFollower.unlink(user, this)
}
}
UserFollower
class UserFollower {
User followed
Person follower
static constraints = {
followed nullable: false
follower nullable: false
}
static void link(User user, Person person) {
UserFollower f = UserFollower.findByFollowedAndFollower(user, person)
if(!f) {
f = new UserFollower()
user.addToPeople(f)
person.addToUsers(f)
f.save()
}
}
static void unlink(User user, Person person) {
UserFollower f = UserFollower.findByFollowedAndFollower(user, person)
if(f) {
f = new UserFollower()
user.removeFromPeople(f)
person.removeFromUsers(f)
f.delete()
}
}
}

inheritance in Grails domain model

My Grails app's domain model has the following requirements:
a user belong to zero or one organisations
an organisation is either a charity or a company
charities and companies have some some common fields and also some (non-nullable) fields that are unique to each organisation type
I put the common organisation fields into an abstract Organisation class which Charity and Company both extend. I can't store this hierarchy in a single table because there are non-nullable fields that are specific to each organisation type. The relevant parts of the domain model are shown below:
class User {
String name
static belongsTo = [organization: Organization]
static constraints = {
organization nullable: true
}
}
abstract class Organization {
String name
static hasMany = [users: User]
static mapping = {
tablePerHierarchy false
}
}
class Charity extends Organization {
// charity-specific fields go here
}
class Company extends Organization {
// company-specific fields go here
}
When I look at the MySQL schema generated from this model, the inheritance relationship between organisation-company and organisation-charity seems to have been completely ignored. Although there is an organisation table with a name column, it has no primary-foreign key relationship with either company or charity
I see the same result as IanRoberts for both MySQL and H2. In other words: no join table generated, but the expected organization_id FK in the users table.
With "Table per subclass" mapping (tablePerHierarchy false), you end up with an implied one-to-one relationship in the database. Primary Keys for Charity and Company will have the same value as the PK for the parent Organization. The schema generated by GORM/Hibernate3 doesn't appear to enforce this with referential integrity constraints. It's pure Hibernate magic. A bit more detail here
Solved!
Add the class below to src/java (this class cannot be written in Groovy)
package org.example;
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration;
import org.hibernate.MappingException;
import org.hibernate.mapping.JoinedSubclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass;
import java.util.Iterator;
public class TablePerSubclassConfiguration extends GrailsAnnotationConfiguration {
private static final long serialVersionUID = 1;
private boolean alreadyProcessed = false;
#Override
protected void secondPassCompile() throws MappingException {
super.secondPassCompile();
if (alreadyProcessed) {
return;
}
for (PersistentClass persistentClass : classes.values()) {
if (persistentClass instanceof RootClass) {
RootClass rootClass = (RootClass) persistentClass;
if (rootClass.hasSubclasses()) {
Iterator subclasses = rootClass.getSubclassIterator();
while (subclasses.hasNext()) {
Object subclass = subclasses.next();
// This test ensures that foreign keys will only be created for subclasses that are
// mapped using "table per subclass"
if (subclass instanceof JoinedSubclass) {
JoinedSubclass joinedSubclass = (JoinedSubclass) subclass;
joinedSubclass.createForeignKey();
}
}
}
}
}
alreadyProcessed = true;
}
}
Then in DataSource.groovy set this as the configuration class
dataSource {
configClass = 'org.example.TablePerSubclassConfiguration'
pooled = true
driverClassName = "org.h2.Driver"
username = "sa"
password = ""
dbCreate = "update"
url = "jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
}
I've submitted a pull request to Grails that fixes this. The fix was be included in Grails 2.3.9.
ORM is not RDBS.
tablePerHierarchy false
so You Have three tables: Organization, Charity, Company. User belongs to only Organization (not Charity or Company). How are you going to get value of specific fields?
There is USER. We know ORGANIZATION, but we don't know Charity or Company. I think you underst...
I can suggest you three solutions:
1. tablePerHierarchy true (But you need to have nullable charity\Company -specific fields )
2.
class User {
static belongsTo = [charity: Charity, company: Company]
}
class Charity {
String name
static hasMany = [users: User]
// charity-specific fields go here
}
class Company {
String name
static hasMany = [users: User]
// company-specific fields go here
}
3.
class User {
static belongsTo = [organization: Organization]
}
class Organization {
String name
Charity charity //nullable
Company company //nullable
static hasMany = [users: User]
}
class Charity {
static belongsTo = [organization: Organization]
// charity-specific fields go here
}
class Company {
static belongsTo = [organization: Organization]
// company-specific fields go here
}

Grails 2.1 Join Query

I m new user in Grails. I don't know how to write projection query. Here is my code. please anyone help me for grails projection query.
From Join table I want to find username which is consist in user table
Given the following example domains:
class User{
transient springSecurityService
String username
String password
boolean enabled
boolean accountExpired
boolean accountLocked
boolean passwordExpired
static mapping = {
table 't04t001'
id column: 'f_account_id'
username column: 'f_username', length: 10
password column: 'f_password', length: 100
enabled column: 'f_account_active'
accountExpired column: 'f_account_expired'
accountLocked column: 'f_account_locked'
passwordExpired column: 'f_password_expired'
version column: 'f_revision'
}
}
class Role{
String role
static mapping = {
table 't04t003'
id column : 'f_role_id'
role column : 'f_role'
version column : 'f_revision'
cache true
}
}
class UserRole implements Serializable {
User user
Role role
static mapping = {
table 't04j002'
id composite : ['role', 'user']
role column :'k_role_id'
user column :'k_user_id'
version false
}
}
I can't figure out how to build the criteria to find all the user. I tried the following:
def criteria = UserRole.createCriteria()
def list = criteria.list {
projections {
user{
ilike('username', 'omar')
}
}
}
In console mode i have seen this query with message
Hibernate:
select
this_.k_role_id as k1_3406_0_,
this_.k_user_id as k2_3406_0_
from
t04j002 this_
where
(
lower(user_alias1_.f_username) like ?
)
However, it says Unknown column 'user_alias1_.f_username' in 'where clause'.
But i cant figure out this(user_alias1_) alias
I am not exactly clear on what you want to retrieve from which table. So based on your criteria query, here is how to retrieve user from the UserRole table.
def criteria = UserRole.createCriteria()
def result = criteria.list {
projections {
property("user")
}
user {
eq("username", "omar") //strict match
}
}
return result;
What this does is it builds a list that has the user.username as omar. By default the result will be the UserRole object, but to get user object instead we used projections.
Update:
Your Domain class seems a bit out of grails conventions. Why not user something like this?
//User.groovy
class User {
String username
// ..
static constraints = {
username blank: false, nullable: false, maxSize: 10
}
}
//UserRole.groovy
// Since you are using composite keys, we have to implement serializable. But is there a need for composite key in your situtation?
class UserRole implements Serializable {
User user
// ..
}
In you service class
// UserRoleService.groovy
def getUser(String username) {
def criteria = UserRole.createCriteria()
def result = criteria.get {
projections {
property("user")
}
user {
ilike("username", username)
}
}
return result;
}
And to call the service method from controller,
// ExampleController.groovy
class ExampleController {
def userRoleService
def index() {}
def someThing(String username) {
def user = userRoleService.getUser(username)
println user?.username
// ..
}
}

Resources