org.springframework.jdbc DB rollback doesn't revert the state - java

I try to rollback my DB change
the roollback code runs with no exception and yet my DB is dirty with changes.
Am i missing something?
final Connection dbConnection = rulesUiRepository.getConnection();
dbConnection.setAutoCommit(false);
try {
if (rulesUiRepository.updateRulesUiSnapshot(this.nonSplittedRulesSnapshot) == -1)
throw new RuntimeException("cannot save ui snapshot to DB");
...more code
} catch (Exception e) {
logger.error("transaction to update db and cofman failed", e);
//did work
//dbConnection.rollback();
throw new Exception("transaction to update db and cofman failed", e);
} finally {
//or
if (dbConnection != null) {
dbConnection.close();
}
}
with that code:
public synchronized void rollback() throws SQLException {
try {
this.txn_known_resolved = true;
this.inner.rollback();
} catch (NullPointerException var2) {
if(this.isDetached()) {
throw SqlUtils.toSQLException("You can't operate on a closed Connection!!!", var2);
} else {
throw var2;
}
} catch (Exception var3) {
if(!this.isDetached()) {
throw this.parentPooledConnection.handleThrowable(var3);
} else {
throw SqlUtils.toSQLException(var3);
}
}
}

Rollbak do rollback since last commit. You have code you didn't show that might do commit implicitly by using #Transactional for example or explicitly and the rollback transaction will be effective only on database transactions after it

Related

How to get Thread Safe in DAO design?

