Grails实现多对多的关联并防止级联 [英] Grails many-to-many associations and preventing cascade

查看:103
本文介绍了Grails实现多对多的关联并防止级联的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我们在客户与角色之间建立了多对多关系,设置为:

 客户{
static hasMany = [roles:Role]
}

角色{
static hasMany = [customer:Customer]
static belongsTo = Customer
}

Role对象只有一个名称和一组权限。我们不希望级联来自Customer - > Role的保存,因为Role只能直接修改。



我补充说:

 静态映射= {
角色级联:'none'
}

但是,无论何时,我创建一个客户,角色表也会更新。没有任何变化,除了版本号增加。



我是否缺少其他需要设置的东西......是否存在多对多的错误关系和级联在Grails中设置...或者有其他一些方法可以防止每次更新角色?

解决方案

我通常将连接表映射为域类以避免此问题及其他问题(集合加载性能,乐观锁定错误等)。这涉及到删除 hasMany belongsTo 并创建一个 CustomerRole 域类:

  import org.apache.commons.lang.builder.HashCodeBuilder 
$ b $ class CustomerRole实现Serializable {

客户客户
角色角色

boolean equals(other){
if(!(other Customerofole)){
return false
}

other.customer?.id ==客户?.id&&&放大器;
other.role?.id == role?.id
}

int hashCode(){
def builder = new HashCodeBuilder()
if (customer)builder.append(customer.id)
if(role)builder.append(role.id)
builder.toHashCode()
}

static CustomerRole get(long customerId,long roleId){
从CustomerRole中查找'其中customer.id =:customerId和role.id =:roleId',
[customerId:customerId,roleId:roleId] $ b $ (客户:客户,角色:角色).save(flush:flush,insert:b)创建客户角色(角色角色,布尔值flush = false){



true)
}

static boolean remove(Customer customer,Role role,boolean flush = false){
CustomerRole instance = CustomerRole.findByCustomerAndRole(customer,role)
实例? instance.delete(flush:flush):false
}

static void removeAll(Customer customer){
executeUpdate'DELETE FROM CustomerRole WHERE customer =:customer',[customer:客户]
}

static void removeAll(Role role){
executeUpdate'DELETE FROM CustomerRole WHERE role =:role',[role:role]
}

静态映射= {
id复合:['customer','role']
版本false
表格'customer_roles'
}
}

映射块配置生成的DDL,因此它与您现在拥有的相同,所以您赢了不需要进行任何数据库更改。静态帮助器方法不是必需的,但可以方便地隐藏授予和撤销角色的过程。



您需要更改代码。由于没有 hasMany ,所以不能使用 customer.addToRoles(...)。相反,只需使用 create 方法创建一个新的CustomerRole实例,然后使用 remove 方法。



更新后的Role类将是

  class Role {
}

以及更新的客户类别为

  class Customer {
Set< Role> getRoles(){
CustomerRole.findAllByUser(this).collect {it.role} as Set
}
}

这有一个方便的方法 getRoles()它可以模拟角色 hasMany 为您创建的集合,因为您仍然需要一种简单的方式来访问客户授予的角色。


So, we have a many-to-many relationship between Customer and Role, set up as:

Customer {
  static hasMany = [roles: Role]
}

Role {
  static hasMany = [customer: Customer]
  static belongsTo = Customer
}

The Role object has only a name and a set of permissions. We don't want to cascade saves from Customer -> Role, as Role should only get modified directly.

I added:

static mapping = {
  roles cascade: 'none'
}

but, whenever, I create a customer, the role table gets updated as well. Nothing changes, except that the version number is incremented.

Am I missing something else that needs to be set ... is there a bug with how many-to-many relationships and cascades are set in Grails ... or is there some other way I can prevent roles from being updated every time?

解决方案

I usually map the join table as a domain class to avoid this issue and others (collection loading performance, optimistic locking errors, etc.) This involves removing the hasMany and belongsTo and creating a CustomerRole domain class:

import org.apache.commons.lang.builder.HashCodeBuilder

class CustomerRole implements Serializable {

   Customer customer
   Role role

   boolean equals(other) {
      if (!(other instanceof CustomerRole)) {
         return false
      }

      other.customer?.id == customer?.id &&
         other.role?.id == role?.id
   }

   int hashCode() {
      def builder = new HashCodeBuilder()
      if (customer) builder.append(customer.id)
      if (role) builder.append(role.id)
      builder.toHashCode()
   }

   static CustomerRole get(long customerId, long roleId) {
      find 'from CustomerRole where customer.id=:customerId and role.id=:roleId',
         [customerId: customerId, roleId: roleId]
   }

   static CustomerRole create(Customer customer, Role role, boolean flush = false) {
      new CustomerRole(customer: customer, role: role).save(flush: flush, insert: true)
   }

   static boolean remove(Customer customer, Role role, boolean flush = false) {
      CustomerRole instance = CustomerRole.findByCustomerAndRole(customer, role)
      instance ? instance.delete(flush: flush) : false
   }

   static void removeAll(Customer customer) {
      executeUpdate 'DELETE FROM CustomerRole WHERE customer=:customer', [customer: customer]
   }

   static void removeAll(Role role) {
      executeUpdate 'DELETE FROM CustomerRole WHERE role=:role', [role: role]
   }

   static mapping = {
      id composite: ['customer', 'role']
      version false
      table 'customer_roles'
   }
}

The mapping block configures the generated DDL so it's the same as what you have now, so you won't need to make any database changes. The static helper methods aren't required but are convenient to hide the process of granting and revoking roles.

You will need to change you code. Since there's no hasMany, you can't use customer.addToRoles(...). Instead to grant a role just create a new CustomerRole instance using the create method and to revoke, delete the instance using the remove method.

The updated Role class would be

class Role {
}

and the updated Customer class would be

class Customer {
   Set<Role> getRoles() {
      CustomerRole.findAllByUser(this).collect { it.role } as Set
   }
}

This has a convenience method getRoles() which mimics the roles collection that's created for you by the hasMany since you will still need an easy way to access a customer's granted roles.

这篇关于Grails实现多对多的关联并防止级联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