Spring boot #Transactional not calling my catch block - spring-boot

Why is the ConstraintViolationException catch block not executing below when I use #Transactional. It does when I remove #Transactional. I want it to execute so I can do some custom error handling. I do understand that Spring wraps my code in a proxy which does the transaction management and that checked exceptions are not rolledback by default. But I still can't connect the dots here:
#Transactional
public PropertyInserterResult insertOrUpdateProperty(ScalerProperty property,
MultipartFile logo, ScalerUser loggedInUser) {
ScalerLogo scalerLogo = null;
int success = 0, error = 0;
try {
logger.info("Logo size:"+logo.getSize());
if(logo.getSize() > 0) {
scalerLogo = new ScalerLogo(logo.getBytes());
scalerLogoRepository.save(scalerLogo);
}
scalerPropertyRepository.save(property.prepareToSave(loggedInUser, scalerLogo));
logger.info("Prop {}:{} inserted for {}",property.getKey(), property.getValue(), property.getCreatedBy().getUsername());
++success;
} catch (org.hibernate.exception.ConstraintViolationException cve) {
logger.error("Dupe key");
handleDuplicate(property);
++error;
}
return new PropertyInserterResult(success, error);
}

Related

grails.gorm.transactions.Transactional not rolling back

I am having issues rolling back a transaction in my service layer with the following:
Grails 3.3.8
GORM 6.1.10.RELEASE
I have the following service method:
import grails.gorm.transactions.Transactional
#Transactional(rollbackFor = Exception.class)
class TestingService {
void testServiceMethod(List<Factory> factories) {
try {
factories.each {
if (it.name == 'second') {
throw new Exception('second')
}
it.testField = 'Edited'
it.save()
println(it.name + ' saved')
}
} catch (Exception e) {
println('Exception Caught ' + e)
}
}
}
I have the following integration test created then also:
#Integration
#Rollback
class TestServiceIntSpec extends Specification {
#Autowired
TestingService testingService
def setup() {
}
def cleanup() {
}
void "test something"() {
when:
Factory factoryOne = new Factory(name: "first").save(flush: true)
Factory factoryTwo = new Factory(name: "second").save(flush: true)
List<Factory> factories = [factoryOne, factoryTwo]
testingService.testServiceMethod(factories)
then:
factoryOne.testField == null
factoryTwo.testField == null
}
}
I also have the following controller method:
class TestController {
TestingService testingService
def index() {
Factory factoryOne = new Factory(name: "first").save(flush: true)
Factory factoryTwo = new Factory(name: "second").save(flush: true)
List<Factory> factories = [factoryOne, factoryTwo]
testingService.testServiceMethod(factories)
println "First Factory: $factoryOne.testField"
println "First Factory: $factoryTwo.testField"
render 'Check Console'
}
}
I would have expected the test to pass as I thought the transaction would of rolled back after I threw new exception, the it.testField is persisting though however? Also when I ping the TestController it is outputting factoryOne.testField as 'edited'. Am I misunderstanding this correctly from the documentation?
"Services enable transaction demarcation, which is a declarative way of defining which methods are to be made transactional. To enable transactions on a service use the Transactional transform:
The result is that all methods are wrapped in a transaction and automatic rollback occurs if a method throws an exception (both Checked or Runtime exceptions) or an Error."
Source: https://docs.grails.org/latest/guide/services.html#declarativeTransactions
I can't see what I'm doing different from this other Stackoverflow answer either:
https://stackoverflow.com/a/25739582/6887293
The issue can be recreated by pulling the following Github project and running /factory/factory/src/integration-test/groovy/com/mycompany/myapp/TestServiceIntSpec.groovy or pinging /factory/factory/grails-app/controllers/com/mycompany/myapp/TestController.groovy
https://github.com/georgy3k/IntegrationTestRollBack/tree/8addd2b95a8ffa4570e70eccb3b023b0ccfef5aa
Thanks in advance ...
In your catch block you need to re-throw the exception.
catch (Exception e) {
println('Exception Caught ' + e)
throw e;
}
The problem as i see it, is that the exception never escapes the method.

