Spring Transaction Rollback When No SQL is Executed - java

I am wondering if I need to rollback the following if the orderId is null, and the following SQL statement is never run? This is a simplified example of a method that is somewhat larger.
I am wondering if starting a transaction, but not doing anything due to the orderId being null, should be terminated somehow.
public OrderInfo insertOrder(String orderId) throws OrderException {
OrderInfo orderInfo = null;
DefaultTransactionDefinition txnDefinition = new DefaultTransactionDefinition();
txnDefinition.setName("InsertOrder");
txnDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus txnStatus = transactionManager.getTransaction(txnDefinition);
if (orderId != null && !orderId.isEmpty()) {
try {
orderInfo = orderDao.insertOrder(orderId);
if (orderInfo != null && orderInfo.getOrderId() > 0) {
transactionManager.commit(txnStatus);
}
else {
transactionManager.rollback(txnStatus);
throw new OrderException();
}
} catch (Exception e) {
transactionManager.rollback(txnStatus);
throw new OrderException();
}
}
// else rollback?
return orderInfo;
}

I suggest to try Spring TransactionTemplate
TransactionTemplate tt = new TransactionTemplate(transactionManager, transactionDefinition);
OrderInfo orderInfo = tt.execute(new TransactionCallback<OrderInfo>() {
#Override
public T doInTransaction(TransactionStatus status) {
return orderDao.insertOrder(orderId);
}
});
this is a guarantee that all things will be done right

Related

If I started transaction but because of the condition, I shouldn't do this, should I use commit or can I call rollback right away?

I have Dao Service that make order and If I don't found a car I don't need to commit
#Override
public String makeOrder(String[] stingNumbers, String[] categories, String userAddress, String userDestination, String login) {
int[] numbers = Stream.of(stingNumbers).mapToInt(Integer::parseInt).toArray();
Car[] foundCars = new Car[categories.length];
String messageTakenTime = null;
MySQLDAOFactory.createConnectionScope();
MySQLDAOFactory.createTransaction();
User foundUser = userDao.findUser(login);
for (int i = 0; i < foundCars.length; i++) {
foundCars[i] = carDao.findCar(numbers[i], categories[i]);
if (foundCars[i] == null) {
MySQLDAOFactory.endTransaction();
MySQLDAOFactory.abortTransaction();
MySQLDAOFactory.endConnectionScope();
return String.format("false %s %d", categories[i], numbers[i]);
}
carDao.updateCar(foundCars[i].getCarId(), "on Order");
double distance = DistanceUtil.getDistance(userAddress, userDestination);
CarCategory foundCarCategory = categoryDao.findCarCategory(categories[i]);
double discount = foundCarCategory.getDiscount();
double costPerKilo = foundCarCategory.getCostPerOneKilometer();
int scale = (int) Math.pow(10, 1);
double orderCost = (double) Math.round((distance * costPerKilo) - ((distance * costPerKilo) * discount) * scale) / scale;
Order order = new Order();
order.setUserId(foundUser.getUserId());
order.setCarId(foundCars[i].getCarId());
order.setOrderDate(LocalDateTime.now());
order.setUserAddress(userAddress);
order.setUserDestination(userDestination);
order.setOrderCost(orderCost);
orderDao.insertOrder(order);
if (messageTakenTime == null) {
messageTakenTime = DistanceUtil.takenTime(distance);
}
}
MySQLDAOFactory.endTransaction();
MySQLDAOFactory.endConnectionScope();
return messageTakenTime;
}
I have methods in DaoFactory that working with connection(open connection, start transaction,close connection,close transaction and make rollback)
public static void createTransaction() {
isTransaction = true;
try {
connection.setAutoCommit(false);
} catch (SQLException throwables) {
LOGGER.error(throwables);
}
}
public static void endTransaction() {
try {
connection.commit();
} catch (SQLException throwables) {
LOGGER.error(throwables);
}
}
public static void abortTransaction() {
try {
connection.rollback();
} catch (SQLException throwables) {
LOGGER.error(throwables);
}
}
public static void createConnectionScope() {
isConnectionScope = true;
try {
connection = DATA_SOURCE.getConnection();
} catch (SQLException e) {
LOGGER.error(e);
}
}
public static void endConnectionScope() {
isConnectionScope = false;
try {
connection.close();
} catch (SQLException throwables) {
LOGGER.error(throwables);
}
}
In my example If i dont want to commit my transaction what should I do?Call rollback?Or call commit and after that rollback? Also, if you could tell me how to catch exceptions in these methods, or let the method throw them and catch them directly in the service layer, because I don't quite understand how it is done. Thanks for reply.
If you don't want to commit a transaction you need use ROLLBACK.
If you COMMIT the transaction there is nothing left to ROLLBACK, these operations are mutually exclusive.
These operations that happens should be catch wherever are the logic of your application. Some times after a exception happen you need to do more things than a ROLLBACK. If you capture the exception in your DAO class you can't tell exactly what was happening and generate better messages or specific logic.

