I'm currently developping an application in java using Hibernate as a persistence manager and JPA as an abstraction of the persistence manage hibernate.
I'd like to know the impact of wrapping a result query around a transaction. I know the entity manager must stay open for lazily fetched field bug what about transaction in all this ?
Here is a code example with transaction activation/desactivation ability.
public List<Exportdata> get(Integer max, EntityManager em, Boolean withTransaction) {
EntityTransaction tx = null;
try {
if (withTransaction) {
tx = em.getTransaction();
tx.begin();
}
Query query = em.createQuery("from Exportdata");
query.setMaxResults(10);
List<Exportdata> list = query.getResultList();
if (withTransaction)
tx.commit();
return list;
} catch (RuntimeException re) {
if (withTransaction)
if (tx != null && tx.isActive())
tx.rollback();
throw re;
}
}
What is the difference between enabling or disabling withTransaction when this function is called ?
Thanks all,
Fred
There is no practical difference here, since you aren't changing any data. The query you execute will generate an SQL select. Transactions are there to allow you to apply ACID properties to a collection of inserts, updates, etc.
However, if you begin manipulating the objects in the list returned from this method, calling setters, etc. those changes will be propagated back to the database out-with a transaction on an ad-hoc basis. In other words you'll effectively be working with the db in auto-commit mode. This is unlikely to be what you want.
The important thing to understand is that the duration of a persistence context and a transaction can be managed separately. Often though you would want to manage them together.
Related
The following SQL if run in MSSQL will insert the 1st and 3rd rows successfully:
BEGIN TRAN
INSERT ... -- valid data
INSERT ... -- invalid data (e.g. over column width)
INSERT ... -- valid data
COMMIT
Even though the second row fails within the transaction, you can still see the two rows with some valid data after the commit in the table.
However, when trying something similar in Hibernate, it rollbacks the whole transaction. Is there a way to tell Hibernate not to rollback on failed rows and commit the rest as same as how MSSQL does it?
e.g.
EntityTransaction transaction = em.getTransaction();
transaction.begin();
em.persist(new MyEntity("good"));
em.persist(new MyEntity("too long"));
em.persist(new MyEntity("good"));
transaction.commit();
This is not possible within the same transaction. Hibernate simply doesn't allow this. An error in a statement leads to an exception, which Hibernate cannot recover from. From the manual:
If the JPA EntityManager or the Hibernate-specific Session throws an exception, including any JDBC SQLException, you have to immediately rollback the database
transaction and close the current EntityManager or Session.
Certain methods of the JPA EntityManager or the Hibernate Session will not leave the Persistence Context in a consistent state. As a rule of thumb, no exception thrown by Hibernate can be treated as recoverable. Ensure that the Session will be closed by calling the close() method in a finally block.
Now this is a restriction (design decision) of Hibernate and not of the underlying JDBC or database stack. So what you want is perfectly possible using JDBC directly. If it is really important for you to get that behaviour, you might consider using JDBC calls for this section of the code. There you can do it exactly like in the SQL client: open transaction, issue statements, catching any exceptions manually and "ignoring" them, and at the end committing the transaction.
Example code:
Session session = em.unwrap(Session.class);
session.doWork(connection -> {
// manual commit mode
connection.setAutoCommit(false);
executeInsertIgnoringError(connection, new Object[]{123, null, "abc"});
executeInsertIgnoringError(connection, new Object[]{....});
...
connection.commit();
});
private void executeInsertIgnoringError(Connection connection, Object[] values) {
try (PreparedStatement stmt =
connection.prepareStatement("INSERT INTO MY_ENTITY VALUES (?, ?, ?, ...)")) {
for (int i = 0; i < values.length; i++) {
// PreparedStatement is indexed from 1
stmt.setObject(i+1, values[i]);
}
stmt.executeUpdate();
} catch (Exception e) {
log.warn("Error occurred, continuing.");
}
}
The way i did it is to divide your logic into diferent functions, and open the transaction inside the persisting function instead of the main one.
The main problem I see in your code is that you're defining a block transaction insead of opening a transaction for each operation.
Here's my snippet:
persistEntity(new MyEntity("good"));
persistEntity(new MyEntity("bad"));
persistEntity(new MyEntity("good"));
...
private void persistEntity(MyEntity entity){
EntityTransaction transaction = em.getTransaction();
transaction.begin();
em.persist(entity);
transaction.commit();
}
This way it will rollback just for the bad entity and keep going with the other. You can also add a try catch inside the persistEntity method, if you want to log the exception.
Fun fact, If you're using Spring you could create another #Component for the persist operations and only add #Transactional to the persisting method, this way you don't have to manage the transactions yourself.
Don't do so, that is idiomatically wrong, at first just review the real scope of your transactions.
You could write the code to run one statement at a time with autocommit on and not use #Transactional... Then perhaps catch any exceptions and throw them away as you go. But pretty much everything in that sentence is troublesome to even think about as a responsible developer and it would affect your entire app. Flavius's post would be a little more granular in doing something similar with explicitly smaller transactions and is a good way to go about it too.
As others have been commenting it's not a long term great plan and goes against so many ways to write programs correctly and the benefits and purpose of transactions. Perhaps if you plan to only use this as a one off data ingestion plan you could but again be very wary of using these patterns in a production grade app.
Having been sufficiently alarmed, you can read more about auto commit here and also be sure to read through the post links on why you probably shouldn't use it.
Spring JPA - No transaction set autocommit 'true'
You can do that by adding below property in hibernate config xml file
<property name="hibernate.connection.autocommit" value="true"/>
If you could use #Transactional annotation then
#Transactional(dontRollbackOn={SQLException.class, NOResultException.class})
Then I would suggest one some change in your code. It's better if you add your entities in a loop and catch exception on each transaction.
While streaming over a "data provider" I need to insert a fairly large number of entities in the database, say around 100.000. This whole step needs to be transactional.
To simplify my use-case as much as possible let's assume this is my code:
#Transactional
public void execute() {
for (int i = 0; i < 100000; i++) {
carRespository.save(new Car());
}
}
The problem with this code is that even if it's clear i have no use for the Car entities after the insert query is generated the entity is attached to the Persistence Context and held in memory until the transaction is done.
I would like to make sure that in case the garbage collection is triggered the created entities are cleared. For this currently I see two solutions:
create a native insert query on the repository
Inject the EntityManager in the service and call em.detach(car) after every insert
I tend to prefer the second option as I would not have to manage the native insert statement as the entity changes.
Can you confirm I taking the correct approach or suggest a better alternative?
You can find in the Hibernate documentation the way to insert the batch of data.
When making new objects persistent flush() and then clear() the session regularly in order to control the size of the first-level cache.
Thus the following approach is recommended:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
session.save(new Car());
if (i%20 == 0 ) {
session.flush();
session.clear();
}
}
tx.commit();
session.close();
You can try using the saveAndFlush(S entity) method from spring data JPA JpaRepository instead of save()
I've always used transactions when querying the database, but recently i wondered why.
What are the benefits / drawbacks of using / not using transactions on a "read-only" query?
Transactional:
public int count() {
PersistenceManager pm=pmf.getPersistenceManager();
JDOTransaction tx=(JDOTransaction)pm.currentTransaction();
try{
tx.begin();
Query query=pm.newQuery(class);
query.setResult("count(this)");
Long count=(Long)query.execute();
query.closeAll();
tx.commit();
return count.intValue();
}finally{
if (tx.isActive()) tx.rollback();
pm.close();
}
}
Non-transactional:
public int count() {
PersistenceManager pm=pmf.getPersistenceManager();
try{
Query query=pm.newQuery(class);
query.setResult("count(this)");
Long count=(Long)query.execute();
query.closeAll();
return count.intValue();
}finally{
pm.close();
}
}
What puzzles me is, for example, Datanucleus JDO implementation. if transactions do not lock the objects by default, what's the benefit of such transaction?
From the docs: JDOQL allows control over whether objects found by a query are locked during that transaction so that other transactions can't update them in the meantime: http://www.datanucleus.org/products/accessplatform_2_1/jdo/jdoql.html
That depends. If you have only an atomic read they are probably not needed.
However, If you want to read more than one value, possibly from different tables, possibly the choice depends on the result of the first query, transactions might help you:
You might not want that the database changes while you perform your read-only query.
Transactions can provide isolation, such as guaranteeing that the data does not change during the transaction.
To also mention drawbacks: Transactions are a performance hit.
I'm using a hibernate session per request model for my web application. My jdbc transaction begins at the beginning of each web request and commited at the end.
// Non-managed environment idiom
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();
// do some work
...
tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // or display error message
}
finally {
sess.close();
}
I'm faced with the problem where I am testing for existence of an entity (A) based on several parameters and doing an insert only if it doesn't exist.
public synchronized myMethod(param1, param2) {
MyEntityA entity = MyEntityADAO.findEntity(param1, param2)
if (entity == null) {
entity = .../create entity
MyEntityADAO.save(entity);
}
}
the problem is that synchronization does not help because the call to MyEntityADAO.save() does not actually write to the database when the currently running thread exits the method and releases the lock, the write to the database occurs after the transaction is commited which is generally what I need for my application except for a few scenarios. The code above causes multiple records saved with same parameters in a multithreaded environment.
I've tried to execute the save code in its own new session and transaction:
public synchronized myMethod(param1, param2) {
MyEntityA entity = MyEntityADAO.findEntity(param1, param2)
if (entity == null) {
entity = .../create entity
Session session = HibernateUtil.createSession();
MyEntityADAO.save(entity);
Transaction t = session.beginTransaction();
}
}
the above causes problems with 2 open sessions loading the same collection with hibernate in some instances.
Should I enclose every DAO call in its own transaction and use transaction propagation with JTA? Is there a way to avoid JTA? Is it alright to commit transaction associated with the main session after the call to MyEntityADAO.save() and call beginTransaction on the main session right after and have the transaction commited at the end of the request as it does now?
The coherence of the data in database should not be compromised by doing only some part of an atomic change in its own transaction. And although some synchronization might work on your environment, if you need to cluster your app, or if several applications acces the database, it won't solve the problem.
What you should do is to put a unique constraint in the database on [param1 - param2]. That will cause one of the two transactions to rollback if there is a race condition.
If you choose to still isolate the check/insert code in its own transaction (because it's not a problem if that succeeds and the outer transaction fails), I don't see how JTA would be a problem. Supposing you're using EJBs or Spring, just put this method in its own EJB/bean, and mark the method as transactional, with the REQUIRES_NEW propagation.
The code would thus look like this:
// some code
Long id = myBean.checkIfExistOrCreate(param1, param2); // this methos call starts a new transaction
// now we're sure that the entity exists. Load it in the current session.
MyEntity e = em.find(MyEntity.class, id);
If you can't synchronize checkIfExistOrCreate, then try calling it, catch any exception that it could throw, and retry calling it:
Long id = null;
try {
id = myBean.checkIfExistOrCreate(param1, param2);
}
catch (Exception e) { // a well-defined exception would be better
// the transaction roled back: retry
id = myBean.checkIfExistOrCreate(param1, param2);
}
// now we're sure that the entity exists. Load it in the current session.
MyEntity e = em.find(MyEntity.class, id);
The solution that worked for me and my particular app requirements trying to avoid JTA and nested transactions:
Using ManagedSessionContext because org.hibernate.context.ThreadLocalSessionContext will close and create a new session for each transaction. You will run into problems with entities that have collections associated if you load those entities in multiple open sessions (when you will create multiple transactions for one request).
I open a hibernate session and bind it to the context in the beginning of my web request
Any service layer method that needs test for existence prior to insert is marked synchronized, the global transaction is commited with the insert statement and a new transaction is started
At the end the request the transaction bound to the session is commited
public synchronized myMethod(param1, param2) {
MyEntityA entity = MyEntityADAO.findEntity(param1, param2)
if (entity == null) {
entity = .../create entity
MyEntityADAO.save(entity);
HibernateUtil.getCurrentSession().getTransaction().commit();
HibernateUtil.getCurrentSession().getTransaction().begin();
}
}
I know its ugly and will not work for everybody in every scenerio, but after doing a very intense search on transaction management, isolation levels, locking, versioning that is the only solution I have found that worked for me. I am not using Spring, and I'm not using a Java EE container, using Tomcat 6.
I wrote the below code to retrieve data from the data base, in that do we need to begin the transaction? Because it runs without any issue. Is it necessary to use it every time? Will it cause any problem in future without that?
public static Student getStudentById(long id) {
Session session = null;
Student student = null;
//Transaction transaction=null;
try {
session = HibernateUtil.getSessionFactory().openSession();
//transaction = session.getTransaction();
//transaction.begin();
/**
* names in the query should match the related class name and variable names.
*/
Query query = session.createQuery("from Student where studentId = :id");
query.setLong("id", id);
student = (Student) query.uniqueResult();
//transaction.commit();
} catch (HibernateException e) {
//transaction.rollback();
e.printStackTrace();
} finally {
session.close();
}
return student;
}
According to hibernate documentation Database, or system, transaction boundaries are always necessary. No communication with the database can occur outside of a database transaction (this seems to confuse many developers who are used to the auto-commit mode). Always use clear transaction boundaries, even for read-only operations. Depending on your isolation level and database capabilities this might not be required, but there is no downside if you always demarcate transactions explicitly. Certainly, a single database transaction is going to perform better than many small transactions, even for reading data.
You can refer hibernate documentation here.
No, you don't need to use transaction unless and until you are planning to persist the data inside the db. And In your question you are not persisting the date you are just fetching the records from the db. So here not mandatory to use transaction.