Spring #Transactional behavior calling both Transactional and Non-Transactional Methods

I'm looking at some existing code and wanted to know what happen's in the following scenario with Spring's #Transactional annotation? Consider the following example:
A POST request hits a #Controller annotated with #Transactional:
#ResponseBody
#Transactional
#RequestMapping(value="/send", method=RequestMethod.POST)
public void send(#RequestBody Response response) {
try {
DBItem updatedDbItem = repository.updateResponse(response);
if (updatedDbItem == null){
//some logging
}
} catch(Exception ex) {
//some logging
}
}
The controller calls a non #transactional repository method which sets a value and in turns calls a another #Transactional method:
#Override
public DBItem updateResponse(Response response) {
try {
DBItem dBItem = findResponseById(response.getKey());
if (dBItem != null){
dBItem.setSomeField(response.getValue());
return updateDataBaseItem(response);
}
} catch (Exception ex) {
//some logging
}
return null;
}
The following updateDataBaseItem() method is common and called from other non transactional methods as well as the above method:
#Transactional
#Override
public DBItem updateDataBaseItem(Response response){
try {
DBItem dBItem = em.merge(response);
return dBItem;
} catch (Exception ex) {
//some logging
}
return null;
}
send() => spring detect #transaction with default parameters
actually Propagation setting is REQUIRED and the spring join the exist transaction or create new if none.
repository.updateResponse(..) => No transactions params the method execute within the same transaction already exist
updateDataBaseItem(..) => calling the method in same repository , spring will not recognize the #Transaction annotation because the use of proxy mode, so this method will be executed within the same transaction
a method within the target object calling another method of the target
object, will not lead to an actual transaction at runtime even if the
invoked method is marked with #Transactional

Test case using SpringJunitRunner

I want to write junit test case for the below code with springJunitRunner.
the below piece of code is one service in a class.
#Component
#Path(/techStack)
public class TechStackResource {
#Autowired
private transient TechStackService techStackService;
#GET
#Path("/{id}")
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response getTechStackById(final #PathParam("id") Integer technicalstackid) {
final TechStackResponse response = new TechStackResponse();
int statusCode = Constants.HTTP_STATUS_OK_200;
try {
TechStackModel techStackModel = techStackService.findObjectById(technicalstackid);
response.setGetTechStackDetails(GetTechStackDetails.newBuilder().technicalStack(techStackModel).build());
if (techStackModel == null) {
statusCode = Constants.HTTP_STATUS_ERROR_404;
}
} catch (EmptyResultDataAccessException erde) {
} catch (Exception e) {
LOGGER.error("Exception occured in TechStackResource.getTechStackById(technicalstackid) ", e);
throw new APMRestException(
"Exception while executing TechStackResource.getTechStackById(technicalstackid) ",
Constants.UNKNOW_ERROR, e);
}
return Response.status(statusCode).entity(response).build();
}
}
the configuration in web.xml for servlet is
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
Since you are using Jersey as well as Spring, you can use the SpringJunitRunner only to wire-up TechStackResource with its dependency TechStackService.
In order to test your REST handler method getTestStackById, you could go the POJO approach and invoke it directly. Alternatively, you can use Jersey's own MockWeb environment. To find out more about this, I recommend looking at the Jersey example sources, e.g. HelloWorld.

Exception handling using hibernate template in Spring framework

