How to test rollback is working as expected? - java

I am trying to write an integration/unit test where an exception is applied to a DAO after a save has been performed - in order to validate the rollback behaviour. My thoughts were to create a Spring AOP aspect - and apply #AfterReturning advice to the 'save' method on the DAO.
The DAO is already proxied via #Transactional advice.
Does this seem like the right way to go ?
So far I'm trying to use a Spring ProxyFactory - to proxy the DAO in the unit test.
E.g.
ProxyFactory pf = new ProxyFactory(new MyFaultInjectingAspect());
pf.setTarget(myDao);
MyDao proxiedDao = (BookmarkDao) pf.getProxy();
Thank you.
FYI: relates to this Is it ok to use DataSourceTransactionManager for ORM persistence instead of HibernateTransactionManager?

From your DB side you can issue a lock by using select for update.
http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
And try to commit with your application, you should see a transaction rolled back exception, but with different reason.
updated link.

I guess there is other approach without any AOP that really checks that nothing is written in the DB:
If you have a test that verifies (if there is no exception) that the transaction is commited, and the entity is written to the DB, then you only need simple second test.
In this test you must do the same but with exception. And then you must only verify that noting is written to the DB. So you do not need the AOP Stuff, and your test becomes more meaningful because it test in the end what you really want. (I hopefully understand it right, that role back is only the technique to prohibit the database change.)

It looks like you're trying to do something similar to this:
How to rollback a database transaction when testing services with Spring in JUnit?

Related

How do I use multiple #Transactional annotated methods?

