I have a requirement where i need to write to multiple DB. If any exception occurs while writing to anyone of the DBs, i want to rollback everything.
E.G.
Session userSession= a.getUserDBSession();
Session departmentSession= a.getDepartmentSession();
Session carSession= a.getCarSession();
//Do some work and write to User DB
// Do some work and write to Department DB
//Do some work and write to Car DB
// commit everything.
Note: Session is Hibernate Session
Any help would be highly appreciated
You can try using the Atomikos with hibernate.
Check the below link.
http://www.atomikos.com/Documentation/HibernateThreeStandalone
Related
I am working on an assignment to make the code transactional. I am having this problem about read-only transaction for while and none of a single suggestion I found on the internet didn't work. (Spring and hibernate integrated project)
This is my read-only transactional method
#Transactional(propagation=Propagation.REQUIRES_NEW, readOnly=true
,rollbackFor=Exception.class)
public void
editInternationalExportConsigment(InternationalExportConsignmentFormWrapper
exportConssi (){}
Inside this method, there is a translator process happening. Where the process fetch (select ) data from DB and set to an Object
Contact contact =inquiry.loadCustomerContactById(consignmentVO.getCustomerContactId().intValue());
if (contact != null && contact.getCity() != null) {
consignment.setOrgin(contact.getCity().getCountry());
consignment.setUniqueOriginCountry((contact.getCity().getCountry()!=null)?contact.getCity().getCountry().getId():null);
consignment.setOrginCity(contact.getCity());
}
There are no any update or insert query run in the middle, Only select. But end of the this code snippet execution it commit the data to DB (whatever the value set to setter method will persist into DB )
Can someone please tell me what wrong is happening here. your feedback will be much appricated.
After tedious research, I have found the answer. In our project there are two session factories are running. And also it uses spring OpenSessionInViewFilter to avoid 'lazy initializing' issue. OpenSessionInViewFilter has set flushMode to Auto. Since the OpenSessionInViewFilter keeping binding a hibernate session to the thread along in the entire process, it will override the transactional hibernate session object which gets creates once I start the new transaction.Therefore even if I kept the flushmode for transactional scope as 'COMMIT' it get override with the AUTO property by the properties declared OpenSessionInViewFilter.
When the flushMode is AUTO hibernate will flush dirty objects to DB.
Read this for understand hibernate data flushin
As a solution, I manually change the flush mode to 'COMMIT' inside my transactional method.
Thank you for everyone replied and commented. :)
I'm trying to do bulk/batch inserts using spring-batch.
public ItemWriter<MyEntity> jpaItemWriter() {
LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(ds);
builder.addAnnotatedClasses(MyEntity.class);
builder.setProperty("hibernate.show_sql", "true");
builder.setProperty("hibernate.batch_size", "20");
builder.setProperty("hibernate.order_updates", "true");
builder.setProperty("hibernate.order_inserts", "true");
HibernateItemWriter<MyEntity> writer = new HibernateItemWriter<>();
writer.setSessionFactory(builder.buildSessionFactory());
return writer;
}
Result:
I'm getting only single insert statements, not bulk inserts! I can see it from the logs both of hibernate + on postgresql level. Why is the bulk insert not working?
Update:
#Entity
public class MyEntity {
#Id
String shortname;
String fullname;
}
Spring has nothing to do with the SQL statements batching, its all managed by Hibernate.
I see you have batching enabled and configured properly, but that's not enough to make it work...you also need to use the right session type. in hibernate there are two session types: stateful session and stateless session.
The stateful session, which is obtained with
sessionFactory.openSession();
and also is used by default if using #Transactional, never uses batching (even if configured) and sends all SQL statements, at once, at transaction commit. However, you can simulate batching by calling flush() from time to time, and SQL statements will be sent to the db on every flush().
The stateless session, which is obtained with
sessionFactory.openStatelessSession();
respects the batching configuration, so just switch to stateless session, and batching will works as expected. Hibernate will log every session.insert(), but will not sent the SQL insert statement to the database, instead the SQL insert statements are sent as batches of configured size. So its best to "tail -f" the database log.
The main idea of having two session types is that, the stateful session uses cache, and every saved entity ends up in the 1st level cache, and therefore if you save 100k entities you will get OOM. The solution is to use stateless session which doesn't interact with any level cache.
You can read more about the stateless session.
Do you know how I can get XAResource that is automaticaly enlist to my transaction when I use my entity manager ?
I use Bitronix, JPA, hibernate, my code works fine, but I don't want to rollback all my XAResources if one specific failed. I just want to delist it from the current transaction and others will be commited.
But for delist it of the current transaction, I need the object XaResource and I don't know how i get it with JPA/Bitronix. example of code :
transactionManager.begin();
try {
(...)
EntityManager em = emf.createEntityManager();
(...)
em.close();
} catch (Exception e) {
// old version - rollback every XaResource in the current transaction
//transactionManager.rollback();
//new version wanted - rollbackonly this XaResource
transactionManager.getTransaction().delistResource(XaResource ...);
throw e;
}
transactionManager.commit();
Thanks for your help.
The A(tomicity) property of ACID doesn't allow such scenario. The transaction can only be successful if all data sources were able to commit.
I think the Command Pattern can help you with your problem. Let's say you want to update two data sources, and if one fails you still have control whether to undo the already executed commands.
If you never want to undo, then you don't need XA transactions. You just have to use Resource Local transactions, and if one data source fails, the rest could proceed.
we have a APP in Hibernate lately we start using Open Session in View in our DAO we fetch the data we really need.. we dont close the session but later in our controller in any operation on the Entity Hibernate is fetching the Data from the DB i know this behavior is the main reason to use open session but i dont need the fetch is some cases.. i was wondering if i can tell hibernate not fetch the data in some cases....
student.getSchool().getTeachers()
in this case i have load all the data i need from this 3 entitys but hibernate starts to load the school and the teachers again..
thanks a lot
Three main options:
If you use EH-Cache, you won't have to wait while Hibernate queries the database for Student & School again.
Or you could keep the form & Hibernate Session in the HTTP Session, which also achieves caching.
For AJAX or similar requests fetching just the Teachers, you could change the Criteria to Projection or use a Hibernate Query, to "project" or retrieve just the target entity.. at the database level, doing a joined or sub-expression query. Student would be loaded but only as a proxy, in this case.
In my application I open session, create criteria but dont close it. Then in other method I open session again, update object and receive database is locked on tr.commit().
If I put session.close() in first instance I receive
could not initialize proxy - no Session.
How do I close and open sessions correctly? Or do I need to copy proxy objects to those created by me and then close()?
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tr=session.beginTransaction();
Criteria criteria = session.createCriteria(MyDocument.class);
criteria.add(Expression.like("isMainDoc", 1));
List docs = criteria.list();
tr.commit();
session.close();
I am a complete begginer. i use sqlite. Any help would be appreciated. Thanks in advance.
Hibernate Session is generally tied to a thread.
So, perhaps you should restructure your code to get a session at the beginning of your processing (e.g. in ServletFilter instance of a web-app).
And then in each method, you can use the same session object, to start a new transaction (and then of course, end the transaction also.
public void doWork(){
Transaction tx = null;
try{
tx = session.beginTransaction();
}catch(){
} finally {
// if tx != null then.. close transaction, or rollback?
}
}
EDIT: And then ofcouse, close the session when the processing is done (in web-app, that could be also in the same ServletFilter)
Google: "Open Session In View" pattern.
Cause
You might be getting the error when you are trying to access properties of the MyDocument class instances returned by the query.
Hibernate is lazy by default. It returns you a proxy for an object instead of hitting the database whenever a reference property is accessed. This behavior can be overwritten whenever required.
Always remember that could not initialize proxy - no Session is recieved when the code tries to access a proxy properties (by hitting the database) and finds that the session is not available ( Session is needed as Hibernate accesses database using this interface)
Solution
Make sure that your session is open whenever Hibernate tries to load object which have not been loaded yet. How do you do that?
(In simple words) There are two schools of thoughts in Hibernate:
Fetch all the data that you might access before you close the Session OR
keep the Session open for the entire duration of time you work on the objects.
I would like you brush up topics such as the unit of work in Hibernate. Hibernate provides a wonderful interface to define boundaries on database access. Data must be accessed (read/written) between these boundaries. Check Here
hibernate.current_session_context_class in the hibernate configuration which can take the values jta | thread | managed | custom.Class. This variable defines the unit of work for your Session.
Last but most importantly try using Contextual Sessions (you must have come across .getCurrentSession()
which helps you to get the same session which is open everytime anywhere in your code. Hibernate handles everything behind the scenes.
Hope this answer serves as a guide for you for taking the correct path in using Hibernate rather than just solving this particular problem.
Follow the below steps when you are using hibernate transactions Read the API here.
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
//Or any other operation.
session.save(a);
tx.commit();
session.close();