fortify is giving code correctness double check locking issue for the code

Class MnetLdapHelper {
private static volatile MnetLDAPService ldapSvc = null;
public MnetLdapHelper() throws Throwable {
if (ldapSvc == null) {
synchronized(MnetLdapHelper.class) {
if (ldapSvc == null) {
setup();
}
}
}
}
public void setup() throws Throwable {
PropertyManager appProp = PropertyUtil.getInstance()
.getPropertyManager();
try {
sLog.info("MnetLdapHelper setup(+) ");
ldapHost = appProp.getStringProperty("LDAP", "Host");
ldapSslPOrt = Integer.valueOf(appProp.getStringProperty("LDAP", "Port"));
ldapPort = Integer.valueOf(appProp.getStringProperty("LDAP", "NonSSLPort"));
baseDn = appProp.getStringProperty("LDAP", "BaseDN");
appCUID = appProp.getStringProperty("LDAP", "AppCUID");
appPasswd = appProp.getStringProperty("LDAP", "AppPassword");
groupBaseDn = appProp.getStringProperty("LDAPGROUPS", "GroupBaseDN");
String appDN = null;
int minConnInt = 1;
int maxConnInt = 10;
if (minConn != null && minConn.length() > 0) {
minConnInt = Integer.parseInt(minConn);
}
if (maxConn != null && maxConn.length() > 0) {
maxConnInt = Integer.parseInt(maxConn);
}
ldapSvc = new MnetLDAPService(ldapHost, ldapPort, false, null,
null, baseDn, minConnInt, maxConnInt);
LDAPEntry appEntry = ldapSvc.getUser(appCUID, null, null);
if (appEntry == null) {
sLog.error("Non-existent application CUID: [" + appCUID +
"], throw new Exception...");
throw new Exception("Non-existent application CUID: [" +
appCUID + "]");
}
appDN = appEntry.getDN();
sLog.info("appDN is: " + InfoSecValidation.cleanLogMessage(appDN));
// Empty the non-SSL connection pool
ldapSvc.finalize();
ldapSvc = new MnetLDAPService(ldapHost, ldapSslPOrt, true, appDN,
appPasswd, baseDn);
// ldapSvc.setDisconnect(Boolean.parseBoolean(ldapApiDisconnectConnectionFlag));
ldapSvc.setDisconnect(false);
// ldapSvc.setUserSearchAttributes(IDATTRS);
} catch (LDAPException ldEx) {
if (ldapSvc != null) {
ldapSvc.finalize();
ldapSvc = null;
}
sLog.error(
"LDAPException caught in setup(), throw new DocsServiceException... " +
ldEx.errorCodeToString(), ldEx);
throw ldEx;
} catch (Throwable ex) {
if (ldapSvc != null) {
ldapSvc.finalize();
ldapSvc = null;
}
sLog.error(
"Throwable caught in setup(), throw new DocsServiceException",
ex);
throw ex;
}
}
}
This above code is being used in my application . Previously I was getting code correctness : Double check locking fortify issue when I was not using volatile , then I found that we can solve this problem by using volatile keyword but issue is still there can someone help me what is wrong here
There is a flaw in your solution. The problem is that the setup() method can be called at any time by any thread, and that it changes the state of the private static by a side-effect.
A better solution is to change setup() to create and return a MnetLdapHelper instance, and the assign the result of to the static.
class MnetLdapHelper {
private static volatile MnetLDAPService ldapSvc = null;
public MnetLdapHelper() {
if (ldapSvc == null) {
synchronized (MnetLdapHelper.class) {
if (ldapSvc == null) {
ldapSvc = setup();
}
}
}
}
private MnetLDAPService setup() {
MnetLDAPService res = ...
// initialize it
return res;
}
}
Now it is debatable whether Fortify's is correct in reporting your version as an incorrectly implemented double-checked lock. It could be that the heuristic that Fortify is using is incorrect.
But that is moot if you can rewrite the code to satisfy Fortify. I think you will find that my version will do that.
I would also take issue with any method or constructor that is declared as throws Throwable. You should change this to list only the checked exceptions that you expect to be thrown.
Finally the way that you are using a static here is likely you cause problems for unit testing MnetLdapHelper or other code that uses a MnetLdapHelper instance.

