JTA(Bitronix)/JPA/Hibernate - How I can get XAResource from an Entitymanager? - java

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.

Related

Does Session.getCurrentSession() closes without doing any transaction?

I am trying to load entity by doing this:
public void reloadRepository() {
Session session = getSessionFactory().getCurrentSession();
session.beginTransaction();
Hibernate.initialize(Repository.class);
}
From this stack overflow post (Hibernate openSession() vs getCurrentSession()), it says
When you call SessionFactory.getCurrentSession, it creates a new
Session if it does not exist, otherwise use same session which is in
current hibernate context. It automatically flushes and closes session
when transaction ends, so you do not need to do it externally.
What does it mean by "transaction ends"? If I don't make any transaction (guessing Hibernate.initialize() is not making transaction), does hibernate close this session?
Probably.
I'm guessing you set current_session_context_class to thread (since you're using beginTransaction). This means that, according to the javadoc, the session is only usable after transaction is started and is destroyed automatically when transaction ends.
I'm not sure what you mean by 'not making any transaction', you just made one using beginTransaction(). Once you commit or rollback, the transaction will end. Even if you do neither, the transaction will eventually time out,and that will also count as ending the transaction.
It's written like that because in modern apps you control transactions with the #Transactional annotation. You simply put it on top of the service methods and Hibernate opens a transaction automatically and closes it when it reaches the end of the method.
I don't really know what you think your last row of code is doing but it looks very wrong. If you want to load an entity you can simply write session.get(), add #Transactional to your method and delete session.beginTransaction() and Hibernate.initialize().

Write to multiple DB in single transaction in hibernate

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

Resuming a global transaction in JTA

I have a little C program that connects to Oracle using OCI. I can start global transactions, suspend them, resume the same transaction in a different process, suspend again, commit back in the first process - all works fine. What I'd like to be able to do is inside my global transaction make a call to a Java app server, passing my Xid as a parameter, and have the web call do some work as part of the global transaction.
I've set up Glassfish 4.1 and I'm using eclipselink as the JPA implementation, but I don't see how I can use the passed in Xid to resume the original transaction. I'm guessing I need to use application managed persistence, so I have some code that looks like:
if (emf == null){
emf=Persistence.createEntityManagerFactory("xaPU");
}
EntityManager em = emf.createEntityManager();
TransactionManager tm = (TransactionManager)ctx.lookup("java:appserver/TransactionManager");
I can then do:
tm.begin();
em.joinTransaction();
em.persist(data);
tm.commit();
And all the data is written to the DB but obviously this is done in a new transaction. If I get the transaction with:
Transaction tx = tm.getTransaction();
and look at tx in the debugger it seems to have a Xid set, but I don't see how I can set this Xid, or associate it with the Xid I pass in from my C program.
Am I on the right tracks with this, or is there an easier way to do this?

Manual Rollback of transactions in Seam

This is a similar problem to Forcing a transaction to rollback on validation error
The scenario is this:
A user edits a page, the transaction is set to MANUAL so only if we call flush it would be committed to the database. Now the user wants to cancel the changes. Easy as you haven't flushed it yet.
Now consider this scenario: User edits page with lots of ajax on there. Some of these ajax callbacks require database queries (e.g. using a richFaces suggestion box etc). Some validation is done as well that requires database lookups. The problem is Hibernate will automatically issue a flush when you do a query. So the user doesn't press the save button (which would flush the transaction) he presses the cancel button. What do you do now?
If you don't do anything the changes will be written to the database - not what the user expects.
You can throw an exception that is annotated with
#ApplicationException(rollback=true)
That would rollback the transaction. You can then redirect to another page. However here I've come across another problem, on some pages you redirect to you get a lazy initialisation exception. I've specified
<exception class="com.mycomp.BookingCancelException">
<end-conversation before-redirect="true"/>
<redirect view-id="/secure/Bookings.xhtml">
<message severity="INFO">#{messages['cancel.rollback']}</message>
</redirect>
</exception>
in pages.xml, so the conversation should end before we are doing the redirect. A new conversation should start (with a new transaction) but that doesn't seem to happen in all cases? Why?
I've read somewhere else that you can simply use
Transaction.instance().rollback();
This would be preferable as you don't have to go via exceptions (the redirect always takes long when Seam handles exceptions) but the problem is that the Transaction isn't actually rolled back. I couldn't figure out why. If I check the status of the transaction it says that it is not in rollback state.
How would you best handle Cancel requests. The pure MANUAL flush doesn't work in this case. You could work with detached entities but the page contains several linked entities so this is getting messy.
Update: I've now discovered that throwing the ApplicationException doesn't rollback the transaction in all cases. So rather confused now.
Update 2: Of course rolling back transactions will not work when you have an page where you use ajax to update values. Each transaction only covers one request. So if you do e.g. 5 edits with ajax request, rolling back a transaction will only roll back the changes from the last ajax request and not from the earlier 4 ones.
So solution is really to use the flush mode MANUAL.
There are a few things that will cause a flush even if you specify MANUAL.
a query in an ajax request can trigger a flush - Use setFlushMode(FlushMode.COMMIT) on the query to avoid this.
Persisting an entity can trigger a flush depending on the ID generation used (e.g. if you use the strategy IDENTITY). You can work around this by using Cascades. If you need to create entities during the edit which don't have any real relationship with the main entity your are editing just add them to a list and persist all entities in that list when you do a save.
When you start a nested conversation or another bean joins the conversation the Flush Mode on that session is set back to AUTO when you don't specify #Begin(join=true,flushMode=FlushModeType.MANUAL)
You might want to specify MANUAL as the default mode in components.xml
<core:manager concurrent-request-timeout="10000"
conversation-id-parameter="cid" conversation-timeout="600000" default-flush-mode="MANUAL"/>
Have you tried
#Begin(flushMode=MANUAL)
someRandomValidationMethodHere(){ ... }
or setting
<core:manager conversation-timeout="120000" default-flush-mode="manual" />
in components.xml?
It might be helpful to know that you can tell the ajax validation components not to flush, using the attribute bypassUpdates. Here's an article explaining it.
You can manage your transaction and manage it with the button activated.
protected void doGet(HttpServletRequest request,
HttpServletResponse response, String butonPressed)
throws ServletException, IOException {
try {
// Begin unit of work
HibernateUtil.getSessionFactory()
.getCurrentSession().beginTransaction();
} catch (Exception ex) {
HibernateUtil.getSessionFactory()
.getCurrentSession().getTransaction().rollback();
throw new ServletException(ex);
}
finally
{
if ("SAVE".equals(butonPressed))
{
// Process request and render page...
// End unit of work
HibernateUtil.getSessionFactory()
.getCurrentSession().getTransaction().commit();
}
else
{
HibernateUtil.getSessionFactory()
.getCurrentSession().getTransaction().rollback();
}
}
}

Hibernate error "database is locked". How do i correctly close session?

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();

Categories

Resources