json and nested objects

401 views
Skip to first unread message

claudio

unread,
May 31, 2010, 8:56:33 AM5/31/10
to Ebean ORM
Hi, I'm trying to use json to communicate with a GWT application.
In my simple test there is the Order class who owns a list of
OrderItem.

I serialize the Order to json with JsonContext.toJsonString(order)
then send the string to the client.
The client can update the order and the orderItems,
Then I deserialize the modified order and call Ebean.update(order):
the order is updated, but I get an error from the DB because Ebean
tries to reinsert the orderItems instead of updating them, even if the
id field is setted.

The question is: after the Order object is modified on the client
(possibly adding/updating/deleting orderItems) and deserialized from
json on the server, is there a simple way using Ebean to synchronize
the state of the order with the DB (inserting/updating/deleting
orderItems)?

P. S. a little problem dropping tables with Oracle 9i: Ebean, when
creates entities, tries "drop table order cascade constraints PURGE",
but the purge option is available only from version 10.

Thanks

Claudio

edge

unread,
May 31, 2010, 9:07:50 AM5/31/10
to Ebean ORM
Hi,

are you using a version property? Can you publish your Order and
OrderItem classes so as we can see what you are doing?

Eddie

claudio

unread,
May 31, 2010, 7:07:50 PM5/31/10
to Ebean ORM
I tryed with and without the version property, but the result is the
same: here is my code

@Entity @Table(name="TEST_ORDER")
public class Order {
@Id Integer id;
String description;
@OneToMany(cascade=CascadeType.ALL) @PrivateOwned List<OrderItem>
items;
@Version int updateCount;

getters and setters ...
}

@Entity @Table(name="TEST_ORDER_ITEM")
public class OrderItem {
@Id Integer id;
String itemDescription;
Integer quantity;
@Version int updateCount;

getters and setters ...
}

In my test I use Ebean 2.6.1 and H2 database:

...

EbeanServerFactory.create(configuration);

// create the order
Order order = new Order();
order.setDescription("New order");

// create the orderItem and add to the order
OrderItem orderItem = new OrderItem();
orderItem.setItemDescription("New item");
orderItem.setQuantity(1000);
order.setItems(new BeanList<OrderItem>());
order.getItems().add(orderItem);

Ebean.save(order);

JsonContext jsonContext = Ebean.createJsonContext();
String json = jsonContext.toJsonString(order);
// json = {"id":1,"description":"New order","items":[{"id":
1,"itemDescription":"New item","quantity":1000,"updateCount":
1}],"updateCount":1}

// this is a test; in a real application the json string will be send
to the client and possibly changed, then resend to the server

// deserialize the order
Order deserializedOrder = jsonContext.toBean(Order.class, json);

// update the database
Ebean.update(deserializedOrder);

I get: javax.persistence.PersistenceException:
org.h2.jdbc.JdbcSQLException: Unique index or primary key violation:
PRIMARY_KEY_C4 ON PUBLIC.TEST_ORDER_ITEM(ID); SQL statement: insert
into TEST_ORDER_ITEM (id, order_id, item_description, quantity,
update_count) values (?,?,?,?,?)

So seems like Ebean.update() forces an update only on the Order bean,
but for the OrderItem tries to do an insert.

What I'm trying to do is:
- the client loads the Order from the server using json
- the client changes the Order and possibly adds/removes/updates
OrderItems
- the client sends a json representation of the changed Order to the
server
- the server synchronize the DB using the deserialized Order
(applies updates to Order and OrderItems, inserts the OrderItems not
yet persisted and deletes OrderItems in the DB but not in the
deserialized Order)

what is the easiest way to do this using Ebean?

Thanks

Claudio

Rob Bygrave

unread,
Jun 1, 2010, 2:58:41 AM6/1/10
to eb...@googlegroups.com
I can have a look. Are you using Enhancement or subclassing?

> and deletes OrderItems in the DB but not in the deserialized Order

I know it does not do this for you.

Rob Bygrave

unread,
Jun 1, 2010, 7:10:00 AM6/1/10
to eb...@googlegroups.com
I reproduced the issue. Looks like you are using enhanced beans.

Right now to handle the nested json update you need to:

- turn off cascade persist (programmatically)
- update the parent bean
- loop through the nested details and either save or update (determine if it is insert or update) based on a version property or id value
- delete any detail beans that you didn't save or update