I am using Spring with Hibernate in my project.There are many methods written in DAO implementation java file and every method is using the same try/catch/finally lines of code which seem redundant to me.
I am told to optimize/refactor the code since the file LOC exceeds 10k.I read somewhere that using HibernateDaoSupport we need not to worry about exceptions or closing the session. It will be taken care of by Spring itself.
Could somebody please help me how to proceed or do the needful or any better way to handle exceptions?I am pasting below code of one method in DAO layer.
public class CSDDaoImpl extends HibernateDaoSupport implements CSDDao {
public Deal getDealStructure(long dealId) throws CSDServiceException {
Session session = null;
try {
session = getSession();
Deal deal = (Deal) session.createCriteria(Deal.class).add(
Restrictions.eq("dealId", dealId)).uniqueResult();
return deal;
} catch (DataAccessResourceFailureException darfex) {
String message = "Failed to retrieve the deal object.";
CSDServiceException ex = new CSDServiceException(message, darfex);
ex.setStackTrace(darfex.getStackTrace());
ex.setErrorCode(Constants.DATA_ACCESS_FAILURE_EXP);
ex.setMessageToUser(message);
throw ex;
} catch (IllegalStateException isex) {
String message = "Failed to retrieve the deal object.";
CSDServiceException ex = new CSDServiceException(message, isex);
ex.setStackTrace(isex.getStackTrace());
ex.setErrorCode(Constants.ILLEGAL_STATE_EP);
ex.setMessageToUser(message);
throw ex;
} catch (HibernateException hbex) {
String message = "Failed to retrieve the deal object.";
CSDServiceException ex = new CSDServiceException(message, hbex);
ex.setStackTrace(hbex.getStackTrace());
ex.setErrorCode(Constants.HIBERNATE_EXP);
ex.setMessageToUser(message);
throw ex;
} finally {
if (session != null && session.isOpen()) {
try {
session.close();
} catch (HibernateException hbex) {
log.error("Failed to close the Hibernate Session.", hbex);
hbex.printStackTrace();
CSDServiceException ex = new CSDServiceException(
"Failed to close the Hibernate Session.", hbex);
ex.initCause(hbex.getCause());
ex.setStackTrace(hbex.getStackTrace());
throw ex;
}
}
}
}
}
The best approach of handling exceptions is i believe through writing an Exception Interceptor to intercept all your DAO calls and you can catch the ones you only need in your application and wrap it with your own custom application specific exceptions.
You definitely do not need to work directly with session once an exception is thrown. That would defeat the purpose of using HibernateDaoSupport and Spring.
Have a look at this link : http://static.springsource.org/spring/docs/current/spring-framework-reference/html/classic-spring.html
Hope that helps.

CMT rollback : how to roll back the transaction

The following code does not help in roll back even if I throw null pointer exception at update() method. Everytime it inserts values into the database if I run the code. Please help me how can I roll back the transaction if null pointer is thrown at update() method. Am I missing something in the code?
#TransactionManagement(value = TransactionManagementType.CONTAINER)
public class Bean implements RemoteIF {
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void insertIntoDb() {
insert();
update();
}
private Integer update() {
val=0;
try {
Connection con = DbConn.getConnection();
Statement st = con.createStatement();
val1 = st.executeUpdate("INSERT INTO tab VALUES('ab')");
st.close();
throw new NullPointerException();
} catch (Exception e) {
System.out.println(e);
}
return val;
}
private Integer insert() {
int val = 0;
try {
Connection con = DbConn.getConnection();
Statement st = con.createStatement();
val = st.executeUpdate("INSERT INTO tab VALUES('bnm')");
st.close();
} catch (Exception e) {
System.out.println(e);
}
return val;
}
}
Couple things that stick out at me as suspect.
No #Stateless, #Stateful or #Singleton annotation on the Bean class. Unless you've declared the bean in an ejb-jar.xml file, then this is not getting recognized as an EJB. Definitely double check that.
The DbConn.getConnection() looks suspiciously like you might be trying to manage database connections yourself. If you have any code that uses the DriverManager or does new FooDataSource(), then that is definitely the problem. If you want transaction management to work you have to get all resources from the container via either
Injection via a #Resource DataSource datasource field in the EJB class
JNDI lookup of java:comp/env/yourDataSource, where yourDataSource is the name of a datasource you configured in the ejb-jar.xml or declared on the bean class via using #Resource(type=DataSource.class, name="youDataSource") -- that annotation goes on the class itself rather than a method or field.
See also these answers for some insight as to how transaction management works:
How does UserTransaction propagate?
Programming BMT - UserTransaction

Resources