JDO Query inside transactions: yes or no? - java

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.

Related

How to make Hibernate not to rollback when an exception occurs

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.

Insert large number of rows without attaching the entities to the Persistence Context

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

Working of hibernate. Is it necessary to use transaction every time? Will it cause any issue if i dont use it while retieving data?

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.

Hibernate pattern for transaction reuse

I have a base method that I'm writing in order to not repeat the same hibernate session/transaction logic over and over. It's fairly simple, but there's a specific issue that I'm not sure can be solved with this approach.
Imagine that you have a User entity and a Permission entity. If a request is made to save a user along with its matching permissions, then I think that it would make sense to perform both operations in a single transaction, since being able to save only one of those entities could be considered data corruption. For example, failing to save the user's permissions would warrant a rollback on previously inserted user data.
I made the following method to allow generic hibernate operations that could work with the current transaction if it were necessary, although I now think that in its current form it won't work since calling session.beginTransaction(); will probably return a new transaction even if the previous hasn't been commited (is this the case?). Suppose that I changed it in order to have it return the current session and transaction if it was specified that there would be more operations for the current transaction, do you think it would work? Would it be advisable to do something like this, or would you recommend a change of approach? Thanks
protected <T> void baseOperation(Class<T> entityClass, List<T> instances, BaseHibernateDAO.Operations operation, boolean isLastOperation) throws Exception
{
Session session = null;
Transaction transaction = null;
boolean caughtException = false;
//get session from factory
session = HibernateSessionFactory.getSession();
try
{
//get current transaction
transaction = session.beginTransaction();
for (Object instance : instances) //perform operation on all instances
{
log.debug(String.format("Will perform %s operation on %s instance.", operation.name(), entityClass.getName()));
switch (operation) //perform requested operation
{
case SAVE:
session.save(instance);
break;
case UPDATE:
session.update(instance);
break;
case SAVEORUPDATE:
session.saveOrUpdate(instance);
break;
case DELETE:
session.saveOrUpdate(instance);
break;
}
log.debug(String.format("%s operation on %s instance was succesful.", operation.name(), entityClass.getName()));
}
session.flush(); //synchronize
if (isLastOperation) //if this is the last operation of the transaction
{
transaction.commit();
log.debug("Transaction commited succesfully.");
}
}
catch (Exception e) //error occurred
{
caughtException = true;
//roll-back if transaction exists
if (transaction != null)
{
transaction.rollback();
}
//log and re-throw
log.error("An error occurred during transaction operation.", e);
throw e;
}
finally //cleanup tasks
{
if (isLastOperation || caughtException) //close session if there are no more pending operations or if an error occurred
{
HibernateSessionFactory.closeSession();
}
}
}
"Advisable" would be to stop trying to rewrite code that's already been written, debugged, dragged through the mud, debugged more, and deployed thousands of times. I.e, the issues and considerations you're encountering have been encountered and overcome before, and the solutions are proven. Further, having been extensively used and improved, they require much less effort to use than what you're putting into your custom solution. Check out Spring's Hibernate support, especially "Implementing DAOs based on plain Hibernate 3 API" and "Declarative transaction demarcation". For further reading, there's a whole chapter on transaction management.
I have a sample project on github where you can see a very simple example of using Spring to manage Hibernate Sessions and transactions in the context of a webapp (using Spring MVC).
Update: For those who come along later, so they don't have to dig through the comments:
There are three general ways to use Spring's transaction handling: declaratively defining which methods are transactional with XML, declaratively annotating methods as #Transactional, or programmatically using TransactionTemplate.

How JPA (Hibernate) deal with transaction when fetching Object from database

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.

Categories

Resources