0

I am having difficulty removing a child from an association. I'm using Grails 4.0.3. The two classes in question are the following:

ShoppingCart:

class ShoppingCart implements Serializable {

    private static final long serialVersionUID = 1

    long id //don't think this is needed since I have a composite id below
    User user
    Store store

    SortedSet<ShoppingCartItem> items
    static hasMany = [items: ShoppingCartItem]

    static constraints = {
        user nullable: false
        store nullable: false
    }

    static mapping = {
        id composite: ['user', 'store']
        items cascade: "all-delete-orphan"
    }
}

And ShoppingCartItem:

class ShoppingCartItem implements Comparable<ShoppingCartItem>, Serializable {

    private static final long serialVersionUID = 1

    long id //don't think this is needed since I have a composite id below

    static belongsTo = [shoppingCart: ShoppingCart]

    Product product
    int quantity = 0
    BigDecimal purchasePrice

    Date lastItemAdded

    static constraints = {
        product nullable: false
        purchasePrice nullable: false
        lastItemAdded nullable: false
    }

    static mapping = {
        id composite: ['shoppingCart', 'product']
    }

    int compareTo(ShoppingCartItem otherItem) {
        lastItemAdded.compareTo(otherItem.lastItemAdded)
    }
}

Because I have a bidirectional relationship (using the belongsTo in ShoppingCartItem) and the ShoppingCart has a composite key, my shopping_cart_item table looks like the following:

'shopping_cart_user_id', 'bigint(20)', 'NO', 'PRI', '', ''
'shopping_cart_store_id', 'bigint(20)', 'NO', 'PRI', '', ''
'product_id', 'bigint(20)', 'NO', 'PRI', '', ''
'version', 'bigint(20)', 'NO', '', '', ''
'date_created', 'datetime', 'NO', '', '', ''
'last_updated', 'datetime', 'NO', '', '', ''
'quantity', 'int(11)', 'NO', '', '', ''
'purchase_price', 'decimal(19,2)', 'NO', '', '', ''
'last_item_added', 'datetime', 'NO', '', '', ''

I am having a heck of time trying to delete a shopping cart item. I have read through https://spring.io/blog/2010/07/02/gorm-gotchas-part-2/ several times and it makes sense to me, but I have not been successful.

In a grails service method I'm doing the following:

def updateCountInCartForProduct(ShoppingCart cart, Serializable productId, int newQuantity) {
        def product = productService.get(productId)
        def cartItem = cart.items.find { it.product == product }
        if ( cartItem ) {
            if ( newQuantity == 0 ) {
                cart.removeFromItems(cartItem) // <-- this line throws the error
              } else {
                cartItem.quantity = newQuantity
                cartItem.save()
            }
        }
    }

After executing this method I get the following error:

12/22/2020 07:48:00 - ERROR [org.hibernate.internal.ExceptionMapperStandardImpl]: HHH000346: Error during managed flush [Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1]
12/22/2020 07:48:01 - ERROR [org.grails.web.errors.GrailsExceptionResolver]: StaleStateException occurred when processing request: [POST] /shop/updateCartQuantity - parameters:
productId: 7
newQuantity: 0
Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1. Stacktrace follows:
java.lang.reflect.InvocationTargetException: null
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)

After turning on SQL logging I am seeing something that seems strange to me:

12/22/2020 07:48:00 - DEBUG [org.hibernate.SQL]: delete from shopping_cart_item where shopping_cart_user_id=? and shopping_cart_store_id=? and product_id=? and version=?
12/22/2020 07:48:00 - TRACE [org.hibernate.type.descriptor.sql.BasicBinder]: binding parameter [1] as [BIGINT] - [null]
12/22/2020 07:48:00 - TRACE [org.hibernate.type.descriptor.sql.BasicBinder]: binding parameter [2] as [BIGINT] - [null]
12/22/2020 07:48:00 - TRACE [org.hibernate.type.descriptor.sql.BasicBinder]: binding parameter [3] as [BIGINT] - [7]
12/22/2020 07:48:00 - TRACE [org.hibernate.type.descriptor.sql.BasicBinder]: binding parameter [4] as [BIGINT] - [1]

Notice the binding parameters for shopping_cart_user_id and shopping_cart_store_id are null. I don't know if this is causing my problem, but it seems suspect. When I debug through the code, the cartItem being removed does have a reference to the parentShopping cart and none of the ids on any of the objects are null, so I don't know where those nulls are coming from.

Any ideas?

chravis
  • 83
  • 8

2 Answers2

0

Did you try updating Hibernate to the latest version yet? If so, and you still have the problem, please create an issue in the issue tracker(https://hibernate.atlassian.net) with a test case(https://github.com/hibernate/hibernate-test-case-templates/blob/master/orm/hibernate-orm-5/src/test/java/org/hibernate/bugs/JPAUnitTestCase.java) that reproduces the issue.

Christian Beikov
  • 15,141
  • 2
  • 32
  • 58
  • No I have not tried to update Hibernate. Honestly I'm not even sure how to do it within Grails. I'm currently on Grails 4.0.3. When I look at build.gradle I see dependencies on hibernate5:7.0.0 and hibernate-core:5.4.0.Final. – chravis Dec 26 '20 at 18:03
  • Then try updating to the latest 5.4 version – Christian Beikov Dec 27 '20 at 12:15
0

I just had tremendous problems deleting a 1:M relationship similar to this. I did all manner of removeFrom and delete that ended up as one big no-op.

The solution was to explicitly annotate the service method with @Transactional (from grails.gorm.transactions.Transactional). It magically made everything work as advertised, cascading and all.

The service class was annotated with @Service. I incorrectly assumed that it implied @Transactional since services are transactional by default, but it's not quite that simple. In addition, creating those structures (several 1:M relationships) worked fine without explicit @Transactional annotations on service methods. I.e. there were no save() problems, only delete().

sodastream
  • 129
  • 10