In my java process I'm connecting to MySql using the following spring configuration:
#Configuration
#EnableTransactionManagement
#PropertySources({ #PropertySource("classpath:/myProperties1.properties"), #PropertySource("classpath:/myProperties2.properties") })
public class MyConfiguration {
#Autowired
protected Environment env;
/**
* #return EntityManagerFactory for use with Hibernate JPA provider
*/
#Bean(destroyMethod = "destroy")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setJpaVendorAdapter(jpaVendorAdapter());
em.setPersistenceUnitManager(persistenceUnitManager());
return em;
}
/**
*
* #return jpaVendorAdapter that works in conjunction with the
* persistence.xml
*/
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.valueOf(env.getProperty("jpa.database")));
vendorAdapter.setDatabasePlatform(env.getProperty("jpa.dialect"));
vendorAdapter.setGenerateDdl(env.getProperty("jpa.generateDdl", Boolean.class, false));
vendorAdapter.setShowSql(env.getProperty("jpa.showSql", Boolean.class, false));
return vendorAdapter;
}
#Bean
public PersistenceUnitManager persistenceUnitManager() {
DefaultPersistenceUnitManager pum = new DefaultPersistenceUnitManager();
pum.setPackagesToScan("com.app.dal");
pum.setDefaultPersistenceUnitName("my-pu");
pum.setPersistenceXmlLocations("classpath:/META-INF/persistence.xml");
pum.setDefaultDataSource(dataSource());
return pum;
}
#Bean(destroyMethod = "close")
public DataSource dataSource() {
Properties dsProps = new Properties();
dsProps.put("driverClassName", env.getProperty("hikari.driverClassName"));
dsProps.put("username", env.getProperty("hikari.username"));
dsProps.put("password", env.getProperty("hikari.password"));
dsProps.put("jdbcUrl", env.getProperty("hikari.source.data.jdbcUrl"));
dsProps.put("connectionTimeout", env.getProperty("hikari.connectionTimeout", Integer.class));
dsProps.put("idleTimeout", env.getProperty("hikari.idleTimeout", Integer.class));
dsProps.put("maxLifetime", env.getProperty("hikari.maxLifetime", Integer.class));
dsProps.put("maximumPoolSize", env.getProperty("hikari.maximumPoolSize.rtb.source", Integer.class));
dsProps.put("leakDetectionThreshold", env.getProperty("hikari.leakDetectionThreshold", Integer.class));
dsProps.put("jdbc4ConnectionTest", env.getProperty("hikari.jdbc4ConnectionTest", Boolean.class));
HikariConfig config = new HikariConfig(dsProps);
HikariDataSource ds = new HikariDataSource(config);
return ds;
}
#Bean(name = "sourceTxMgr")
public PlatformTransactionManager sourceDatatransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setPersistenceUnitName("my-pu");
transactionManager.setDataSource(dataSource());
return transactionManager;
}
#Bean
public PersistencyManager persistencyManager() {
return new JpaPersistencyManager();
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
The Entity-Manager is injected to the data access layer by the container:
#PersistenceContext(type = PersistenceContextType.TRANSACTION, unitName = "my-pu")
private EntityManager myEntityManager;
And my public business logic methods are annotated with the #Transactional annotation.
As far as I understand the container is responsible for ensuring that the entity-manager returns connections to the pool (in my case HikariCP) once a transaction is done but I did not find any official documentation that describes how the connections are managed. Can anyone explain it to me or provide a good reference that can explain when exactly connections are returned to the pool when using such a configuration?
UPDATE:
The best related info I could come up with so far (taken from here):
The persistence context proxy that implements EntityManager is not the only component needed for making declarative transaction management work. Actually three separate components are needed:
The EntityManager Proxy itself
The Transactional Aspect
The Transaction Manager
Let's go over each one and see how they interact.
The Transactional Aspect
The Transactional Aspect is an 'around' aspect that gets called both before and after the annotated business method. The concrete class for implementing the aspect is TransactionInterceptor.
The Transactional Aspect has two main responsibilities:
At the 'before' moment, the aspect provides a hook point for determining if the business method about to be called should run in the scope of an ongoing database transaction, or if a new separate transaction should be started.
At the 'after' moment, the aspect needs to decide if the transaction should be committed, rolled back or left running.
At the 'before' moment the Transactional Aspect itself does not contain any decision logic, the decision to start a new transaction if needed is delegated to the Transaction Manager.
The Transaction Manager
The transaction manager needs to provide an answer to two questions:
should a new Entity Manager be created?
should a new database transaction be started?
This needs to be decided at the moment the Transactional Aspect 'before' logic is called. The transaction manager will decide based on:
the fact that one transaction is already ongoing or not
the propagation attribute of the transactional method (for example REQUIRES_NEW always starts a new transaction)
If the transaction manager decides to create a new transaction, then it will:
create a new entity manager
bind the entity manager to the current thread
grab a connection from the DB connection pool
bind the connection to the current thread
The entity manager and the connection are both bound to the current thread using ThreadLocal variables.
They are stored in the thread while the transaction is running, and it's up to the Transaction Manager to clean them up when no longer needed.
Any parts of the program that need the current entity manager or connection can retrieve them from the thread. One program component that does exactly that is the EntityManager proxy.
It's not complicated at all.
First, you need to understand that the Spring transaction manager is only a transaction management abstraction. In your case, the actual transactions happen at the JDBC Connection level.
All #Transactional service method calls are intercepted by the TransactionInterceptor Aspect.
The TransactionIntreceptor delegates transaction management to the current configured
AbstractPlatformTransactionManager implementation (JpaTransactionManager in your case).
JpaTransactionManager will bind the current running Spring transaction to an EntityManager, so all DAOs participating in the current transaction share the same Persistence Context.
JpaTransactionManager simply uses the EntityManager Transaction API for controlling transactions:
EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction();
tx.commit();
The JPA Transaction API simply delegates the call to the underlying JDBC Connection commit/rollback methods.
When the transaction is done (commit/rollback), the org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction calls:
transactionCoordinator().getTransactionContext().managedClose();
which triggers a Hibernate Session (Entity Manager) close.
The underlying JDBC connection is therefore triggered to be closed as well:
jdbcCoordinator.close();
Hibernate has a logical JDBC connection handle:
#Override
public Connection close() {
LOG.tracev( "Closing JDBC container [{0}]", this );
if ( currentBatch != null ) {
LOG.closingUnreleasedBatch();
currentBatch.release();
}
cleanup();
return logicalConnection.close();
}
The logical connection delegates the close call to the currently configured connection provider (DataSourceConnectionProvider in your case), which simply calls the close method on the JDBC connection:
#Override
public void closeConnection(Connection connection) throws SQLException {
connection.close();
}
Like any other connection pooling DataSource, the JDBC connection close simply returns the connection to the pool and doesn't close the physical database connection. That's because the connection pooling DataSource returns a JDBC Connection proxy that intercepts all calls and delegates the closing to the connection pool handling logic.
Note that for RESOURCE_LOCAL transactions, you should also set the hibernate.connection.provider_disables_autocommit property if the autocommit check was disabled by the connection pool. This way, the database connections are going to be acquired lazily prior to executing a SQL query or flushing the Persistence Context.
Related
HI We are migrating hibernate 3 to 5.4.25 >Please help me to get CMT transactionfactory replacement in hibernate 5 as it is deprecated and i am getting no transaction is in progress if i use session.flush() in my DAO class GET methos. Kindly help
An excerpt from link
Its not just org.hibernate.transaction.CMTTransactionFactory that was
removed; all of the TransactionFactory classes were removed. Actually I
think we should add them as resolvable names:
org.hibernate.transaction.CMTTransactionFactory
->
org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl
org.hibernate.transaction.JTATransactionFactory
-> org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl
org.hibernate.transaction.JDBCTransactionFactory ->
org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl
Actually, I have recently done a similar migration where I have completely changed the transaction-related blocks to Spring's imperative transaction infrastructure.
I have used PlatformTransactionManager which is the default implementations of this strategy interface are org.springframework.transaction.jta.JtaTransactionManager and org.springframework.jdbc.datasource.DataSourceTransactionManager, which can serve as an implementation guide for other transaction strategies.
#EnableTransactionManagement
#Configuration
public class DbConfig{
#Autowired
private DataSource dataSource;
#Bean
#Primary
public PlatformTransactionManager annotationDrivenTransactionManager(LocalSessionFactoryBean lsfb) {
return new HibernateTransactionManager(lsfb.getObject());
}
#Bean
#Primary
public LocalSessionFactoryBean getSessionFactory()
{
LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean();
lsfb.setDataSource(this.dataSource);
Properties properties = new Properties();
properties.put("hibernate.dialect", <hibernateDialect>);
properties.put("hibernate.connection.isolation",2); // READ_COMMITTED
properties.put("hibernate.show_sql", hibernateShowSql); //To show sql or not
//...configurable hibernate properties to add
lsfb.setHibernateProperties(properties);
lsfb.setPackagesToScan("com.demo.entity.model"");
return lsfb;
}
}
JavaDoc about the PlatformTransactionManager(link)
PlatformTransactionManager implementation for a single Hibernate
SessionFactory. Binds a Hibernate Session from the specified factory
to the thread, potentially allowing for one thread-bound Session per
factory. SessionFactory.getCurrentSession() is required for Hibernate
access code that needs to support this transaction handling mechanism,
with the SessionFactory being configured with SpringSessionContext.
Supports custom isolation levels, and timeouts that get applied as
Hibernate transaction timeouts.
This transaction manager is appropriate for applications that use a
single Hibernate SessionFactory for transactional data access, but it
also supports direct DataSource access within a transaction (i.e.
plain JDBC code working with the same DataSource). This allows for
mixing services which access Hibernate and services which use plain
JDBC (without being aware of Hibernate)! Application code needs to
stick to the same simple Connection lookup pattern as with
DataSourceTransactionManager (i.e.
DataSourceUtils.getConnection(javax.sql.DataSource) or going through a
TransactionAwareDataSourceProxy).
In the SDN 4.1 reference, the configuration includes extending Neo4jConfiguration and setting up a Session object explicitly as a #Bean. In 4.2, the guidance is to not extend Neo4jConfiguration and follow the config below. Note that explicitly setting up a standalone Session object is absent:
#Bean
public org.neo4j.ogm.config.Configuration configuration() {
org.neo4j.ogm.config.Configuration config = new org.neo4j.ogm.config.Configuration();
config
.driverConfiguration()
.setDriverClassName("org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver")
.setURI(dbLocation);
return config;
#Bean
public SessionFactory sessionFactory() {
SessionFactory sessionFactory = new SessionFactory(configuration(), "org.my.package" );
sessionFactory.register(new MyPreSaveListener());
return sessionFactory;
}
I've seen this config used while #Autowiring the Session object itself (not the factory) in repository classes. Does this mean that there will then be only one Session instance across the application? If so, doesn't that go against the idea that a Session lifespan should be limited to an application's "unit of work?"
Note that my repositories are custom and do not extend the neo4j repos, as I am currently migrating away from using the Neo4jTemplate object.
No, there is not one session per application. So long as your Session is called from within a #Transactional annotation or TransactionTemplate it will call a proxy Session (which is generated by the Spring Data Neo4j framework during startup). This will effectively create a session for the lifetime of the transaction (where it is demarcated) and once out of scope, allow it to be garbage collected.
I'm sure this is a feature, but I would appreciate any pointers/help on the issue.
I'm using Spring (Boot) / JPA (Hibernate) stack.
If I query for an entity (called Homes) using JPA, and increment a Calendar field
homeInstance.getListedDate().add(Calendar.DATE, 1), for example
with no intention of saving this change back to the database (it's only for quick intermediary calculations as the several routines run on a list of these entities).
Then I call a nativequery using an injected EntityManager bean.
#Autowired
EntityManager em;
...
Query nvqry = em.createNativeQuery(...)
nvqry.getResultList()
Doing this automatically persists the changes made to the entity above (which were never supposed to be persisted.
Is there a way to disable this feature without losing the in-memory changes? I manually persist anything I want using the repository, and as such a whole session persistence is useless for me.
That's related to the hibernate FlushMode. If you haven't modified the default configuration, this will be likely set to FlushMode.AUTO.
To alter this behavior you have to provide an EntityManagerFactory bean.
In the configuration pojo you can declare something like this:
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
final DataSource dataSource,
final JpaVendorAdapter jpaVendorAdapter) {
final LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setPersistenceUnitName("domainPC");
factory.setDataSource(dataSource);
factory.setJpaVendorAdapter(jpaVendorAdapter);
factory.setPackagesToScan(getClass().getPackage().getName());
Properties jpaProperties = new Properties();
//and here is your flushMode set.
jpaProperties.setProperty("org.hibernate.flushMode", "COMMIT");
factory.setJpaProperties(jpaProperties);
return factory;
}
An integration test class is annotated with:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = IntegrationTestConfig.class)
It's not supposed to run in a transaction so isn't marked as #Transactional but I'm getting errors when trying to perform persist, merge etc. operations on the EntityManager, which is injected using #PersistenceContext:
No transactional EntityManager available
How can this be resolved?
EDIT:
As requested in the comments, the Spring version is 4.1.0.RELEASE and IntegrationTestConfig is below:
#EnableAspectJAutoProxy
#EnableAsync
#EnableScheduling
#EnableTransactionManagement
#Configuration
public class IntegrationTestConfig {
/**
* Override the existing JPA data source bean with a test data source.
* #return test data source
*/
#Bean
public DataSource dataSource() {
final SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriverClass(org.h2.Driver.class);
dataSource.setUrl("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;INIT=CREATE SCHEMA IF NOT EXISTS mydb");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
}
If you are sure that you are never going to call entityManager.flush(), obtain the PersistenceContext as follows:
#PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager entityManager;
Why is this needed? Spring Data JPA hands out what is called a shared EntityManager when the #PersistenceContext annotation is used (without any attributes). Full details for this are available in the JavaDocs for org.springframework.orm.jpa.SharedEntityManagerCreator. This class maintains a lookup table where the EntityManager methods flush, merge, persist, refresh and remove are required to be run inside a transaction. So, any time it encounters a method call that is not inside a transaction, it bails out.
The annotation #PersistenceContext has a type attribute that can be set to one of PersistenceContextType.EXTENDED or PersistenceContextType.TRANSACTION, with the later being the default. Therefore, the default #PersistenceContext causes SharedEntityManagerCreator to look for a transaction and bail out if none is found.
Using PersistenceContextType.EXTENDED bypasses the need to check for a transaction when obtaining the EntityManager and therefore the code should work.
flush still cannot be called without a transaction because the JPA providers require it to be called only within a transactional context.
In spring the HibernateTransactionManager uses the SessionFactory it was initialised with to "bind" a Session to the current thread context when creating a new transaction. Then when HibernateTemplate is used it find that bound Session and uses it.
However I found today that HTM also binds its transaction to the underlying DataSource as well as the SessionFactory (if possible). This allows code to use JdbcTemplate within the transaction scope and, provided the DataSource used by JdbcTemplate is the same as the SessionFactory uses, the Jdbc operations will participate in the transaction (using the same underlying Connection).
This bit me quite badly today when I had some code in my hibernate id allocator that was creating a DataSourceTransactionManager and JdbcTemplate to allocate ids out of a high-lo table. I was intending that this be a standalone transaction that would fetch the next high number and then commit the change to the id table. However because of the above behaviour it was actually participating in my "outer" hibernate transaction AND even worse committing it early. Suffice to say not good.
I tried playing around with transaction propogation settings (used REQUIRES_NEW) but this didn't help.
Does anyone know the best way to use JdbcTemplate within a hibernate transaction and NOT have them share a transaction, even tho they share the same DataSource?
EDIT:
I have a SessionFactory (S) which is created by the spring LocalSessionFactoryBean using a DataSource (D). The HibernateTransactionManager is created with that SessionFactory (S).
some business logic code would look like this..
hibernateTransactionOperations.execute( new TransactionCallbackWithoutResult()
{
#Override
protected void doInTransactionWithoutResult( TransactionStatus status )
{
// some transactional code here using a HibernateTemplate
// will include calls to id allocation when doing hibernateTemplate.save(obj)
}
} );
my id allocation does this (paraphrased), the DataSource below is the same (D) as the one used in the SessionFactory (S).
PlatformTransactionManager txManager = new DataSourceTransactionManager( dataSource );
TransactionOperations transactionOperations = new TransactionTemplate( txManager );
return transactionOperations.execute( new TransactionCallback<Long>()
{
public Long doInTransaction( TransactionStatus status )
{
return allocateBatchTxn( idKey, batchSize );
}
} );
When the transactionOperations execute above completes it will commit the underlying transaction which seems to be the same as the 'outer' hibernate transaction. I have confirmed this by checking locks/transactions in the DB.
Don't create a new DataSourceTransactionManager in your id allocation code. Instead use REQUIRES_NEW and the HibernateTransactionManager.
In allocateBatchTxn(), the safest way to get the JDBC connection is via Spring's DataSourceUtils.getConnection() method.
I tried it with REQUIRES_NEW - it works as expected (on HSQLDB), perhaps it's DB-dependent:
// txManager is a HibernateTransactionManager obtained from the application context
TransactionOperations transactionOperations = new TransactionTemplate( txManager );
transactionOperations.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
return transactionOperations.execute(new TransactionCallback<Long>() {
public Long doInTransaction( TransactionStatus status ) {
return allocateBatchTxn( idKey, batchSize );
}
});
Answering my own question.
The root cause of my problem is a couple of things in HibernateTransactionManager.
The setting 'autodetectDataSource' which defaults to true
In afterPropertiesSet() with the above true it auto-detects the DataSource from the SessionFactory
In doBegin() if the DataSource is not null it will bind new transactions to the SessionFactory AND the DataSource
This is causing my problem because also I have a new DataSourceTransactionManager it still uses the same underlying storage (TransactionSynchronizationManager) to manage transactions and because both use the DataSource you get this leaking of transactions between txn managers. I might argue that a txn manager should include its own 'key/id' in the key for the transactional resources so there are independent but it doesn't appear to do that.
The response above are sensible. Using the hibernate txn manager rather than creating a new DataSourceTransactionManager and then using REQURES_NEW would solve the problem. However in my case that would introduce a circular dependency between HTM -> SessionFactory -> IdAllocator -> HTM.
I came up a solution that works but isn't the most elegant thing ever.
When constructor the id allocator it is passed a DataSource in the constructor. I simply wrap that DataSource in a delegating wrapper that is 100% pass through. This changes the DataSource reference so the txn logic does not think there is a transaction in progress and works as I want it to.