I tumbled into this problem when working with a multi-threaded Java program that was using a Sqllite database. It uses file locking so I had to make sure that only one thread was doing work at the same time.
I basically ended up with using synchronized. When the ConnectionFactory returns a db connection, it also returns a lock object that one should lock when using the connection. So you could do synchronization lock manually, or create a subclass of the class below which does it for you:
/**
* Subclass this class and implement the persistInTransaction method to perform
* an update to the database.
*/
public abstract class DBOperationInTransaction {
protected Logger logger = Logger.getLogger(DBOperationInTransaction.class.getName());
public DBOperationInTransaction(ConnectionFactory connectionFactory) {
DBConnection con = null;
try {
con = connectionFactory.getConnection();
if(con == null) {
logger.log(Level.SEVERE, "Could not get db connection");
throw new RuntimException("Could not get db connection");
}
synchronized (con.activityLock) {
con.connection.setAutoCommit(false);
persistInTransaction(con.connection);
con.connection.commit();
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Failed to persist data:", e);
throw new RuntimeException(e);
} finally {
if(con != null) {
//Close con.connection silently.
}
}
}
/**
* Method for persisting data within a transaction. If any SQLExceptions
* occur they are logged and the transaction is rolled back.
*
* In the scope of the method there is a logger object available that any
* errors/warnings besides sqlException that you want to log.
*
* @param con
* Connection ready for use, do not do any transaction handling
* on this object.
* @throws SQLException
* Any SQL exception that your code might throw. These errors
* are logged. Any exception will rollback the transaction.
*
*/
abstract protected void persistInTransaction(Connection con) throws SQLException;
}
And the DBConnection struct:
final public class DBConnection {
public final Connection connection;
public final String activityLock;
public DBConnection(Connection connection, String activityLock) {
this.connection = connection;
this.activityLock = activityLock;
}
}