So the relevant part of my test case code to do this was:

 
// ... generate the jsonString with the changes ...



        Order updOrder = jsonContext.toBean(Order.class, jsonString);
       
        Transaction t = server.beginTransaction();
        try {
            // turn of the cascading persist for the moment
            t.setPersistCascade(false);
           
            // force an update on the parent bean
            t.log("-- stateless update of order ... ");
            server.update(updOrder);
       
            List<Integer> detailIds = new ArrayList<Integer>();
           
            // loop through the detail beans... and insert or update
            t.log("-- insert/update of order details ... ");
            List<OrderDetail> details = updOrder.getDetails();
            for (OrderDetail orderDetail : details) {
                detailIds.add(orderDetail.getId());
               
                if (orderDetail.getUpdtime() == null) {
                    // insert
                    server.save(orderDetail);
                } else {
                    // force update
                    server.update(orderDetail);
                }
            }

            // delete any orderDetails ... that where not in the list
            String del = "delete from OrderDetail where order.id=:orderId and id not in (:detailIds)";
            Update<OrderDetail> deleteOtherDetails = server.createUpdate(OrderDetail.class, del);
            deleteOtherDetails.setParameter("orderId", updOrder.getId());
            deleteOtherDetails.setParameter("detailIds", detailIds);
           
            t.log("-- deleting details that are not in list ");
            deleteOtherDetails.execute();
            t.commit();
           
        } finally {
            t.end();
        }




... and the transaction log looks like...

txn[1008], 10:57:14.745, -- stateless update of order ... 
txn[1008], 10:57:16.570, update o_order set status=?, order_date=?, ship_date=?, updtime=?, kcustomer_id=? where id=? and updtime=?
txn[1008], 10:57:27.038, Binding Update [o_order]  set[status=SHIPPED, orderDate=2010-06-01, shipDate=null, updtime=2010-06-01 10:57:27.025, kcustomer_id=5, ] where[id=5, updtime=2010-06-01 10:57:14.647, ]
txn[1008], 10:57:27.040, Updated [Order] [5]
txn[1008], 10:57:27.041, -- insert/update of order details ... 
txn[1008], 10:57:27.043, update o_order_detail set order_qty=?, ship_qty=?, unit_price=?, cretime=?, updtime=?, product_id=? where id=? and updtime=?
txn[1008], 10:57:27.044, Binding Update [o_order_detail]  set[orderQty=5, shipQty=300, unitPrice=56.98, cretime=null, updtime=2010-06-01 10:57:27.043, product_id=1, ] where[id=7, updtime=2010-06-01 10:57:14.647, ]
txn[1008], 10:57:27.044, Updated [OrderDetail] [7]
txn[1008], 10:57:27.045, update o_order_detail set order_qty=?, ship_qty=?, unit_price=?, cretime=?, updtime=?, product_id=? where id=? and updtime=?
txn[1008], 10:57:27.046, Binding Update [o_order_detail]  set[orderQty=3, shipQty=null, unitPrice=1.1, cretime=null, updtime=2010-06-01 10:57:27.045, product_id=2, ] where[id=8, updtime=2010-06-01 10:57:14.647, ]
txn[1008], 10:57:27.054, Updated [OrderDetail] [8]
txn[1008], 10:57:27.057, insert into o_order_detail (id, order_qty, ship_qty, unit_price, cretime, updtime, order_id, product_id) values (?,?,?,?,?,?,?,?)
txn[1008], 10:57:27.059, Binding Insert [o_order_detail]  set[id=10, orderQty=899, shipQty=null, unitPrice=12.12, cretime=null, updtime=2010-06-01 10:57:27.058, order_id=null, product_id=1, ]
txn[1008], 10:57:27.061, Inserted [OrderDetail] [10]
txn[1008], 10:57:27.077, -- deleting details that are not in list 
txn[1008], 10:57:27.107, delete from o_order_detail where order_id=? and id not in (?,?,?)
txn[1008], 10:57:27.124, Delete table[o_order_detail] rows[0] bind[5, 7, 8, null, ]



So that said... I'll have a look at this to see if we should get Ebean to do this for us when we cascade on the 'stateless bean update'.


Cheers, Rob.

claudio

unread,
Jun 1, 2010, 9:17:57 AM6/1/10
to Ebean ORM
Hi Rob, thanks for the ultra fast answer. As You guessed I'm using
enhanced beans.
I'll try your suggestion for the stateless bean update.

Claudio
Reply all
Reply to author
Forward
0 new messages