Grails one-many mapping stalestaleexception on composite key

grails, grails-orm, hibernate

Solution

Your Product domain class must implements `Serializable` and override methods `hashCode()` and `equals()`, this thing must be done in situations where you use composite key.

Your Product domain class must be like this

class Product implements Serializable {
        Long id
        ProductType productType
        ...

        static mapping = {
             table 'T_PRODUCT'
             id composite:['member', 'productType']
             member column: 'MEMBER_ID'
             productType column: 'PRODUCT_TYPE'
        }

        boolean equals(other) {
            if (!(other instanceof Product )) {
                return false
            }
            other.member== member && other.productType== productType
        }

        int hashCode() {
            def builder = new HashCodeBuilder()
            builder.append member
            builder.append productType
            builder.toHashCode()
        }

    }

I think this way everything will be OK.

In case of problems write.

Problem

I use grails 2.0.0. I have three objects Member, Product and ProductType. Member has many Products and is a one-to-many relation. Product points to ProductType (reference table) and is a many-to-one relation. My question is about deleting Products. It works in one scenario and not in another. Read on. Rough outline of the mappings below: Member.groovy: ``` class Member { Long id .... SortedSet products static hasMany = [products:Product] static mapping = { table 'T_MEMBER' id column:'MEMBER_ID'... products cascade: "all-delete-orphan" } } ``` Product.groovy: ``` class Product { Long id ProductType productType ... static belongsTo = [member:Member] static mapping = { table 'T_PRODUCT' id column:'PRODUCT_ID' member column: 'MEMBER_ID' productType column: 'PRODUCT_TYPE' ... } } ``` ProductType.groovy: ``` class ProductType { Long id .. static mapping = { table 'T_PRODUCT_TYPE' id column:'PRODUCT_TYPE', generator:'assigned' ... } } ``` I got the client service code an outline of which is ... ``` if((newMember.products) && (newMember.products.size() >0)) { def addList = newMember.products - existingMember.products def removeList = existingMember.products- newMember.products removeList.each { product -> existingMember.removeFromProducts(product) } addList.each {product -> existingMember.addToProducts(product) } } ``` So far so good. This is working perfectly. However when I introduce composite primary key for the T_PRODUCT table by doing the following: ``` static mapping = { table 'T_PRODUCT' //id column:'PRODUCT_ID' id composite:['member', 'productType'] member column: 'MEMBER_ID' productType column: 'PRODUCT_TYPE' ... } ``` I get this: org.hibernate.StaleStateException: Batch upda Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1 org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1 at ProductService.cleanUpGorm(ProductService.groovy:442) at ProductService.maintainProduct(ProductService.groovy:213) at ClientService$_maintainMembers_closure5.doCall(ClientService.groovy:158) at ClientService.maintainMembers(ClientService.groovy:152) at ClientService.processMembers(ClientService.groovy:394) Any idea where I might be going wrong?

Original source