I thought that RuntimeExceptions in a #Transaction lead to a revert and checked exceptions not.
For example I have two classes:
A:
#POST
#Path( DO_SOMETHING )
#Consumes( MediaType.APPLICATION_JSON )
#Produces( { MediaType.APPLICATION_JSON } )
#Authenticated
public RestResponse doSomething( SetttingsDTO settings ) {
Response eok = new Response();
try {
b.callMe( settings );
eok.setResponse( "Successfull", null );
} catch ( Exception ex ) {
log.error( ex.getMessage() );
eok.setResponse( ex );
}
return eok;
}
B class:
#Transactional
public void callMe( SetttingsDTO settings ) throws Exception {
/*
Here we do some things in the database
*/
throw new IOException("Test");
}
In my understanding because the IOException is thrown and catched in the Class A the database should not return the database-changes. But in real the database does revert. Although the exception is catched my database-changes are written in the database.
Can someone please explain why?
Related
i'm trying to intercept an optimistick lock exception and throw another exception but it doesn't work in my case, the exception is catched but i still have error optimistick lock in my console.
//MY DAO
public Entity getEntitieSimple(...) throws CustomException{
Entity entity="my select";
}
//MY SERVICE
#Transactional(propagation = Propagation.REQUIRED, readOnly = true)
public Entity recupererEntity() throws CustomException{
Entity entity =null;
try {
entity = dao.getEntitieSimple(...);
}catch (Exception exec){
throw new CustomException("custom message");
}
return entity;
}
#Transactional(propagation = Propagation.REQUIRED)
public void myUpdate() throws HibernateException{
try {
// entity update here
}catch (HibernateException exec){
log.error("OPTIMISTIC: "+ exec.getMessage());
throw new HibernateException("optimi update");
}
}
I'm getting this error evern after catching the exception:
ERROR - 29-09-2022 09:59:44.204 - http-nio-8090-exec-5 - 3933807 - null - c:96e7d7ca8f7d5637 - org.hibernate.internal.ExceptionMapperStandardImpl - HHH000346: Error during managed flush [Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.apicil.cosy.dop.domain.impl.Entity#1420]]
You have to catch ConcurrencyFailureException
private void updateStatus(UpdateOrderDto dto, OrdersStatusUpdaterService self) {
try {
updateStatusHelper(dto, self);
} catch (ConcurrencyFailureException ex) {
log.error("Concurrent modification error. Will try next time. orderId={}", dto.getId(), ex);
}
}
My code is written in Kotlin. I have a config class defined in a file along 2 more classes as below:
#Configuration
class MultipartConfig(private val multipartProperties: MultipartProperties) {
#Bean
fun multipartResolver(): StandardServletMultipartResolver {
val multipartResolver = MultipartResolver(multipartProperties)
multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily)
return multipartResolver
}
}
class MultipartResolver(private val multipartProperties: MultipartProperties) :
StandardServletMultipartResolver() {
override fun resolveMultipart(request: HttpServletRequest): MultipartHttpServletRequest {
return MultipartHttpServletRequest(multipartProperties, request)
}
}
class MultipartHttpServletRequest(
private val multipartProperties: MultipartProperties, request: HttpServletRequest
) : StandardMultipartHttpServletRequest(request, multipartProperties.isResolveLazily) {
override fun handleParseFailure(ex: Throwable) {
val msg = ex.message
if (msg != null && msg.contains("size") && msg.contains("exceed")) {
throw MaxUploadSizeExceededException(multipartProperties.maxFileSize.toMegabytes(), ex)
}
throw MultipartException("Failed to parse multipart servlet request", ex)
}
}
When I debug this code, in the class MultipartHttpServletRequest, constructor property multipartProperties is NOT null but the same property in the throw MaxUploadSizeExceededException(multipartProperties.maxFileSize.toMegabytes(), ex) is ALWAYS null. I cannot understand why this is happening.
Could someone please explain why this is happening?
I'm just answering my own question for clarity. I figured out the issue. It was related to the constructor of that class StandardMultipartHttpServletRequest. Below is the code of the constrcutor.
public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing)
throws MultipartException {
super(request);
if (!lazyParsing) {
parseRequest(request);
}
}
Now, inside of parseRequest there is a catch block (for brevity I'm not posting the whole code of the method)
catch (Throwable ex) {
handleParseFailure(ex);
}
When the constructor of the super class is throwing exception the child's constructor will not get a chance to initialize.
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.
I am doing a simple experiment for debugging purpose.
First I insert serveral records to database, and then I do a invalid data conversion which will throw DataIntegrityViolationException, but I will catch the exception.
I expected the records being successfully inserted into the db, since I catch the checked exception. But the whole thing is rolled back.
I do the experiment again using TransactionTemplate instead of using annotation, same result.
My questions are:
is this the expected behavior?
If anwser to No.1 is yes, then I catch the exception, how is it possible that spring knows an exception is thrown?
Here is my code:
public void insertValue() {
jdbcTemplate.execute("insert into people (person_id, name) values (4, 'asjkdhadsjkqhweqkewhkashdkahd')");
jdbcTemplate.execute("insert into people (person_id, name) values (5, 'tttqqq')");
}
// this should throw exception
public void truncateValue() {
jdbcTemplate.execute("alter table people alter column name varchar(7)");
}
public void jdbc_calls() {
insertValue();
try {
truncateValue();
} catch (Exception e) {
System.out.println(e.getMessage());
}
System.out.println("Finish");
}
public void run() {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
transactionTemplate.execute(transactionStatus -> {
try {
jdbc_calls();
} catch (RuntimeException e) {
throw e;
} catch (Throwable e) {
throw new RuntimeException(e);
}
return null;
});
}
More about question No.2.
Here is the source code of TransactionTemplate.execute()
From my understanding, if I don't throw an exception, rollbackOnException won'r be triggered.
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
}
else {
TransactionStatus status = this.transactionManager.getTransaction(this);
T result;
try {
result = action.doInTransaction(status);
}
catch (RuntimeException | Error ex) {
// Transactional code threw application exception -> rollback
rollbackOnException(status, ex);
throw ex;
}
catch (Throwable ex) {
// Transactional code threw unexpected exception -> rollback
rollbackOnException(status, ex);
throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
}
this.transactionManager.commit(status);
return result;
}
}
is this the expected behavior?
Yes, it is.
If anwser to No.1 is yes, then I catch the exception, how is it possible that spring knows an exception is thrown?
When an exception occurs, spring will mark your transaction as rollbackOnly.
So even when you catch your exception, at the end of your method, your transaction still rolled back.
In your case, I don't get why you use #Transaction since you want to commit regardless if exception occurs.
Edit
When you're using transaction with DB, the transaction invocation is delegated to EntityManager.
Look at AbstractEntityManagerImpl#handlePersistenceException:
#Override
public void handlePersistenceException(PersistenceException e) {
if ( e instanceof NoResultException ) {
return;
}
if ( e instanceof NonUniqueResultException ) {
return;
}
if ( e instanceof LockTimeoutException ) {
return;
}
if ( e instanceof QueryTimeoutException ) {
return;
}
try {
markForRollbackOnly();
}
catch ( Exception ne ) {
//we do not want the subsequent exception to swallow the original one
LOG.unableToMarkForRollbackOnPersistenceException(ne);
}
}
When exception occurs, the EntityManager mark your transaction as rollbackOnly before throws out the exception for you to catch.
After the exception is catched in your service, the AbstractPlatformTransactionManager will try to commit (because, as you know, no exception is detected there), but the EntityManager refuses to commit because its detect that the transaction marked as rollback-only.
If you read the exception, you will see something like:
javax.persistence.RollbackException: Transaction marked as rollbackOnly
I cannot get my afterThrowing Spring AOP advice to fire,
I have made the point cut as generic as possible now and it still does not fire
I hope this is just a poor pointcut but I cannot see why, I would be grateful if anyone could see why
Advice
//Generic Exceptions
#AfterThrowing(value = "execution(* *(..)) throws Exception", throwing = "exception")
public void loggingGenericException(JoinPoint joinPoint, Exception exception) {
String classMethod = this.getClassMethod(joinPoint);
String stackTrace = "";
for (StackTraceElement element : exception.getStackTrace()) {
stackTrace += element.toString() + "\n";
}
String exceptionMessageAndStackTrace = exception.getMessage() + "\n" + stackTrace;
if (exception instanceof EmptyResultSetException) {
this.infoLevelLogging(joinPoint, classMethod);
} else {
this.errorLevelLogging(joinPoint, classMethod, exceptionMessageAndStackTrace);
}
}
Method that should be advised
public void getStudentTranscript(String studentId) throws RestClientException,IllegalArgumentException{
if (!this.serviceUrl.isEmpty()) {
if(studentId.isEmpty())
{
throw new IllegalArgumentException("studentId empty");
}
this.transcript = (Transcript) super.getForObject(this.serviceUrl,Transcript.class, studentId);
} else {
throw new IllegalArgumentException("url is empty");
}
}
If I run a test to check it is applied it is not working the test looks like this
#Test
public void testLoggingFiredOnExceptionInTranscriptRepository() throws Exception
{
Log log;
log = mock(Log.class);
when(log.isErrorEnabled()).thenReturn(true);
try {
loggingAspects.setLogger(log);
transcriptRepository.setServiceUrl("");
transcriptRepository.getStudentTranscript("12345");
} catch (RuntimeException e) {
System.out.println("e = " + e);
verify(log, times(1)).isErrorEnabled();
verify(log, times(1)).error(anyString());
}
}
The system out shows an exception fired
Can anyone offer any advice ( pun intended) :-)
Did you put the <aop:aspectj-autoproxy /> element in your spring configuration file? Otherwise, the AOP annotations won't be interpreted.
FYI, after having read your question, I created a sample project on my own and the #AfterThrowing annotation just works as it should.