I have a Spring-boot project where I have a service bean with 2 #Transactional annotated methods.
These methods do read-only JPA (hibernated) actions to fetch data from an HSQL file database, using both JPA repositories and lazy loaded getters in entities.
I also have a cli bean that handles commands (Using PicoCLI). From one of these commands I try to call both #Transactional annotated methods, but I get the following error during execution of the second method:
org.hibernate.LazyInitializationException - could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:602)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:217)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:581)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:148)
at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:188)
at java.util.Spliterators$IteratorSpliterator.estimateSize(Spliterators.java:1821)
at java.util.Spliterator.getExactSizeIfKnown(Spliterator.java:408)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:566)
at <mypackage>.SomeImpl.getThings(SomeImpl.java:<linenr>)
...
If I mark the method that calls both #Transactional annotated methods with #Transactional itself, the code seems to work (due to there now only being 1 top level transaction I presume?).
I just want to find out why I cannot start multiple transactions in a single session or why the second transaction doesn't start a new session if there are none.
So my questions are:
Does this have to do with how hibernate starts a session, how transactions close sessions or anything related to the HSQL database?
Is adding an encompassing transaction the right way to fix the issue
or is this just fighting the symptom?
What would be the best way to be able to use multiple #Transactional annotated methods from one method?
EDIT: I want to make clear that I don't expose the entities outside of the transactional methods, so on the surface it looks to me like the 2 transactional methods should be working independently from one another.
EDIT2: for more clarification: the transactional methods need to be available in an api and the user of the api should be able to call multiple of these transactional methods, without needing to use transactional annotations and without getting the LazyInitializationException
Api:
public interface SomeApi {
List<String> getSomeList();
List<Something> getThings(String somethingGroupName);
}
Implementation:
public class SomeImpl implements SomeApi {
#Transactional
public List<String> getSomeList() {
return ...; //Do jpa stuff to get the list
}
#Transactional
public List<Something> getThings(String somethingGroupName) {
return ...; //Do other jpa stuff to get the result from the group name
}
}
Usage by 3rd party (who might not know what transactionality is):
public someMethod(String somethingGroupName) {
...
SomeApi someApi = ...; // Get an implementation of the api in some way
List<String> someList = someApi.someList();
if (someList.contains(somethingGroupName) {
System.out.println(someApi.getThings(somethingGroupName));
}
...
}
It seems that you are accessing some not initialized data from your entities after the transactions have ended. In that cases, the persistence provider may throw the lazyinitialization exception.
If you need to retrieve some information not eagerly loaded with the entities, you may use one of two strategies:
annotate the calling method also with #Transactional annotation, as you did: it does not start a new transaction for each call, but makes the opened transaction active until your calling method ends, avoiding the exception; or
make the called methods load eagerly the required fields USING the JOIN FETCH JPQL idiom.
Transaction boundaries requires some analysis of your scenario. Please, read this answer and search for better books or tutorials to master it. Probably only you will be able to define aptly your requirements.
I found that hibernate out of the box doesn't reopen a session and therefore doesn't enable lazy loading after the first transaction has ended, whether or not subsequent jpa statements are in a transaction or not. There is however a property in hibernate to enable this feature:
spring:
jpa:
properties:
hibernate.enable_lazy_load_no_trans: true
This will make sure that if there is no session, then a temp session will be created. I believe that it will also prevent a session from ending after a transaction, but I don't know this for sure.
Partial credit goes to the following answers from other StackOverflow questions:
http://stackoverflow.com/a/32046337/2877358
https://stackoverflow.com/a/11913404/2877358
WARNING: In hibernate 4.1.8 there is a bug that could lead to loss of data! Make sure that you are using 4.2.12, 4.3.5 or newer versions of hibernate. See: https://hibernate.atlassian.net/browse/HHH-7971.

Spring data jpa with multiple entities not rolling back - PostgreSQL

I am using spring-data-jpa in one of my projects. In service layer, I have annotated a private method with #Transactional and also enabled #EnableTransactionManagement in application. When one of the save method of entities throws an exception, the rest of the entities which were saved before are not rolling back. BTW I am using PostgreSQL.
Please let me know if I am missing anything here.
Spring transaction will only work with public method. As it need to inject code using proxy classes for transactions. So making your method public will resolve your issue. Have a look on documentation of proxy mechanism of spring.
Spring by default will rollback only for Runtime Exceptions (https://docs.spring.io/spring/docs/2.5.x/reference/transaction.html#transaction-declarative).
If you want to rollback for any exception, you could try adding:
#Transactional(rollbackFor = Exception.class)

Java EntityManager transaction gets broken, how to locate the SQL that causes it?

I have an EntityManager and a JobDAO class which has many methods which use the EntityManager to select/update/delete/insert.
For some methods, I now am getting a javax.persistence.TransactionRequiredException: Executing an update/delete query ..
about not having a transaction.
I have a #Transactional annotation on the method calling the other methods.
I have now fixed it somewhat by using my own database connection for some of these commands, but I'd like to find the SQL that causes the problem.
One idea I have is to add a transaction checker method call to the end of each of the 20 methods that are suspicious. But I'd like to know whether you have a better idea to check on the EntityManager, for example by logging all SQL so I can find the last SQL where it stopped working.
See this post on SO for configuring the ORM provider (e.g. Hibernate, etc.) to log sql:
How to view the SQL queries issued by JPA?
In addition, you can programatically ask the EntityManager if it is currently in a transaction, see isJoinedToTransaction().
Also..., make sure that the class with the methods that have the #Transactional annotation is a Spring Bean, otherwise the annotation does nothing.
In addition you have to tell Spring to enable transaction management. This post will help you understand how to do that: https://www.baeldung.com/transaction-configuration-with-jpa-and-spring

Spring JPA method name validator

Spring JpaRepository can generate SQL query from interface method name:
#Repository
#Transactional
public interface SomeEntityRepository extends JpaRepository<SomeEntity, Long> {
List<SomeEntity> findAllByEntityId(Long id);
List<SomeEntity> findAllByEntityIdAndDateOfCreationBetweenAnd/*...*/(Long id, /*...*/);
}
As you can see - it can be complicated. So it's possible that method name won't be correct and compiler errors occurs.
Question:
Is it possible to check/validate method name before using it in real project (which require some compile time, trial and error)?
Or maybe it is possible to convert such method name to SQL before usage - to see if this method name is valid and if it do all operation right?
Of course the obvious method is to make smaller project for test only. But I feel that it must be better way.
Thanks for any suggestions.
You can write a test using a test database and Spring test runner. There are multiple ways to do that, so I'll just give a rough sketch to get you started:
Instead of your real DataSource Spring bean, create a test DataSource bean using an in-memory (eg. H2) database
Create a Spring test context, that will:
discover your entities
set up the DB schema (eg. using hibernate.hbm2ddl.auto=create)
optionally pre-fill the DB with some data (eg. using hibernate.hbm2ddl.import_files)
enable and discover your Spring Data repositories
Use #RunWith(SpringJUnit4ClassRunner.class) to run your tests with JUnit.
Use #ContextConfiguration to point to your test Spring context
Use #Transactional to make the tests transactional and have any changes done in test methods rolled back automatically
#Autowire the EntityManager and the Spring Data repositories and use them in tests.
See Spring docs on Integration Testing.
A bit late to this, but....
If you want to test the name of the method without compiling the interface you can find a class called PartTree in org.springframework.data.repository.query.parser in the spring-data-commons:1.12.6 jar.
new PartTree(stringVersionOfMethodName,classOfEntityRepoIsFor)
It will throw an exception if it can't parse the name properly.
For maven I'm using spring-boot-starter-parent of 1.4.3.RELEASE and including the spring-boot-starter-data-jpa depedency

Disable/Ignore Transaction Interceptor at runtime

I am working on an application consisting of Spring and Hibernate frameworks. In one particular module, the application fetches the data from database (select queries). Along with the select queries, application also issues an update statement. After further debugging, I found that the update query is fired from some TransactionInterceptor.
I think, transaction interceptor is not required here as all are select queries. Can anyone please suggest me a way to disable/suppress this interceptor at runtime?
This problem might sound too abstract at first. However, I am new to this application and don't have much knowledge about it's architecture. If you need any configuration details, please let me know.
Thanks in advance.
Can you post your application-context.xml transaction management declarations part. Where the bean : org.springframework.jdbc.datasource.DataSourceTransactionManager is defined.
If the annotaion is not enabled you should activate it like this :
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="yourDataSource" />
</bean>
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" />
#Transactional(propagation = Propagation.NOT_SUPPORTED)
on your method will disable any Spring transactions on this proxy method call. Note that by disabling the transaction you also lose other benefits, like isolation.
However, the fact that you have an update query fired is NOT because of a transaction. You are likely to encounter a different error if you simply remove the transaction (likely stale object exception when hibernate tries to update outside of a transaction, or a malfunction of some module). Hibernate does not fire spurious updates, you should look for updates to the object in question during your transaction.
Here you have the JavaDoc of the interface org.hibernate.Session method clear() :
Completely clear the session. Evict all loaded instances and cancel all pending saves, updates and deletions. Do not close open iterators or instances of ScrollableResults
So when you use clear you will clear whole the Session. That ok, you will ask me : have I only one session per transaction ? I will answer you it's depends on your application HibernateTemplate configuration, if the HibernateTemplate.alwaysUseNewSession==true but the default value is false. The solution is to not intercepte your dao method with the Transaction Manager because it will be executed by default in a non Transactional Session.
Did you get a look to the Spring Framework AOP Proxy configuration. section 10.5 Declarative transaction management
I managed to suppress the update query by writing the following line in my DAO class (which was extending HibernateDAOSupport)
super.getSessionFactory().getCurrentSession().clear();
I just cleared the session as there was no update required while fetching the data and interceptor was updating the table.
Also, the original issue which I was facing is, the application was encountering org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1 from this update statement when it got executed twice (God knows why!).
Hence, we figured out that the update was never required and wanted to suppress it.
Can this fix have any consequences on the application? Is this the right way to do it? Will appreciate the inputs.
So your PlatformTransactionManager instance is HibernateTransactionManager. TransactionInterceptor will delegate the transaction handling to HibernateTransactionManager. All that means : all calls that you make to your data access methods annotated with #Transactional will be path throw spring AOP Proxy (which is a Proxy Design pattern).
If you don't use annotation-based and you have declared an AOP Proxy (search for aop:config tag in your ApplicationContext.xml).
So in the AOP Proxy configuration you will find the politic that your application use for intercepting data access methods and handling transactions.
For finding if you are using annotation-based you should know what is 'transactionAttributeSource' : AnnotationTransactionAttributeSource or AttributesTransactionAttributeSource ?

Categories

Resources