To my understanding it is possible with this code that when changing a user role another user can change the same role and always wins the last. It would even be possible for us to store parts of one and parts of the other. This is possible due to the 3 queries in the DAO. I would like to get "ThreadSafe" that during a change not another user can make a change or it will be detected that someone changed it before.
My idea was to change the method in the RoleManager.
Idea:
public interface RoleManager {
static synchronized void EditRole(UserRoleBO editedObjet, UserRoleBO nonEditedObject);
This does not work with this type of design(with a interface).
My Question:
Is there an elegant way to solve the problem without changing the
design?
Addition Note:
Tell me if i have big mistakes in my code.
Manager:
public class RoleManagerImpl implements RoleManager {
#Override
public void editRole(UserRoleBO editedObjet, UserRoleBO nonEditedObject) {
EditUserRole editUserRole = EditUserRole.Factory.createEditUserRole(nonEditedObject);
boolean hasChangedBeforeInDB = editUserRole.detectChanges();
if (hasChangedBeforeInDB) {
throw new ManagerException(ManagerException.TYPE.HASCHANGEDBEFOREINDB, null);
}
RoleDAO roleDAO = new RoleDAOImpl();
roleDAO.editRole(editedObjet);
}
}
DAO:
#Override
public int editRole(UserRoleBO role) {
Connection conn = null;
int status;
try {
//Set up connection
conn = ConnectionPool.getInstance().acquire();
DSLContext create = DSL.using(conn, SQLDialect.MARIADB);
//sql processing and return
status = create.executeUpdate(role.getRole());
EditUserRole editUserRole = EditUserRole.Factory.createEditUserRole(role);
editUserRole.detectChanges();
addPermission(editUserRole.getAddlist(), role.getRole());
deletePermissions(editUserRole.getDeleteList(), role.getRole());
}
// Error handling sql
catch (MappingException e) {
throw new DAOException(DAOException.TYPE.MAPPINGEXCEPTION, e);
}
catch (DataAccessException e) {
throw new DAOException(DAOException.TYPE.DATAACCESSEXECPTION, e);
}
catch (Exception e) {
throw new DAOException(DAOException.TYPE.UNKOWNEXCEPTION, e);
} finally {
//Connection release handling
try{
if(conn != null) {
ConnectionPool.getInstance().release(conn);
}
}
// Error handling connection
catch (DataAccessException e) {
throw new DAOException(DAOException.TYPE.RELEASECONNECTIONEXCEPTION, e);
}
catch (Exception e) {
throw new DAOException(DAOException.TYPE.UNKOWNRELEASECONNECTIONEXCEPTION, e);
}
}
//Return result
return status;
}
Thanks for helping.
this is just a possible answer. In my case, i use jooq and a mariadb.
With the assumption that we only have one central database this solution works. In a cluster, there is always the problem of the split brain.
What happens is that I lock the rows. So if the next thread tries to lock this he must wait. If it is allowed to continue, the exception HASCHANGEDBEFOREINDB is thrown.
Take care u have to commit or rollback to end the lock.
EditRole:
#Override
public int editRole(UserRoleBO editedRole ,UserRoleBO nonEditedRole) throws SQLException {
Connection conn = null;
int status;
try {
//Set up connection
conn = ConnectionPool.getInstance().acquire();
conn.setAutoCommit(false);
DSLContext create = DSL.using(conn, SQLDialect.MARIADB);
//lock rows
lockRowsOf(editedRole, conn);
EditUserRole editUserRole = EditUserRole.Factory.createEditUserRole(nonEditedRole);
boolean hasChangedBeforeInDB = editUserRole.detectChanges();
if (hasChangedBeforeInDB) {
throw new DAOException(DAOException.TYPE.HASCHANGEDBEFOREINDB, null);
}
EditUserRole editUserRole2 = EditUserRole.Factory.createEditUserRole(editedRole);
editUserRole2.detectChanges();
//sql processing and return
status = create.executeUpdate(editedRole.getRole());
addPermission(editUserRole2.getAddlist(), editedRole.getRole().getId(), conn);
deletePermissions(editUserRole2.getDeleteList(), editedRole.getRole(), conn);
conn.commit();
}
// Error handling sql
catch (MappingException e) {
conn.rollback();
throw new DAOException(DAOException.TYPE.MAPPINGEXCEPTION, e);
}
catch (DataAccessException e) {
conn.rollback();
throw new DAOException(DAOException.TYPE.DATAACCESSEXECPTION, e);
}
catch (Exception e) {
conn.rollback();
throw new DAOException(DAOException.TYPE.UNKOWNEXCEPTION, e);
} finally {
//Connection release handling
try{
if(conn != null) {
conn.setAutoCommit(true);
ConnectionPool.getInstance().release(conn);
}
}
// Error handling connection
catch (DataAccessException e) {
throw new DAOException(DAOException.TYPE.RELEASECONNECTIONEXCEPTION, e);
}
catch (Exception e) {
throw new DAOException(DAOException.TYPE.UNKOWNRELEASECONNECTIONEXCEPTION, e);
}
}
//Return result
return status;
}
Lock:
#Override
public void lockRowsOf(UserRoleBO role, Connection conn) {
int status;
try {
DSLContext create = DSL.using(conn, SQLDialect.MARIADB);
//sql processing and return
status = create.select()
.from(AUTH_ROLE)
.where(AUTH_ROLE.ID.eq(role.getRole().getId()))
.forUpdate().execute();
status = create.select()
.from(AUTH_ROLE_PERMISSION)
.where(AUTH_ROLE_PERMISSION.ROLE_ID.eq(role.getRole().getId()))
.forUpdate().execute();
}
// Error handling sql
catch (MappingException e) {
throw new DAOException(DAOException.TYPE.MAPPINGEXCEPTION, e);
}
catch (DataAccessException e) {
throw new DAOException(DAOException.TYPE.DATAACCESSEXECPTION, e);
}
catch (Exception e) {
throw new DAOException(DAOException.TYPE.UNKOWNEXCEPTION, e);
} finally {
//Connection will still needed to buffer the delete and insert
}
}

How to test when log occurs on Exception

How do I test that the following code will perform the logging statement when Exception is thrown, using Mockito?
public void cleanUp() {
for (Map.Entry<String, Connection> connection : dbConnectionMap.entrySet()) {
try {
if (connection.getValue() != null) {
connection.getValue().close();
}
}catch (Exception e) {
LOGGER.log(Level.WARNING, "Exception when closing database connection: ", e);
}
}
reset();
}

SDN 3.3.0 with CDI; Use TransactionManager to Commit Returns CypherTransactionExecutionException

I have tried to work with SDN 3.3.0 to get a graphdatabase, which i want to provide via cdi to use it without the complete Spring-Framework.
I produce my TransactionManager with this code:
#ApplicationScoped
public class Neo4jConfigurationProvider {
#Resource(lookup = "rest/neo4j")
private String baseUriString;
#Produces
public GraphDatabase graphDatabase() {
return new SpringCypherRestGraphDatabase(baseUriString);
}
#TXM
#Produces
public TransactionManager transactionManager() {
return graphDatabase().getTransactionManager();
}
}
I getting this Exception, by using the TransactionManager from SpringCypherRestGraphDatabase
org.neo4j.rest.graphdb.query.CypherTransactionExecutionException:
Error executing statements: 404
{"results":[],"errors":[{"code":"Neo.ClientError.Transaction.UnknownId","message":"Unrecognized
transaction id. Transaction may have timed out and been rolled
back."}]}[{message={"results":[],"errors":[{"code":"Neo.ClientError.Transaction.UnknownId","message":"Unrecognized
transaction id. Transaction may have timed out and been rolled
back."}]}, code=Http.404}]
Usage of my TransactionManager:
#POST
#Consumes(PersonEntity.MEDIA_TYPE)
#Produces(PersonEntity.MEDIA_TYPE)
public Response create(PersonEntity entity) throws SystemException {
try {
transactionManager.begin();
Response response = doCreate(checkValid(entity));
transactionManager.commit();
return response;
} catch (ConstraintViolationException e) {
transactionManager.rollback();
throw BadRequestException.fromConstraintViolationException(e);
} catch (NotSupportedException e) {
transactionManager.rollback();
throw new InternalServerErrorException(formatMsg("create.persistence", e.getMessage()), e);
} catch (SystemException e) {
transactionManager.rollback();
throw new InternalServerErrorException(formatMsg("create.persistence", e.getMessage()), e);
} catch (HeuristicRollbackException e) {
transactionManager.rollback();
throw new InternalServerErrorException(formatMsg("create.persistence", e.getMessage()), e);
} catch (HeuristicMixedException e) {
transactionManager.rollback();
throw new InternalServerErrorException(formatMsg("create.persistence", e.getMessage()), e);
} catch (RollbackException e) {
transactionManager.rollback();
throw new InternalServerErrorException(formatMsg("create.persistence", e.getMessage()), e);
}
}
private Response doCreate(PersonEntity entity) {
if (repository.findBySic(entity.getSic()) == null) {
Person person = repository.save(new Person(entity.getSic(), entity.getGender(), getCurrentUsername()));
return createResponseBuilder.build(person);
} else {
throw new ConflictException(formatMsg("create.conflict", entity.getSic()));
}
}
My Provided TransactionManager is a RestCypherTransactionManager.
begin() use only beginTx() from Transaction
commit() use only tx.success() and tx.close()
rollback() use only tx.failure() and tx.close()
Now my Question is: Why does my Transaction may have timed out or been rolled back? If you have a better way to manage Transactions in SDN 3.3.0 without the whole Spring-Framework (like #Transactional) please tell my how.
Btw.: Sometimes a mvn clean install works, sometimes not.

Exception handling during database operations

I am bit curious to know in the below code snippet, is there any chances of database connection not being closed. I am getting an issue in the SonarQube telling "Method may fail to close database resource"
try {
con = OracleUtil.getConnection();
pstmtInsert = con.prepareStatement(insertUpdateQuery);
pstmtInsert.setString(++k, categoryCode);
pstmtInsert.clearParameters();
pstmtInsert = con.prepareStatement(updateQuery);
for (i = 0; i < userList.size(); i++) {
pstmtInsert.setString(1, p_setId);
addCount = pstmtInsert.executeUpdate();
if (addCount == 1) {
con.commit();
usercount++;
} else {
con.rollback();
}
}
}
catch (SQLException sqle) {
_log.error(methodName, "SQLException " + sqle.getMessage());
sqle.printStackTrace();
EventHandler.handle();//calling event handler
throw new BTSLBaseException(this, "addInterfaceDetails", "error.general.sql.processing");
}
catch (Exception e) {
_log.error(methodName, " Exception " + e.getMessage());
e.printStackTrace();
EventHandler.handle();//calling event handler
throw new BTSLBaseException(this, "addInterfaceDetails", "error.general.processing");
}
finally {
try {
if (pstmtInsert != null) {
pstmtInsert.close();
}
} catch (Exception e) {
_log.errorTrace(methodName, e);
}
try {
if (con != null) {
con.close();
}
} catch (Exception e) {
_log.errorTrace(methodName, e);
}
if (_log.isDebugEnabled()) {
_log.debug("addRewardDetails", " Exiting addCount " + addCount);
}
}
Thanks in advance
If you are using Java 7+, I suggest you use try-with-resources. It ensures the resources are closed after the operation is completed.
Issue has been resolved when I closed the first prepare statement before starting the another one.
added below code snippet after the line pstmtInsert.clearParameters();
try {
if (pstmtInsert != null) {
pstmtInsert.close();
}
} catch (Exception e) {
_log.errorTrace(methodName, e);
}

Java 1.6 java.sql try/catch/finally crazy coding

I'm currently creating a lot of classes that will access database using a connection pool.
So I get a connection, create a statement and get the result set. (I can't use Java 1.7 and the fantastic Automatic Resource Management)
When finishing my method I must finish with a finally block:
if (rs != null) {
try {
rs.close();
} catch (SQLException sqle) {
logger.error("Couldn't close result set", sqle);
}
}
if (st != null) {
try {
st.close();
} catch (SQLException sqle) {
logger.error("Couldn't close statement", sqle);
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException sqle) {
logger.error("Couldn't close connection", sqle);
}
}
I'm already seeing the nightmare it will be for XX classes having 4/5 methods each.
Would it be good practice to make an helper class which would got a special close method for each object type like :
public static void closeResource(Connection connection) {
if (connection != null) {
try {
connection.close();
} catch (SQLException sqle) {
logger.error("Couldn't close connection", sqle);
}
}
And then just doing my usual finally with xx.close(connection);xx.close(statement);xx.close(rs);
Or in the same thinking (I know at this point I'll shock some people as I myself find that a bit ackward), having a method like public static void closeResources(Object... obj) {} with an awful list of instanceof ?
Or in your experience, coding the whole thing everywhere is better ?
Use Apache commons project : http://commons.apache.org/dbutils/apidocs/org/apache/commons/dbutils/DbUtils.html
DbUtils.closeQuietly() is probably what you need
Use overloading.
private void close(ResultSet rSet) throws SQLException {
if (rSet != null) {
rSet.close();
}
}
private void close(Statement statement) throws SQLException {
if (statement != null) {
statement.close();
}
}
private void close(Connection conn) throws SQLException {
if (conn != null) {
conn.close();
}
}
Usage will be much cleaner now:
try {
// do db stuff
} catch (Exception e) {
logger.error("log it", e);
} finally {
close(rs);
close(cs);
close(conn);
}
Just one more example. Suitable for simple small projects.
Object doRequest() throws SQLException {
PreparedStatement ps = ... // initialize statement
try {
ResultSet rs = ps.executeQuery();
try {
// use ResultSet
return someResult;
} finally {
rs.close();
}
} finally {
ps.close();
}
}
Although it is not pretends to be complete solution (many nested try-finally are quite unreadable), there are several advantages:
Method itself not deals with exception handling. Often only caller may decide what to do with exception.
As follows, method always returns correct result or throws exception. No magic "error values" required.
Resources closed only if they were initialized. No need to check for null berode close().
You could also exploit the fact that for every class you want to close, the close method has no args, and make a reflective helper method like this:
public static final void tryClose(Object o){
if(o != null){
Method[] m = o.getClass().getMethods();
for (Method method : m) {
if("close".equals(method.getName())){
if(!method.isAccessible()) method.setAccessible(true);
try {
method.invoke(o);
} catch (Exception e) {
System.err.println(e);
}
break;
}
}
}
}
EDIT: Tested with FileWriter, works fine in my machine.

Categories