Added the ability to retry operations if a transient failure occurs. Also
cleaned up connection handling so that the connection to the database can properly be closed and reestablished on transient failure. git-svn-id: https://samskivert.googlecode.com/svn/trunk@103 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: MySQLRepository.java,v 1.2 2001/03/01 22:59:51 mdb Exp $
|
||||
// $Id: MySQLRepository.java,v 1.3 2001/03/19 22:59:08 mdb Exp $
|
||||
|
||||
package com.samskivert.jdbc;
|
||||
|
||||
@@ -52,4 +52,19 @@ public abstract class MySQLRepository extends Repository
|
||||
String msg = sqe.getMessage();
|
||||
return (msg != null && msg.indexOf("Duplicate entry") != -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not the supplied SQL exception is a transient
|
||||
* failure, meaning one that is not related to the SQL being executed,
|
||||
* but instead to the environment at the time of execution, like the
|
||||
* connection to the database having been lost.
|
||||
*
|
||||
* @return true if the exception was thrown due to a transient
|
||||
* failure, false if not.
|
||||
*/
|
||||
protected boolean isTransientException (SQLException sqe)
|
||||
{
|
||||
String msg = sqe.getMessage();
|
||||
return (msg != null && msg.indexOf("Lost connection") != -1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: Repository.java,v 1.3 2001/03/01 02:04:09 mdb Exp $
|
||||
// $Id: Repository.java,v 1.4 2001/03/19 22:59:08 mdb Exp $
|
||||
|
||||
package com.samskivert.jdbc;
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.sql.*;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.samskivert.Log;
|
||||
import com.samskivert.util.*;
|
||||
import com.samskivert.jdbc.jora.*;
|
||||
|
||||
@@ -37,30 +38,74 @@ public abstract class Repository
|
||||
public Repository (Properties props)
|
||||
throws SQLException
|
||||
{
|
||||
// create our JORA session
|
||||
String dclass =
|
||||
// extract our configuration parameters
|
||||
_dclass =
|
||||
requireProp(props, "driver", "No driver class specified.");
|
||||
_session = new Session(dclass);
|
||||
|
||||
// connect the session to the database
|
||||
String url =
|
||||
_url =
|
||||
requireProp(props, "url", "No driver url specified.");
|
||||
String username =
|
||||
_username =
|
||||
requireProp(props, "username", "No driver username specified.");
|
||||
String password =
|
||||
_password =
|
||||
requireProp(props, "password", "No driver password specified.");
|
||||
|
||||
// the only reason session.open() fails is class not found
|
||||
if (!_session.open(url, username, password)) {
|
||||
throw new SQLException("Unable to load JDBC driver class: " +
|
||||
dclass);
|
||||
}
|
||||
// establish a connection to the database
|
||||
establishConnection();
|
||||
}
|
||||
|
||||
// set auto-commit to false
|
||||
_session.connection.setAutoCommit(false);
|
||||
/** Establishes a connection to the database. */
|
||||
protected void establishConnection ()
|
||||
throws SQLException
|
||||
{
|
||||
// bail out if we've already got a connection
|
||||
if (_session != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create our table objects
|
||||
createTables();
|
||||
try {
|
||||
// create our JORA session
|
||||
_session = new Session(_dclass);
|
||||
|
||||
// connect the session to the database. the only reason
|
||||
// session.open() fails is class not found
|
||||
if (!_session.open(_url, _username, _password)) {
|
||||
throw new SQLException("Unable to load JDBC driver class: " +
|
||||
_dclass);
|
||||
}
|
||||
|
||||
// set auto-commit to false
|
||||
if (supportsTransactions()) {
|
||||
_session.connection.setAutoCommit(false);
|
||||
}
|
||||
|
||||
// create our table objects
|
||||
createTables();
|
||||
|
||||
} catch (SQLException sqe) {
|
||||
// if we have any problems establishing a connection, close
|
||||
// and clear out our session reference to prevent attempted
|
||||
// use of a half initialized session
|
||||
try {
|
||||
shutdown();
|
||||
} catch (SQLException sqe2) {
|
||||
Log.warning("Error shutting down half-initialized " +
|
||||
"connection [primary_failure=" + sqe +
|
||||
", secondary_failure=" + sqe2 + "].");
|
||||
}
|
||||
throw sqe;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the existing connection to the database and
|
||||
* reestablishes the connection.
|
||||
*/
|
||||
public void reestablishConnection ()
|
||||
throws SQLException
|
||||
{
|
||||
// first close the old connection
|
||||
shutdown();
|
||||
// then open a new one
|
||||
establishConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,7 +136,10 @@ public abstract class Repository
|
||||
public void shutdown ()
|
||||
throws SQLException
|
||||
{
|
||||
_session.close();
|
||||
if (_session != null) {
|
||||
_session.close();
|
||||
_session = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,31 +153,111 @@ public abstract class Repository
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the supplied operation followed by a call to commit() on
|
||||
* the session unless an SQLException or runtime error occurs, in
|
||||
* which case a call to rollback() is executed on the session.
|
||||
* Executes the supplied operation without attempting to reestablish
|
||||
* the database connection in the event of a transient failure.
|
||||
*/
|
||||
protected void execute (Operation op)
|
||||
throws SQLException
|
||||
{
|
||||
execute(op, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the supplied operation followed by a call to commit() on
|
||||
* the session unless an SQLException or runtime error occurs, in
|
||||
* which case a call to rollback() is executed on the session.
|
||||
*
|
||||
* @param retryOnTransientFailure if true and the operation fails due
|
||||
* to an transient failure (like losing the connection to the database
|
||||
* or deadlock detection), the connection to the database will be
|
||||
* reestablished and the operation attempted once more.
|
||||
*/
|
||||
protected void execute (Operation op, boolean retryOnTransientFailure)
|
||||
throws SQLException
|
||||
{
|
||||
// make sure our connection is established
|
||||
establishConnection();
|
||||
|
||||
try {
|
||||
// invoke the operation
|
||||
op.invoke();
|
||||
|
||||
// commit the session
|
||||
_session.commit();
|
||||
if (supportsTransactions()) {
|
||||
_session.commit();
|
||||
}
|
||||
|
||||
} catch (SQLException sqe) {
|
||||
// back out our changes if something got hosed
|
||||
_session.rollback();
|
||||
throw sqe;
|
||||
if (supportsTransactions()) {
|
||||
try {
|
||||
_session.rollback();
|
||||
} catch (SQLException rbe) {
|
||||
Log.warning("Unable to roll back operation.");
|
||||
Log.logStackTrace(rbe);
|
||||
}
|
||||
}
|
||||
|
||||
// if this is a transient failure and we've been requested to
|
||||
// retry such failures, close and reopen the database
|
||||
// connection and try one more time
|
||||
if (retryOnTransientFailure && isTransientException(sqe)) {
|
||||
Log.info("Transient failure executing operation, " +
|
||||
"retrying [error=" + sqe + "].");
|
||||
// to reset the connection, we simply close it. it will be
|
||||
// reopened on our subsequent call to execute()
|
||||
shutdown();
|
||||
execute(op, false);
|
||||
|
||||
} else {
|
||||
throw sqe;
|
||||
}
|
||||
|
||||
} catch (RuntimeException rte) {
|
||||
// back out our changes if something got hosed
|
||||
_session.rollback();
|
||||
if (supportsTransactions()) {
|
||||
try {
|
||||
_session.rollback();
|
||||
} catch (SQLException rbe) {
|
||||
Log.warning("Unable to roll back operation.");
|
||||
Log.logStackTrace(rbe);
|
||||
}
|
||||
}
|
||||
throw rte;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specializations of this class for particular RDBMS/JDBC driver
|
||||
* combinations should override this method and indicate whether or
|
||||
* not transactions are supported.
|
||||
*/
|
||||
protected boolean supportsTransactions ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not the supplied SQL exception is a transient
|
||||
* failure, meaning one that is not related to the SQL being executed,
|
||||
* but instead to the environment at the time of execution, like the
|
||||
* connection to the database having been lost.
|
||||
*
|
||||
* <p> Specializations of the repository class for particular
|
||||
* database/JDBC driver combos should override this function and match
|
||||
* the appropriate exceptions.
|
||||
*
|
||||
* @return true if the exception was thrown due to a transient
|
||||
* failure, false if not.
|
||||
*/
|
||||
protected boolean isTransientException (SQLException sqe)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected Session _session;
|
||||
protected String _dclass;
|
||||
protected String _url;
|
||||
protected String _username;
|
||||
protected String _password;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user