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
}
}
Related
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
I am currently facing connection leak problem in my code (Java , Struts). I have closed all the result sets, prepared statements, callable statements and the connection in the finally blocks of all the methods in my dao. still I face the issue.Additional information is , I am using StructDescriptor.createDescriptor for creating oracle objects. Will it cause any connection leaks? Please advise.
Code below
public boolean updatedDetails(Distribution distribution, String appCode, Connection dbConnection) {
boolean savedFlag = true;
CallableStatement updateStoredProc = null;
PreparedStatement pstmt1 = null;
try {
logger.debug("In DistributionDAO.updatedDistributionDetails");
//PreparedStatement pstmt1 = null;
ARRAY liArray = null;
ARRAY conArray = null;
ARRAY payArray = null;
ArrayDescriptor licenseeArrDesc = ArrayDescriptor.createDescriptor(LICENSEE_TAB, dbConnection);
ArrayDescriptor contractArrDesc = ArrayDescriptor.createDescriptor(DISTRIBUTION_CONTRACT_TAB, dbConnection);
ArrayDescriptor paymentArrDesc = ArrayDescriptor.createDescriptor(DISTRIBUTION_PAYMENT_TAB, dbConnection);
licenseeArray = new ARRAY(licenseeArrDesc, dbConnection, licenseeEleList.toArray());
contractArray = new ARRAY(contractArrDesc, dbConnection, contractEleList.toArray());
paymentArray = new ARRAY(paymentArrDesc, dbConnection, paymentEleList.toArray());
updateStoredProc = dbConnection.prepareCall("{CALL DIS_UPDATE_PROC(?,?,to_clob(?),?,?,?,?)}");
updateStoredProc.setLong(1, distribution.getDistributionId());
updateStoredProc.setString(2, distribution.getId());
updateStoredProc.setString(3, distribution.getNotes());
updateStoredProc.setString(4, distribution.getNotesUpdateFlag());
updateStoredProc.setArray(5, liArray);
updateStoredProc.setArray(6, conArray);
updateStoredProc.setArray(7, payArray);
String sql1="Update STORY set LAST_UPDATE_DATE_TIME= sysdate WHERE STORY_ID = ? ";
pstmt1=dbConnection.prepareStatement(sql1);
pstmt1.setLong(1,distribution.getStoryId());
pstmt1.execute();
List<Object> removedEleList = new ArrayList<Object>();
removedEleList.add(createDeleteElementObject(removedEle, dbConnection));
catch (SQLException sqle) {
savedFlag = false;
} catch (Exception e) {
savedFlag = false;
} finally {
try {
updateStoredProc.close();
updateStoredProc = null;
pstmt1.close();
pstmt1 = null;
dbConnection.close();
} catch (SQLException e) {
}
}
return savedFlag;
}
// Method createDeleteElementObject
private Object createDeleteElementObject(String removedEle,
Connection connection) {
StructDescriptor structDesc;
STRUCT structObj = null;
try {
structDesc = StructDescriptor.createDescriptor(DISTRIBUTION_REMOVED_ELEMENT_OBJ, connection);
if(removedEle != null) {
String[] tmpArr = removedEle.split("\\|");
if(tmpArr.length == 2) {
Object[] obj = new Object[2];
String eleType = tmpArr[0];
long eleId = Integer.parseInt(tmpArr[1]);
obj[0] = eleType.toUpperCase();
obj[1] = eleId;
structObj = new STRUCT(structDesc, connection, obj);
}
}
} catch (ArrayIndexOutOfBoundsException e) {
} catch (NumberFormatException e) {
} catch (SQLException e) {
}
return structObj;
}
Some hints on your code:
You pass a Connection variable into your call but close it inside your call - is the caller aware of that fact? It would be cleaner to get the connection inside your code or return it unclosed (calling method is responsible)
Exceptions are meant to be caught, not ignored - you don't log your exception - you'll never know what happens. I bet a simple e.printStackTrace() in your catch blocks will reveal helpful information.
Use try-with-resource (see this post)
//Resources declared in try-with-resource will be closed automatically.
try(Connection con = getConnection();
PreparedStatement ps = con.prepareStatement(sql)) {
//Process Statement...
} catch(SQLException e) {
e.printStackTrace();
}
At the very least put every close inside single try-catch:
} finally {
try {
if(updateStoredProc != null) {
updateStoredProc.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(pstmt1!= null) {
pstmt1.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(dbConnection != null) {
dbConnection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
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);
}
I have a requirement of trying to achieve transaction propagation across multiple stateful beans
I have 3 Stateful EJB;s in my application.. The start and end transaction is controlled be an external java application through remote interface method invocation.
#Remote
#Stateful
public class MyEJB1 implements RemoteEJB1{
#EJB
private RemoteEJB2 ejb2;
#Resource
UserTransaction utx;
public void startTransaction() {
try {
utx.begin();
} catch (NotSupportedException e) {
throw new EJBException(e);
} catch (SystemException e) {
throw new EJBException(e);
}
}
public void commitTransaction() {
try {
utx.commit();
} catch (SecurityException e) {
throw new EJBException(e);
} catch (IllegalStateException e) {
throw new EJBException(e);
} catch (RollbackException e) {
throw new EJBException(e);
} catch (HeuristicMixedException e) {
throw new EJBException(e);
} catch (HeuristicRollbackException e) {
throw new EJBException(e);
} catch (SystemException e) {
throw new EJBException(e);
}
}
public RemoteEJB2 getEJB2() {
return ejb2;
}
}
public class MyEJB2 implements RemoteEJB2{
#EJB
private RemoteEJB3 ejb3;
#Resource(name = "java:jboss/datasources/MyDS")
private DataSource ds;
public RemoteEJB3 getEJB3() {
return ejb3;
}
#TransactionAttribute(TransactionAttributeType.MANDATORY)
public void insertElement(String elementName) {
PreparedStatement pStat = null;
Connection con = null;
try {
con = ds.getConnection();
String sql = "insert into TRANSACTIONTEST(COL1,COL2) values(?,?)";
pStat = con.prepareStatement(sql);
pStat.setString(1, elementName);
pStat.setDouble(2, Math.random());
pStat.executeUpdate();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
con.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class MyEJB3 implements RemoteEJB3{
#Resource(name="java:jboss/datasources/MyDS")
private DataSource ds;
#TransactionAttribute(TransactionAttributeType.MANDATORY)
public void updateElement(String newName) {
PreparedStatement pStat = null;
Connection con = null;
try{ con = getDs().getConnection();
String sql ="update TRANSACTIONTEST set COL1=?";
pStat = con.prepareStatement(sql);
pStat.setString(1, newName);
pStat.executeUpdate();
}catch(Exception ex){
ex.printStackTrace();
}finally{
try {
con.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Test Class:
public class MyTest{
public static void main(String[] args) throws Exception {
final Hashtable jndiProperties = new Hashtable();
jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
final Context context = new InitialContext(jndiProperties);
MyEJB1 ejb1 = context.lookup("ejb:/EJBTrials/MyEJB1!edu.in.ejbinterfaces. RemoteEJB1?stateful");
ejb1.startTransaction();
RemoteEJB2 ejb2 = ejb1.getEJB2();
ejb2.insertElement (“Test”);
RemoteEJB3 ejb3 = ejb2.getEJB3();
ejb3.updateElement (“UpdatedTest”);
ejb1.commitTransaction();
}
}
I would ideally like the whole transaction(record insertions in db) to be completed after invocation of commitTransaction() on RemoteEJB1 bean.
I tried combination of BMT for EJB1 and CMT for EJB2 and EJB3 which lead to EJBTransactionRequiredException being thrown
I tried to make all beans as BMT. However according to EJB3.1 spec BMT cannot be propagated across multiple beans.
Could you let me know of any ideas/links which I could use to solve this problem?
Reference Application Server : JBOSS AS 7.1
Could you let me know of any ideas/links which I could use to solve this problem?
If I understand your issue correctly, wath you need is called Client-Managed trasaction demarcation. UnLike Container Maneged Transaction, in this case the client is responsable to set the transaction boundaries (start and commit/rollback the transaction). You can get some ideas of how to implement it here.
I have this function in userDAO
public List<Country> findAllCountries() {
try {
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("FROM Country");
return query.list();
} catch (Exception e) {
System.out.println(e);
}
}
The problem is that eclipse is giving error that telling me to either use void in return or add return statement , if i remove try catch then it works
I'd write it this way. I don't know what you should be doing to close your Session or clean up in a finally block. This will make Eclipse happy:
public List<Country> findAllCountries()
{
List<Country> countries = new ArrayList<Country>();
try
{
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("FROM Country");
countries = query.list();
}
catch (Exception e)
{
e.printStackTrace();
}
return countries;
}
You need to return something (or throw an Exception) from your catch block as well:
catch (Exception e) {
System.out.println(e);
return null;
}
catch (Exception e) {
System.out.println(e);
throw new RuntimeException("DAO failed", e);
}
Otherwise there will be a code path through the method without a return statement.