I have a question about creating an associative table, in grails, to reconcile a many-to-many relationship. The setup is this: 1. Domain A (Client Profile) can have many Domain B (Friends) 2. Each Domain B (Friends) can have many Domain A (Client Profile) 3. To Resolve this, I need to create an Associative table (or domain) that has FK's from each table. This domain can be named Domain C (client_friend)
Here is the code I have so far:
class DomainA{
String id
String firstName
String lastName
static hasMany = [domainB: DomainB]
static mapping = {
cache true
id generator: 'assigned'
columns {
firstName type:'text'
lastName type:'text'
alumConnections column: 'domaina_id', joinTable: 'a_b'
}
}
static constraints = {
firstName (nullable:true)
lastName (nullable:true)
}
}
DomainB Code:
class DomainB{
String id
String firstName
String lastName
static hasMany = [domainA:DomainA]
static belongsTo = DomainA
static mapping = {
cache true
id generator: 'assigned'
columns {
firstName type:'text'
lastName type:'text'
domainA column: 'domainb_id', joinTable: 'a_b'
}
}
static constraints = {
firstName (nullable:true)
lastName (nullable:true)
}
}
Domain A_B code:
class AB{
Date dateCreated
Date lastUpdate开发者_Go百科d
long version
}
When I run this code, it seems to work. The tables, using MySQL, are created, FK seem to be in place. When I enter data into the DomainB class, data is entered and both PK's from DomainA and DomainB are inserted into A_B. But, the problems comes when I try to delete values from A_B. I've tried something like this:
AB results =AB.findByAIdAndBId('jIi-hRi4cI','2BYvuA2X14')
but get an error: InvalidPropertyException: No property found for name [a_id] for class [class mgr.AB]
My question is this: first, have i set this up correctly? Second, if so, how then do i query the AB table who's PK is made up of a composite of DomainA and DomainB?
Thanks for any help.
jason
Your composite class isn't entirely correct. Look at this example and adjust yours accordingly. This is how I do all my composite domains:
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)
instance ? instance.delete(flush: flush) : false
}
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
}
}
Once you have that, there is no need for the belongsTo and hasMany associations. But to access the data from those domains, you can provide methods like the following:
class User {
// typical domain junk
Set<Role> getAuthorities() {
UserRole.findAllByUser(this).collect { it.role } as Set
}
}
Then you can do things like userInstance.authorites just as if the hasMany was there. Basically, you're doing what Grails would typical do for you. But this is actually a good thing. Collections in Grails can be costly if not done right. This is being addressed in 2.0 with the use of Bags.
精彩评论