Why is not completed transaction in Spring boot on TransactionStatus?

I don't know why is transaction is not completed.
Example)
public void transactionMethod() {
DefaultTransactionDefinition txDefinition = new DefaultTransactionDefinition();
txDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus testTx = txManager.getTransaction(txDefinition);
try {
txManager.commit(testTx);
} catch (Exception ex) {
log.error("Catch!!!!!!!!!!!");
}
if (Objects.nonNull(testTx) && !testTx.isCompleted()) {
txManager.rollback(testTx);
}
}
#Test
public void test() {
SimpleTransactionStatus testTx = new SimpleTransactionStatus();
when(txManager.getTransaction(any())).thenReturn(testTx);
target.transactionMethod();
}
I think rollback is not implemented because commit ended in try{}.
However, testTx.isCompleted()is false.....
Could you tell me why testTx.isCompleted() is false?

Is it safe to use try with resources in Java - does it check if the closeable is not null and does it catch exceptions while trying to close it

Is it safe to use Java's try with resources in Android- does it check if the closeable is not null and does it catch exceptions thrown by close while trying to close it?
if I convert this:
try {
inChannel.transferTo(0, inChannel.size(), outChannel);
} finally {
if (inChannel != null) {
inChannel.close();
}
if (outChannel != null) {
outChannel.close();
}
}
to
try (FileChannel inChannel = new FileInputStream(src).getChannel();
FileChannel outChannel = new FileOutputStream(dst).getChannel()) {
inChannel.transferTo(0, inChannel.size(), outChannel);
}
will it check if inChannel and outChannel are not null before trying to call close?
Also, is it safe to use try with resources here:
try {
cursor = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Images.Media.DATA},
MediaStore.Images.Media._ID + " =? ",
new String[]{"" + imageIdInMediaStore},
null);
if (cursor != null && cursor.getCount() > 0) {
cursor.moveToFirst();
return cursor.getString(0);
} else {
return "";
}
} catch (Exception e) {
return "";
} finally {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
cursor = null;
}
}
The finally block does an important check for !cursor.isClosed() - will try with resources figure out how to do that, or should I leave this unchanged?
The easiest way to find that out yourself is writing a test class simply telling you about it:
import junit.framework.TestCase;
public class __Test_AutoClosable extends TestCase {
public void testWithNull() throws Exception {
try (TestClosable tc = getNull()) {
assertNull("check existance of closable", tc);
}
}
public void testNonNullWithoutException() throws Exception {
try (TestClosable tc = getNotNull(false)) {
assertNotNull("check existance of closable", tc);
}
}
public void testNonNullWithException() throws Exception {
try (TestClosable tc = getNotNull(true)) {
assertNotNull("check existance of closable", tc);
}
catch(Exception e) {
assertEquals("check message", "Dummy Exception", e.getMessage());
}
}
TestClosable getNull() {
return null;
}
TestClosable getNotNull(boolean throwException) {
return new TestClosable(throwException);
}
static class TestClosable implements AutoCloseable {
private boolean throwException;
TestClosable(boolean throwException) {
this.throwException = throwException;
}
#Override
public void close() throws Exception {
if (throwException) {
throw new Exception("Dummy Exception");
}
}
}
}
This class runs through without errors, so the answers to your questions are:
Yes, null as response is possible, it is checked in the close-phase
Exceptions thrown on close are not catched but must be catched and handled by yourself
If you think about this a bit, that makes complete sense. Not supporting null would draw the whole construct useless, simply ignoring closes, even if they are implicitly done, is a bad, bad thing.
Edit: The testcase above is the result but for a "real" test, e.g. being added to your source base, you should actually check if the close-method in the autoclosable has been called.
Concerning your second question: If there are cases where you don't want the close to happen, a 1:1-change to try-with-resources doesn't work but you can do something like this:
public class CursorClosable implements AutoClosable {
private Cursor cursor;
public CursorClosable(Cursor cursor) {
this.cursor = cursor;
}
public Cursor getCursor() {
return cursor;
}
#Override
public void close() throws Exception {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
}
Your new code would then look like this:
try (Cursor cursorac = new CursorClosable(context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Images.Media.DATA},
MediaStore.Images.Media._ID + " =? ",
new String[]{"" + imageIdInMediaStore},
null)) {
Cursor cursor = cursorac.getCursor();
if (cursor != null && cursor.getCount() > 0) {
cursor.moveToFirst();
return cursor.getString(0);
} else {
return "";
}
} catch (Exception e) {
return "";
}
I'm not sure if the check for isClosed is really necessary so maybe you don't need this kind of "hack" for this particular example but it's still valid for other examples where you don't want to close resources.

