We went ahead and tried HibernateExtendedJpaDialect just to notice that randomly entity persistence would fail with:
WARN [org.hibernate.util.JDBCExceptionReporter] - SQL Error: 0, SQLState: S1009 ERROR [org.hibernate.util.JDBCExceptionReporter] - Connection is read-only. Queries leading to data modification are not allowedWhen you compare the code of the two classes you can identify extra cleanup which is essential to avoid leaving existing connections in a dirty state. This is very important especially for pooled connections of course:
package com.sample.utils.jpa;
import java.sql.Connection;
import java.sql.SQLException;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import org.hibernate.Session;
import org.hibernate.jdbc.Work;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.orm.jpa.vendor.HibernateJpaDialect;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
public class HibernateExtendedJpaDialect extends HibernateJpaDialect {
private Logger logger = LoggerFactory.getLogger(HibernateExtendedJpaDialect.class);
/**
* This method is overridden to set custom isolation levels on the connection
* @param entityManager
* @param definition
* @return
* @throws PersistenceException
* @throws SQLException
* @throws TransactionException
*/
@Override
public Object beginTransaction(final EntityManager entityManager,
final TransactionDefinition definition) throws PersistenceException,
SQLException, TransactionException {
Session session = (Session) entityManager.getDelegate();
if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
getSession(entityManager).getTransaction().setTimeout(definition.getTimeout());
}
entityManager.getTransaction().begin();
logger.debug("Transaction started");
session.doWork(new Work() {
@Override
public void execute(Connection connection) throws SQLException {
logger.debug("The connection instance is {}", connection);
logger.debug("The isolation level of the connection is {} and the isolation level set on the transaction is {}",
connection.getTransactionIsolation(), definition.getIsolationLevel());
DataSourceUtils.prepareConnectionForTransaction(connection, definition);
}
});
return prepareTransaction(entityManager, definition.isReadOnly(), definition.getName());
}
}
package com.sample.utils.jpa;
import java.sql.Connection;
import java.sql.SQLException;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import org.hibernate.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.orm.jpa.vendor.HibernateJpaDialect;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
public class IsolationSupportHibernateJpaDialect extends HibernateJpaDialect {
/**
*
*/
private static final long serialVersionUID = 1L;
private Logger logger = LoggerFactory.getLogger(IsolationSupportHibernateJpaDialect.class);
/**
* This method is overridden to set custom isolation levels on the connection
*
* @param entityManager
* @param definition
* @return
* @throws PersistenceException
* @throws SQLException
* @throws TransactionException
*/
@SuppressWarnings("deprecation")
@Override
public Object beginTransaction(final EntityManager entityManager, final TransactionDefinition definition)
throws PersistenceException, SQLException, TransactionException {
boolean infoEnabled = false;
boolean debugEnabled = false;
Session session = (Session) entityManager.getDelegate();
if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
getSession(entityManager).getTransaction().setTimeout(definition.getTimeout());
}
Connection connection = session.connection();
infoEnabled = logger.isInfoEnabled();
debugEnabled = logger.isDebugEnabled();
if (infoEnabled) {
logger.info("Connection Info: isolationlevel={} , instance={} ", connection.getTransactionIsolation(), connection);
logger.info("Transaction Info: IsolationLevel={} , PropagationBehavior={} , Timeout={} , Name={}",
new Object[] { definition.getIsolationLevel(), definition.getPropagationBehavior(), definition.getTimeout(),
definition.getName() });
}
if (debugEnabled) {
logger.debug("The isolation level of the connection is {} and the isolation level set on the transaction is {}",
connection.getTransactionIsolation(), definition.getIsolationLevel());
}
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(connection, definition);
if (infoEnabled) {
logger.info("The previousIsolationLevel {}", previousIsolationLevel);
}
entityManager.getTransaction().begin();
if (infoEnabled) {
logger.debug("Transaction started");
}
Object transactionDataFromHibernateJpaTemplate = prepareTransaction(entityManager, definition.isReadOnly(),
definition.getName());
return new IsolationSupportSessionTransactionData(transactionDataFromHibernateJpaTemplate, previousIsolationLevel,
connection);
}
/*
* (non-Javadoc)
*
* @see org.springframework.orm.jpa.vendor.HibernateJpaDialect#cleanupTransaction(java.lang.Object)
*/
@Override
public void cleanupTransaction(Object transactionData) {
super.cleanupTransaction(((IsolationSupportSessionTransactionData) transactionData)
.getSessionTransactionDataFromHibernateTemplate());
((IsolationSupportSessionTransactionData) transactionData).resetIsolationLevel();
}
private static class IsolationSupportSessionTransactionData {
private final Object sessionTransactionDataFromHibernateJpaTemplate;
private final Integer previousIsolationLevel;
private final Connection connection;
public IsolationSupportSessionTransactionData(Object sessionTransactionDataFromHibernateJpaTemplate,
Integer previousIsolationLevel, Connection connection) {
this.sessionTransactionDataFromHibernateJpaTemplate = sessionTransactionDataFromHibernateJpaTemplate;
this.previousIsolationLevel = previousIsolationLevel;
this.connection = connection;
}
public void resetIsolationLevel() {
if (this.previousIsolationLevel != null) {
DataSourceUtils.resetConnectionAfterTransaction(connection, previousIsolationLevel);
}
}
public Object getSessionTransactionDataFromHibernateTemplate() {
return this.sessionTransactionDataFromHibernateJpaTemplate;
}
}
}
No comments:
Post a Comment