Architecture
I am using Hibernate to provide persistance for a java application which will use Macromedia Flex as the client/gui.
Flex is essentially a technology which allows java developers to defined a Flash GUI in XML and map graphs of java objects to graphs of Flash objects; it does deep copies, so it will copy all associations aswell.
Essentially I have a 'fat' client architecture (aka Rich Client ;-) )
I provide a business delegate which under the covers uses Hibernate.
My graphs of objects which I pass between the delegate and the client are my Hibernate mapped objects,
they acts as my ValueObjects / DTOs / JavaBeans etc
The business delegate is stateless, in simple terms I open a new Hibernate Session at the beginning of each method and close it at the end of each method. Eventually I will get the Hibernate SessionFactory from jndi, but for now it is just got via a static factory method.
My problem
I am keen
not to have a seperate set of DTOs from Hibernate Persistant objects
I like the idea of my DTOs being my Persistent objects for arguments sake I will call these objects value objects (vo)
My 'Admin' client creates a new Product (something like)
Code:
Product starbucksMug = new Product(.......);
starbucksMug = businessDelegate.createProduct(starbucksMug);
I pass the vo back from the delegate method so that it has the id populated (ie it is no longer transient)
for the purpose of the discussion I will create some more products:
Code:
Product costaCoffeeMug = new Product(...);
costaCoffeeMug = businessDelegate.createProduct(costaCoffeeMug);
Product caramelSyrup = new Product(..);
caramelSyrup = businessDelegate.createProduct(caramelSyrup);
Product bagOfCoffeeBeans = new Product(..);
bagOfCoffeeBeans = businessDelegate.createProduct(...);
I have the notion of compliments and substitutes (ie Products which will be recommended to the user at checkout time)
, they are implemented as two Sets in the Product vo and are mapped as many-to-many to Product (at the moment they are not lazy)
so to find out the compliments for starbucksMug, I would call:
Code:
List complimentProducts = starbucksMug.getCompliments();
ofcourse a the moment there are no compliments, so I will add two (this part is important to the later problem)
Code:
starbucksMug.getCompliments().add(bagOfCoffeeBeans);
starbucksMug.getCompliments().add(caramelSyrup);
and to update this on the server:
Code:
starbucksMug = businessDelegate.updateProduct(starbucksMug);
now I delete the the bagOfCoffeeBeans:
Code:
businessDelegate.deleteProduct(bagOfCoffeeBeans);
now I update the vo which contains the vo I have just deleted
Code:
starbucksMug.setPrice(12.99);
starbucksMug = businessDelegate.update(starbucksMug);
under the covers of update I am doing:
Code:
public Product updateProduct(Product product)
throws Exception
{
Session session = null;
Transaction transaction = null;
try
{
session = super.gainSession(); // just gets the session from the factory
transaction = session.beginTransaction();
session.update(product);
transaction.commit();
return (Product)session.load(Product.class, product.getId());
}
catch(HibernateException he)
{
super.rollback(transaction); // rolls back the transaction if it is not null
throw he;
}
finally
{
super.release(session); // closes the session
}
}
I get the following exception:
Code:
net.sf.hibernate.HibernateException: Batch update row count wrong: 2
at net.sf.hibernate.impl.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:65)
at net.sf.hibernate.impl.BatcherImpl.executeBatch(BatcherImpl.java:118)
at net.sf.hibernate.impl.SessionImpl.executeAll(SessionImpl.java:2311)
at net.sf.hibernate.impl.SessionImpl.execute(SessionImpl.java:2262)
at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2187)
at net.sf.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:61)
at # businessDelegate #.updateProduct(#######)
at # junit test #.testAddCompliment(#######)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at junit.framework.TestCase.runTest(TestCase.java:154)
I am assuming this is because the product I passed in had two nested Products, one of which does not exist anymore
However, I was hoping that Hibernate would know that the child had been removed in a previous call when update is called on the parent
The problem is that I am obviously going to get in a situation where object graphs on the client get out of sync
with object graphs on the server, can anyone suggest any techniques to easily overcome this ?
I also noticed that the join table in the database that hibernate uses to implement the many to many mapping of
Product to Product (compliments) still contains the mapping of the deleted Product, even though the Product row itself had been deleted from its table
is there a way in hibernate mapping files to tell hibernate to remove the many to many mapping row when a
child is deleted ?
thanks for any help