Grails many-to-many associations and preventing cascade

六月ゝ 毕业季﹏ 提交于 2019-12-07 14:51:46

问题


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?


回答1:


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.



来源:https://stackoverflow.com/questions/4088810/grails-many-to-many-associations-and-preventing-cascade

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!