Junit test case for 'if' condition in code

I am getting null pointer exception at if condition in the below method.Here is my method and junit test class i am working.
METHOD
#Override
public Event getDisplayEventDetails(String ceccid,
String ceocid) {
Event evnt = null;
if(!(validate.isStringBlank(ceccid)))
{
if(!(validate.isStringBlank(ceocid)))
{
String dispEventUri = eventServicesUrl;
eventSrvcLogger.debug("dispEventUri..."+dispEventUri);
try {
ResponseEntity<Event> responseEntity = restTemplate.getForEntity(dispEventUri , Event.class);
evnt=responseEntity.getBody();
if(responseEntity.getStatusCode().toString().equals("200")){
if(evnt.getValue().length > 0){
for(int i=0;i<evnt.getValue().length;i++){
DisplayValue val = new DisplayValue();
val = evnt.getValue()[i];
eventSrvcLogger.debug(val.toString());
}
} else{
evnt.setStatusCode(responseEntity.getStatusCode().toString());
evnt.setStatus(Boolean.FALSE);
evnt.setMessage("Exception occured in handling the request BAD REQUEST");
}
}
} catch (RestClientException e) {
eventSrvcLogger.error("DisplayEventServiceImpl displayEventDetail() RestClientException",
e);
}
}
}
return evnt;
}
Junit class
#Test
public void testGetDisplayEventDetails() throws Exception{
//test setup with mocking, expectations, data population
String eventServicesUrl = "http://restUrl";
Event evnt = newEvent();
ResponseEntity<Event> responseEntity = new ResponseEntity<Event>(evnt, HttpStatus.OK);
DisplayValue[] dv = new DisplayValue[1];
DisplayValue dvalue = new DisplayValue();
dvalue.setFirst_name("Ron");
dv[0] =dvalue;
evnt.setValue(dv);
new NonStrictExpectations() {
{
restTemplate.getForEntity(anyString,evnt.class );returns(responseEntity);
}
};
EventService evntSrvcImpl = new EventServiceImpl();
ReflectionTestUtils.setField(evntSrvcImpl,"eventServicesUrl", eventServicesUrl);
ReflectionTestUtils.setField(evntSrvcImpl,"restTemplate", restTemplate);
}
//execute your test case
Event evnt1 = evntSrvcImpl.getDisplayEventDetails("ceccid", "ceocid");
//perform verifications and assertions
assertNotNull(evnt);
assertEquals(evnt.getValue()[0].getName(), evnt1.getValue()[0].getName());
}
On debugging its throwing null pointer exception
at this line of code
if(responseEntity.getStatusCode().toString().equals("200"))
How to set that value in the junit test class ?
I got it by adding below lines to junit test class:
HttpStatus statusCode= HttpStatus.OK;,
responseEntity.getStatusCode(); returns(statusCode);

Categories