I have a service, which uses multiple DAOs to do a unit of work like following. The DAOs fetch the data/save by creating a session and commit. In that case, why do i need to have transactional on the service when the dao is managing the session by itself? If something goes wrong in the second DAO, the data in the first DAO is already commited, how do I get around this situation?
#Transactional
public boolean testService(SomeObject obj){
userDao.save(obj.getUser()); // this dao gets the session from sessino factory and commits there itself.
addressDao.save(obj.getAddress()); // something fails here, and the above line has already commited the data, so the data is not Atomic.
return true;
}
Usually you don't put #Transactional in your DAO layer but in your service layer.
So I suggest you remove #Transactional from you DAOs and keep the one in your service.
In that case, if something goes wrong in any of your DAO everything will be rollback.
But It's up to you where you want the transaction to start and to finish.
Related
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.
I have the need to insert 2 different entities into 2 different tables, using the same transaction. If the second insert fails, the first one should be rolled back.
Is there any way of doing this nicely?
Pseudo code:
start tx
repo1.save(myEntity);
repo2.save(anotherEntity);
try commit
I know you can leverage #Transactioal but only on method level?
you need check that you don't have set autocommit = false.
wrap save operations into one service method and make it #Transactional. But if you use save() custom method check that save in not marked as #Transactional with propagation level required_new or nested. If you need you can use REQUIRES_NEW for saving service method to make this service method transaction independent of other transactions.
also you can wrap in with TransactionTemplate.
#Autowired
private TransactionTemplate transactionTemplate;
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
public void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
repo1.save(myEntity);
repo2.save(anotherEntity);
});
It is usually a wrong idea to have #Transactional declared around repository methods.
Repositories are only for you to access domain entities. Business logic normally involves multiple domain entities and collaborations between them.
In your architecture you should have a layer to compose business logic. This usually corresponds to a service exposed to external.
This is usually the place you should have your transaction boundary set on. Usually it is a Controller, or a Service method.
Steps to follow:
Ensure you use Interfaces for repo1 and repo2 methods as spring transactions works on proxy. ( If you use only classes, then you may need to add few other dependencies).
Annotate repo1.save(..) and repo2.save(..) with #Transactional(propagation=Propagation.REQUIRED) annotation.
Call repo1.save() from any method outside of class.
Unit test the code properly using special junit runner.
The situation is as follows:
Method1 has four database update methods in it. The Method1 is annotated using the Spring transaction management semantics.
Method2 has a database read method in it and it is invoked after Method1 has finished executing all its database updates. Method2 is also annotated using the Spring transaction semantics.
There's a web request that comes in, the controller intercepts the request and invokes method1 and then method2.
A transaction is wrapped around the web-request as well.
What I am interested in knowing is:
1.How does Spring know to commit the database updates upon a successful transaction? Is there some reference to the Spring implementation that does the transaction management?
2.Since we have a hierarchy of transactions:
Transaction around the web-request->Transaction with Propagation=RequestNew for Method1->Transaction with Propagation=Required for Method2, how does Spring do the transaction management to ensure the transactions are executed within the proper context with the right order?
In short, it will be great to get a play by play account of how Spring performs the transaction management in all its grittiest details or a reference to documentation that doesn't simply hand-wave an explanation centered around JTA or some other acronym.
Thanks
Lets make some basic statements.
A transactional context is an environment where some special properties (database session) are made available to the application runtime which are otherwise not available. A Transaction Context is generally used to scope a transaction.
Spring uses, AOP Proxies and XML metadata to achieve a Declarative transaction management.
Annotations are used to mark the Transaction Propagation behavior of a particular method.
Spring uses Interceptor Mechanism to apply the transaction over the methods.
Here I am reusing the example give by #stacker above
MyClass{
#Transactional
public void sequence() {
method1();
method2();
}
#Transactional
void method1() {
}
#Transactional(propagation=Propagation.REQUIRES_NEW)
void method2() {
}
}
You can also achieve the same functionality using xml configuration as well. Lets take this as its popular and widely used.
At the time of deployment
Spring framework checks the xml configuration files (famed applicationContext.xml) and depending on the configuration, scans the code for #Transactional annotation (assuming that the configuration is mentioned as annotation based).
After this, it generates AOP proxies for methods marked for transaction. In simple terms, these proxies are nothing but wrapper around the concerned methods.
Within these wrapper methods, before and after a Transaction Advisor code is also generated depending on the configuration (namely the transaction propagation).
Now when these wrapper methods are invoked Transaction Advisor comes into picture before and after the actual method call. .
Representing the same in pseudo code for the example above
ProxyMyClass{
MyClass myclass;
.
.
.
sequence(){
//Transaction Advisor code (Typically begin/check for transaction)
myclass.sequence();
//Transaction Advisor code(Typically rollback/commit)
}
.
.
.
}
This is how spring managers the transaction. A slight oversimplification though.
Now to answer your questions,
.How does Spring know to commit the database updates upon a successful transaction? Is there some reference to the Spring implementation that does the transaction management?
Whenever you call a method under transaction, you actually call a proxy which first executes the transaction advisor (which will begin the transaction), then you call the actual business method, once that completes, another transaction advisor executes (which depending on way method returned, will commit or rollback transaction).
Since we have a hierarchy of transactions: Transaction around the web-request->Transaction with Propagation=RequestNew for Method1->Transaction with Propagation=Required for Method2, how does Spring do the transaction management to ensure the transactions are executed within the proper context with the right order?
In case of transaction hierarchy, the spring framework generates the Transaction Advisor checks accordingly. For the example you mentioned,
for method1 (RequestNew ) Transaction Advsor code (or transaction Advice) would be to create a new transaction always.
for method2 (Required ) Transaction Advisor code (or transaction Advice) would be check for existing transaction and use the same if it exists or else create a new transaction.
There is an image on the spring documentation page which very nicely summarizes these aspects.
Hope this helps.
Controller
#Transactional
public void sequence() {
method1();
method2();
}
#Transactional
void method1() {
}
#Transactional(propagation=Propagation.REQUIRES_NEW)
void method2() {
}
The default propagation is REQUIRED (Support a current transaction, create a new one if none exists.) Therefore m1 will use the Transaction started in the Controller. m2 is annotated as REQUIRES_NEW ( Create a new transaction, suspend the current transaction if one exists.) The order of the transaction is the order you call the transactional methods.
Controller
begin tx1
|--------------------> m1 (uses tx1)
|
| begin tx2
|--------------------> m2 (uses tx2)
| commit tx2
commit tx1
Have you read the Spring documentation? Basically AOP is used to manage the transaction. You should also read the AOP documentation. If the AOP documentation is not enough I suggest you go through the code. Stepping through the code in debug mode with break-point would be good.
I am using following code
TestDAO {
Session session = null;
public TestDAO() {
this.session = HibernateUtil.getSessionFactory().getCurrentSession();
}
//...more code create,update ...
//each method starts a transcation using "tx= session.beginTransaction();"
}
1)Now should i commit the transcation using tx.commit for a fetch operation too or only for save/update operation??
2)Should i create a seperate instance of TestDAO every time i need?Or should i create a singleton class that returns a single instance of DAO everytme?Will this have a problem?
You don't need tx.commit() for fetch operation. That is only needed for any save, update or delete. Close the session after data fetching.
If your application connect to only one database then use of single DAO is better. Spring framework encourages this. You will find more details about this on the following link
Don't repeat the DAO!
Transactions should not be the responsibility of the DAO, those really need to be controlled at a higher level. A DAO should be something that does queries and updates without being aware of the bigger picture, calls to DAOs can be grouped within an object like a Spring service or EJB session bean which is responsible for deciding what needs to go together in a transaction. This makes your DAO code more reusable since it doesn't have to know as much about the context in which it's operating.
Look at how Spring does it (in the sample applications like petstore that come with Spring), or better, look at the King/Bauer Hibernate-JPA book, which has a chapter on creating DAOs.
I'm playing around with Spring + Hibernate and some "manual" transaction management with PostgreSQL
I'd like to try this out and understand how this works before moving to aop based transaction management.
#Repository
public class UserDAOImpl extends HibernateDaoSupport implements UserDAO {
#Override
public void saveUser(User u) {
Transaction tx = getSession().beginTransaction();
getHibernateTemplate().saveOrUpdate(u);
tx.rollback();
}
}
Calling saveUser here, I'd assume that saving a new User will be rolled back.
However, moving to a psql command line, the user is saved in the table.
Why isn't this rolled back, What do I have to configure to do transactions this way ?
Edit; a bit more debugging seems to indicate getHibernateTemplate() uses a different session than what getSession() returns (?)
Changing the code to
Transaction tx = getSession().beginTransaction();
getSession().persist(u);
tx.rollback();
and the transaction does get rolled back. But I still don't get why the hibernateTemplate would use/create a new session..
A couple of possibilities spring to mind (no pun intended):
a) Your JDBC driver defaults to autocommit=true and is somehow ignoring the beginTransaction() and rollback() calls;
b) If you're using Spring 3, I believe that SessionFactory.getSession() returns the Hibernate Session object wrapped by a Spring proxy. The Spring proxy is set up on the Session in part to handle transaction management, and maybe it's possible that it is interfering with your manual transaction calls?
While you can certainly use AOP-scoped proxies for transaction management, why not use the #Transactional(readOnly=false|true) annotation on your service layer methods? In your Spring config file for your service layer methods, all you need to do to make this work is to add
<tx:annotation-driven />
See chapters 10 and 13 of the Spring Reference Documentation on Transaction Management and ORM Data Access, respectively:
http://static.springsource.org/spring/docs/3.0.x/reference/index.html
Finally, if you're using Spring 3, you can eliminate references to the Spring Framework in your code by injecting the Spring-proxied SessionFactory bean into your DAO code - no more need to use HibernateDaoSupport. Just inject the SessionFactory, get the current Session, and use Hibernate according to the Hibernate examples. (You can combine both HibernateDaoSupport and plain SessionFactory-based Hibernate code in the same application, if required.)
If you see the JavaDoc for HibernateDaoSupport.getSession() it says it will obtain a new session or give you the one that is used by the existing transaction. In your case there isn't a transaction listed with HibernateDaoSupport already.
So if you use getHibernateTemplate().getSession() instead of just getSession(), you should get the session that is used by HibernateTemplate and then the above should work.
Please let me know how it goes.
EDIT:
I agree its protected...my bad. So the other option then is to keep the session thread bound which is usually the best practice in a web application. If HibernateDaoSupport is going to find a thread bound session then it will not create a new one and use the same one. That should let you do rollbacks.