Thanks for your reply but I would prefer to stay in a session-per-conversation pattern.
I finally get it to work by using the "merge" instead of a simple flush.
I reworked all the thing and have now a nice persistance layer (see the code below). In the model persister class, I instanciate this abstract class by providing the load and create methods.
I still have some problems in some occasions (the latest is the addition of an element, see below) and am still trying to understand why I do not get any warning or exception when a given DB operation fails (or at least has no effect in the DB)... May it be a HSQLDB problem?
Code:
package org.myproject.model.persister;
import org.apache.log4j.Logger;
import org.myproject.model.exceptions.VersionMismatchException;
import org.myproject.model.interfaces.IModelUpdateEventProvider;
import org.myproject.model.interfaces.IModelUpdateListener;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
/**
* This abstract class defines the necessary methods for persisting a data
* model using Hibernate. It uses the session-per-conversation pattern.<br/>
* The user shall provide the abstract methods load and create. The
* VersionMismatchException exception may be thrown if the persister
* implementation detects a mismatch between the data model in the database
* and the data model used by the application.<br/>
* When the conversation is ended, the user shall call the dispose method
* to free the hibernate session and session factories.<br/>
* When an exception is thrown, the application should either exit or reload
* the model.<br/>
*
* @param <Model> A model class to persist. This model should implement the
* IModelUpdateEventProvider interface and trigger the addEntry, removeEntry
* and updateEntry callbacks when an element has been added, removed or
* modified.
*/
public abstract class Persister<Model> implements IModelUpdateListener {
/**
* A logger
*/
private static final Logger logger = Logger.getLogger(Persister.class);
/**
* The current hibernate session (session-per-conversation pattern).
* We assume hibernate is configured with:
* - hibernate.connection.release_mode = after_transaction
*/
protected Session session = null;
/**
* The model to persist
*/
protected Model model;
/**
* The hibernate session factory.
*/
protected SessionFactory sessionFactory = null;
public Persister(Model model) {
this.model = model;
((IModelUpdateEventProvider)model).addModelUpdateListener(this);
}
/**
* Disposing the persister will close the session and the session factory
* that is used by this instance. It should be called before exiting the
* application or when the conversion is ended.<br/>
* Accessing to collections in the model after calling this method should
* be made carefully as some collection may not have been loaded due to
* lazy collection handling in Hibernate.
*/
public void dispose() {
// Unregister for model modification events
((IModelUpdateEventProvider)model).removeModelUpdateListener(this);
// Close the session
if (session!=null) {
session.flush(); // necessary?
session.close();
session = null;
}
// Close the session factory
if (sessionFactory!=null) {
sessionFactory.close();
sessionFactory = null;
}
}
/**
* This method creates the session factory, opens a session and loads the
* model from the database.<br/>
* @param databaseURL
* The URL of the database from which to load the model for
* this instance of persister.
* @throws VersionMismatchException
* If the persister detects that the database structure is not
* as expected.
*/
public abstract void load(String databaseURL) throws VersionMismatchException;
/**
* This method creates the session factory, opens a session and creates
* the model in the database.<br/>
* @param databaseURL
* The URL of the database to create and manage within this
* instance.
*/
public abstract void create(String databaseURL);
/**
* Get the persisted model.
* @return The model that is persisted by this instance.
*/
public Model getModel() {
return model;
}
/*
* (non-Javadoc)
* @see org.myproject.model.interfaces.IModelUpdateListener#addEntry(java.lang.Object)
*/
public void addEntry(Object entry) {
logger.debug("addEntry IN: "+entry);
try {
session.beginTransaction();
session.save(entry);
session.getTransaction().commit();
} catch (HibernateException e) {
logger.error("Could not persist instance of class "+entry.getClass());
logger.error("Caused by: "+e);
session.getTransaction().rollback();
dispose();
throw e;
}
logger.debug("addEntry OUT: "+entry);
}
/*
* (non-Javadoc)
* @see org.myproject.model.interfaces.IModelUpdateListener#removeEntry(java.lang.Object)
*/
public void removeEntry(Object entry) {
logger.debug("removeEntry IN: "+entry);
try {
session.beginTransaction();
session.delete(entry);
session.getTransaction().commit();
} catch (HibernateException e) {
logger.error("Could not delete instance of class "+entry.getClass());
logger.error("Caused by: "+e);
session.getTransaction().rollback();
dispose();
throw e;
}
logger.debug("removeEntry OUT: "+entry);
}
/*
* (non-Javadoc)
* @see org.myproject.model.interfaces.IModelUpdateListener#updateEntry(java.lang.Object)
*/
public void updateEntry(Object entry) {
logger.debug("updateEntry IN: "+entry);
try {
session.beginTransaction();
session.merge(entry);
session.getTransaction().commit();
} catch (HibernateException e) {
logger.error("Could not flush on the update of an instance of class "+entry.getClass());
logger.error("Caused by: "+e);
session.getTransaction().rollback();
dispose();
throw e;
}
logger.debug("updateEntry OUT: "+entry);
}
}
Code:
12:38:44,144 DEBUG Persister:69 - addEntry IN: org.myproject.model.Environment@15386c2
12:38:44,144 DEBUG JDBCTransaction:54 - begin
12:38:44,144 DEBUG ConnectionManager:421 - opening JDBC connection
12:38:44,160 DEBUG DriverManagerConnectionProvider:93 - total checked-out connections: 0
12:38:44,160 DEBUG DriverManagerConnectionProvider:99 - using pooled JDBC connection, pool size: 0
12:38:44,160 DEBUG JDBCTransaction:59 - current autocommit status: false
12:38:44,160 DEBUG JDBCContext:214 - after transaction begin
12:38:44,160 DEBUG DefaultSaveOrUpdateEventListener:158 - saving transient instance
12:38:44,160 DEBUG IncrementGenerator:80 - fetching initial value: select max(ENV_ID) from Environment
12:38:44,160 DEBUG AbstractBatcher:366 - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
12:38:44,160 DEBUG SQL:401 - select max(ENV_ID) from Environment
12:38:44,160 DEBUG AbstractBatcher:484 - preparing statement
12:38:44,160 DEBUG IncrementGenerator:95 - first free id: 1
12:38:44,160 DEBUG AbstractBatcher:374 - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
12:38:44,160 DEBUG AbstractBatcher:533 - closing statement
12:38:44,160 DEBUG AbstractSaveEventListener:112 - generated identifier: 1, using strategy: org.hibernate.id.IncrementGenerator
12:38:44,160 DEBUG AbstractSaveEventListener:153 - saving [org.myproject.model.Environment#1]
12:38:44,191 DEBUG JDBCTransaction:103 - commit
12:38:44,191 DEBUG SessionImpl:337 - automatically flushing session
12:38:44,191 DEBUG AbstractFlushingEventListener:58 - flushing session
12:38:44,191 DEBUG AbstractFlushingEventListener:111 - processing flush-time cascades
12:38:44,191 DEBUG AbstractFlushingEventListener:154 - dirty checking collections
12:38:44,191 DEBUG AbstractFlushingEventListener:171 - Flushing entities and processing referenced collections
12:38:44,191 DEBUG AbstractFlushingEventListener:210 - Processing unreferenced collections
12:38:44,191 DEBUG AbstractFlushingEventListener:224 - Scheduling collection removes/(re)creates/updates
12:38:44,191 DEBUG AbstractFlushingEventListener:85 - Flushed: 1 insertions, 0 updates, 0 deletions to 7 objects
12:38:44,191 DEBUG AbstractFlushingEventListener:91 - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
12:38:44,191 DEBUG Printer:83 - listing entities:
12:38:44,191 DEBUG Printer:90 - org.myproject.model.RCSEntry{criterias=No safety impact, severity=5, id=3}
12:38:44,191 DEBUG Printer:90 - org.myproject.model.RCSEntry{criterias=Loss of separation (more than half), severity=2, id=5}
12:38:44,191 DEBUG Printer:90 - org.myproject.model.Environment{description=Describe the environment here, name=ENV_1, id=1}
12:38:44,191 DEBUG Printer:90 - org.myproject.model.RCSEntry{criterias=Controler workload increase, severity=4, id=1}
12:38:44,191 DEBUG Printer:90 - org.myproject.model.RCSEntry{criterias=Accident, severity=1, id=2}
12:38:44,191 DEBUG Printer:90 - org.myproject.model.RCSEntry{criterias=Loss of separation (less than half), severity=3, id=4}
12:38:44,191 DEBUG Printer:90 - org.myproject.model.ModelStatus{modelVersion=2, id=1}
12:38:44,191 DEBUG AbstractFlushingEventListener:290 - executing flush
12:38:44,191 DEBUG ConnectionManager:469 - registering flush begin
12:38:44,191 DEBUG AbstractEntityPersister:2209 - Inserting entity: [org.myproject.model.Environment#1]
12:38:44,332 DEBUG AbstractBatcher:366 - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
12:38:44,332 DEBUG SQL:401 - insert into Environment (name, description, ENV_ID) values (?, ?, ?)
12:38:44,332 DEBUG AbstractBatcher:484 - preparing statement
12:38:44,332 DEBUG AbstractEntityPersister:1997 - Dehydrating entity: [org.myproject.model.Environment#1]
12:38:44,332 DEBUG StringType:133 - binding 'ENV_1' to parameter: 1
12:38:44,332 DEBUG StringType:133 - binding 'Describe the environment here' to parameter: 2
12:38:44,332 DEBUG LongType:133 - binding '1' to parameter: 3
12:38:44,332 DEBUG AbstractBatcher:44 - Executing batch size: 1
12:38:44,332 DEBUG AbstractBatcher:374 - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
12:38:44,332 DEBUG AbstractBatcher:533 - closing statement
12:38:44,332 DEBUG ConnectionManager:478 - registering flush end
12:38:44,332 DEBUG AbstractFlushingEventListener:321 - post flush
12:38:44,332 DEBUG JDBCContext:205 - before transaction completion
12:38:44,348 DEBUG SessionImpl:393 - before transaction completion
12:38:44,348 DEBUG JDBCTransaction:116 - committed JDBC Connection
12:38:44,348 DEBUG JDBCContext:219 - after transaction completion
12:38:44,348 DEBUG ConnectionManager:404 - aggressively releasing JDBC connection
12:38:44,348 DEBUG ConnectionManager:441 - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
12:38:44,348 DEBUG DriverManagerConnectionProvider:129 - returning connection to pool, pool size: 1
12:38:44,348 DEBUG SessionImpl:422 - after transaction completion
12:38:44,348 DEBUG Persister:81 - addEntry OUT: org.myproject.model.Environment@15386c2