We have used class level #transaction annotations to enable rollback mechanism in Spring batch.
The code is given below:
#Transactional(rollbackFor = { DaoException.class, LogicRuntimeException.class })
public class ClassA{}
where, it is expected that whenever any exception is being thrown from any method from ClassA, it should rollback all other transactions which is already executed (but not committed) from the following class.
But it is observed that when we are executing this using local setting, it is working perfectly but when the WAR is deployed to the server, in which Global setting is used, the rollback is not working at all.
The main difference is for local system, we have used JDBC:
<beans:property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
and for server (global) setup, we have used JNDI:
org.springframework.jndi.JndiObjectFactoryBean
Also, FYI,
to set auto-commit to false, the following common code exists for both local and global setup:
SqlSession session = sqlSessionFactory.openSession();
session.getConnection().setAutoCommit(false);
where, sqlSessionFactory is object for "org.mybatis.spring.SqlSessionFactoryBean" where all the mappers and data source are linked.
Just wanna know if I am missing any specific configuration for Global transaction, probably to set auto-commit as false or any other details.
I work in a project that uses JavaEE. I work with Glassfish servers version 3. I'm frequently having a problem (not always) in my singleton EJB's that use an EntityManager instance. A lot of times, I get this error:
[timestamp] [http-thread-pool-8080(49)] ERROR com.sun.xml.ws.server.sei.TieHandler.serializeResponse Attempting to execute an operation on a closed EntityManager.
java.lang.IllegalStateException: Attempting to execute an operation on a closed EntityManager.
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.verifyOpen(EntityManagerImpl.java:1662) ~[org.eclipse.persistence.jpa.jar:2.3.4.v20130626-0ab9c4c]
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.find(EntityManagerImpl.java:643) ~[org.eclipse.persistence.jpa.jar:2.3.4.v20130626-0ab9c4c]
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.find(EntityManagerImpl.java:532) ~[org.eclipse.persistence.jpa.jar:2.3.4.v20130626-0ab9c4c]
at com.sun.enterprise.container.common.impl.EntityManagerWrapper.find(EntityManagerWrapper.java:320) ~[container-common.jar:3.1.2.1]
The log goes on, but I just showed the top of it. The next line of the log was the call to an WebService deployed in the same server. And when this error happens, it is always originated by a call to a WebService deployed in the same server that performs a find in the database using the method 'find' from the entityManager instance.
The entity manager is being injected outside of the bean in the #PostConstruct of a #WebService annotated class, using the line '(EntityManager)new InitialContext().lookup("java:comp/env/persistence/etc");' This is the class that receives all the incoming requests and decides which bean should be called, based on request.
Right after receiving a request, this class calls the respective singleton bean, based on the request, passing the injected EntityManager to the respective bean.
I understand that the EntityManager is closed when I try to perform the operation and that is indeed the problem. However, I thought this opening and closing of the EntityManager was managed automatically. Apparently it doesn't work that way. I'm not closing the EntityManager directly anywhere in the code either.
I'm not seeing any reasonable solution to approach this problem. All I find in online resources is that it may be a Glassfish bug and restart the server generally works. Nothing concrete to solve the problem.
Some of the information present in the PersistenceUnit configured in the persistence.xml file I'm using is presented below.
<persistence-unit> name="XXX" transaction-type="JTA"
<provider>org.eclipse.persistence.jpa.PersistenceProvider></provider>
<jta-data-source>jdbc/YYY</jta-data-source>
<properties>
<property name="eclipselink.target-database" value="Oracle"/>
<property name="eclipselink.cache.shared.default" value="false"/>
<property name="eclipselink.cache.size.default" value="0"/>
<property name="eclipselink.cache.type.default" value="None"/>
<property name="eclipselink.weaving.internal" value="false"/>
<property name="toplink.target-database" value="Oracle"/>
<property name="eclipselink.session.customizer"
value="aaa.bbb.ccc.IsolateEmbeddablesCustomizer"/>
</properties>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
Do you have any idea on how to solve this problem and did any of you also got the same error?
Thank you.
I think the the problem lies just here:
The entity manager is being injected outside of the bean in the
#PostConstruct of a #WebService annotated class, using the line
'(EntityManager)new
InitialContext().lookup("java:comp/env/persistence/etc");' ...
Container provides EntityManagers from some pool it maintains. EMs have some lifecycle and they can become invalid at some point of time. Normally (explained a bit later) this would not be a problem since usually EMs are injected in a managed way.
But you initialize it once in #PostConstruct to some variable and when EM pointed by that variable gets invalid it is not re-initialized or so because it is no managed by container the way meant to be.
I think you can get around if this problem by just checking if EM is invalid and doing the lookup again if reference is not valid. But I stress: that is not the correct way.
passing the injected
EntityManager to the respective bean.
Do not pass the EM. Initialize EM in the bean that uses it. You will not have any savings by passing the one and only instance. On the contrary it can make the performance worse. Let the container handle the optimization of creating of EMs.
So , normally you would be doing something like this in your beans (not passing the EM but managing it on a bean):
#PersistenceContext(unitName="XXX") // "unitName" might not be needed
private EntityManeger em; // if you use only one persistence unit
to make EM managed correctly. That way container makes sure it is always valid.
If you have correctly constructed beans that you let container to initialize by for example #Injecting those to your #WebService this should be possible.
If for some reason it is not possible then you might still do a JNDI lookup in the bean but then I am quite sceptic about functionality of any JTA transactions or so.
I'm using Hibernate 5.1.0.Final with ehcache and Spring 3.2.11.RELEASE. I have the following #Cacheable annotation set up in one of my DAOs:
#Override
#Cacheable(value = "main")
public Item findItemById(String id)
{
return entityManager.find(Item.class, id);
}
The item being returned has a number of assocations, some of which are lazy. So for instance, it (eventually) references the field:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "product_category", joinColumns = { #JoinColumn(name = "PRODUCT_ID") }, inverseJoinColumns = { #JoinColumn(name = "CATEGORY_ID") })
private List<Category> categories;
I notice that within one of my methods that I mark as #Transactional, when the above method is retrieved from the second level cache, I get the below exception when trying to iterate over the categories field:
#Transactional(readOnly=true)
public UserContentDto getContent(String itemId, String pageNumber) throws IOException
{
Item Item = contentDao.findItemById(ItemId);
…
// Below line causes a “LazyInitializationException” exception
for (Category category : item.getParent().getProduct().getCategories())
{
The stack trace is:
16:29:42,557 INFO [org.directwebremoting.log.accessLog] (ajp-/127.0.0.1:8009-18) Method execution failed: : org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: org.mainco.subco.ecom.domain.Product.standardCategories, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:579) [hibernate-myproject-5.1.0.Final.jar:5.1.0.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:203) [hibernate-myproject-5.1.0.Final.jar:5.1.0.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:558) [hibernate-myproject-5.1.0.Final.jar:5.1.0.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:131) [hibernate-myproject-5.1.0.Final.jar:5.1.0.Final]
at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:277) [hibernate-myproject-5.1.0.Final.jar:5.1.0.Final]
at org.mainco.subco.ebook.service.ContentServiceImpl.getCorrelationsByItem(ContentServiceImpl.java:957) [myproject-90.0.0-SNAPSHOT.jar:]
at org.mainco.subco.ebook.service.ContentServiceImpl.getContent(ContentServiceImpl.java:501) [myproject-90.0.0-SNAPSHOT.jar:]
at sun.reflect.GeneratedMethodAccessor819.invoke(Unknown Source) [:1.6.0_65]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [rt.jar:1.6.0_65]
at java.lang.reflect.Method.invoke(Method.java:597) [rt.jar:1.6.0_65]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) [spring-aop-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183) [spring-aop-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) [spring-aop-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96) [spring-tx-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260) [spring-tx-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94) [spring-tx-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) [spring-aop-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91) [spring-aop-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) [spring-aop-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) [spring-aop-3.2.11.RELEASE.jar:3.2.11.RELEASE]
at com.sun.proxy.$Proxy126.getContent(Unknown Source)
I understand what the Hibernate session is closed — I do not care about why this happens. Also, it is NOT an option o make the above association eager (instead of lazy). Given that, how can I solve this problem?
Edit: Here is how my ehccahe.xml is configured …
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd" updateCheck="false">
<!-- This is a default configuration for 256Mb of cached data using the JVM's heap, but it must be adjusted
according to specific requirement and heap sizes -->
<defaultCache maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="86400"
timeToLiveSeconds="86400"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
<cache name="main" maxElementsInMemory="10000" />
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1,
multicastGroupPort=4446, timeToLive=32"/>
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="hostName=localhost, port=40001,
socketTimeoutMillis=2000"/>
</ehcache>
and here is how I’m plugging it into my Spring context …
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="packagesToScan" value="org.mainco.subco" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
<property name="dataSource" ref="dataSource"/>
<property name="jpaPropertyMap" ref="jpaPropertyMap" />
</bean>
<cache:annotation-driven key-generator="cacheKeyGenerator" />
<bean id="cacheKeyGenerator" class="org.mainco.subco.myproject.util.CacheKeyGenerator" />
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheCacheManager"
p:cacheManager-ref="ehcache"/>
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
p:configLocation="classpath:ehcache.xml"
p:shared="true" />
<util:map id="jpaPropertyMap">
<entry key="hibernate.show_sql" value="false" />
<entry key="hibernate.hbm2ddl.auto" value="validate"/>
<entry key="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
<entry key="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup" />
<entry key="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
<entry key="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/>
<entry key="hibernate.cache.use_second_level_cache" value="true" />
<entry key="hibernate.cache.use_query_cache" value="false" />
<entry key="hibernate.generate_statistics" value="false" />
</util:map>
<bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
Take a look at a similar question. Basically, your cache is not a Hibernate second-level cache. You are accessing a lazy uninitialized association on a detached entity instance, so a LazyInitializationException is expected to be thrown.
You can try to play around with hibernate.enable_lazy_load_no_trans, but the recommended approach is to configure Hibernate second level cache so that:
Cached entities are automatically attached to the subsequent sessions in which they are loaded.
Cached data is automatically refreshed/invalidated in the cache when they are changed.
Changes to the cached instances are synchronized taking the transaction semantics into consideration. Changes are visible to other sessions/transactions with the desired level of cache/db consistency guarantees.
Cached instances are automatically fetched from the cache when they are navigated to from the other entities which have associations with them.
EDIT
If you nevertheless want to use Spring cache for this purpose, or your requirements are such that this is an adequate solution, then keep in mind that Hibernate managed entities are not thread-safe, so you will have to store and return detached entities to/from the custom cache. Also, prior to detachment you would need to initialize all lazy associations that you expect to be accessed on the entity while it is detached.
To achieve this you could:
Explicitly detach the managed entity with EntityManager.detach. You would need to detach or cascade detach operation to the associated entities also, and make sure that the references to the detached entities from other managed entities are handled appropriately.
Or, you could execute this in a separate transaction to make sure that everything is detached and that you don't reference detached entities from the managed ones in the current persistence context:
#Override
#Cacheable(value = "main")
#Transactional(propagation = Propagation.REQUIRES_NEW)
public Item findItemById(String id) {
Item result = entityManager.find(Item.class, id);
Hibernate.initialize(result.getAssociation1());
Hibernate.initialize(result.getAssociation2());
return result;
}
Because it may happen that the Spring transaction proxy (interceptor) is executed before the cache proxy (both have the same default order value: transaction; cache), then you would always start a nested transaction, be it to really fetch the entity, or to just return the cached instance.
While we may conclude that performance penalty for starting unneeded nested transactions is small, the issue here is that you leave a small time window when a managed instance is present in the cache.
To avoid that, you could change the default order values:
<tx:annotation-driven order="200"/>
<cache:annotation-driven order="100"/>
so that cache interceptor is always placed before the transaction one.
Or, to avoid ordering configuration changes, you could simply delegate the call from the #Cacheable method to the #Transactional(propagation = Propagation.REQUIRES_NEW) method on another bean.
What you implemented in your code snippets is a custom cache based on spring-cache. With your implementation you would need to take care of cache evictions, making sure that at the point when your object graphs will get cached they are properly loaded, etc. Once they get cached and the original hibernate session that loaded them is closed they'll become detached, you can no longer navigate unfetched lazy associations. Also, your custom cache solution in its current state would cache entity graphs, which is probably not what you want, since any part of that graph might change at a given time, and your cache solution would need to watch for changes in all parts of that graph to properly handle evictions.
The configuration you posted in your question is not Hibernate second-level cache.
Managing a cache is a complex endeavor and I don't recommend it doing it by yourself, unless you're absolutely sure what you're doing (but then you won't be asking this question on Stackoverflow).
Let me explain what is happening with when you get the LazyInitializationException: you marked one of your dao methods with #org.springframework.cache.annotation.Cacheable. What happens in this case is the following:
Spring attaches an interceptor to your managed bean. The interceptor will intercept the dao method call, it will create a cache key based on the interceptor method and the actual method arguments (this can be customized), and look up the cache to see if there's any entry in the cache for that key. In case there's an entry it will return that entry without actually invoking your method. In case there's no cache entry for that key, it will invoke your method, serializes the return value and store it in the cache.
For the case when there was no cache entry for the key, your method will get invoked. Your method uses a spring provided singleton proxy to the thread bound EntityManager which was assigned earlier when Spring encountered the first #Transactional method invocation. In your case this was the getContent(...) method of another spring service bean. So your method loads an entity with EntityManager.find(). This will give you a partially loaded entity graph containing uninitialized proxies and collections to other associated entities not yet loaded by the persistence context.
Your method returns with the partially loaded entity graph and spring will immediately serialize it for you and store it in the cache. Note that serializing a partially loaded entity graph will deserialize to a partially loaded entity graph.
On the second invocation of the dao method marked with #Cacheable with the same arguments, Spring will find that there is indeed an entry in the cache corresponding to that key and will load and deserialize the entry. Your dao method will not be called since it uses the cached entry. Now you encounter the problem: your deserialized cached entity graph was only partially loaded when you stored in the cache, and as soon as you touch any uninitialized part of the graph you'll get the LazyInitializationException. A deserialized entity will always be detached, so even if the original EntityManager would be still open (which is not), you would still get the same exception.
Now the question is: what can you do to avoid the LazyInitializationException. Well, my recommendation is that you forget about implementing a custom cache and just configure Hibernate to do the caching for you. I will talk about how to do that later. If you want to stick with the custom cache you tried to implement, here's what you need to do:
Go through your whole code base and find all invocations of your #Cacheable dao method. Follow all possible code paths where the loaded entity graph is passed around and mark all parts of the entity graph which ever gets touched by client code. Now go back to your #Cacheable method and modify it so that it loads and initializes all parts of the entity graph that would ever get possibly touched. Because once you return it and it gets serialized, and deserialized later, it will always be in a detached state so better make sure all possible graph paths are properly loaded. You should already feel how impractical this will end up. If that still didn't convince you not to follow this direction, here's another argument.
Since you load up a potentially big chunk of the database, you will have a snapshot of that part of the database at the given time when it got actually loaded and cached. Now, whenever you use a cached version of this big chunk of the database, there's is a risk that you are using a stale version of that data. To defend from this, you would need to watch for any changes in the current version of that big chunk of the database you just cached and evict the whole entity graph from the cache. So you pretty much need to take into account which entities are parts of your entity graph and set up some event listeners whenever those entities are changed and evict the whole graph. None of these issues are present with Hibernate second-level cache.
Now back to my recommendation: set up Hibernate second-level cache
Hibernate second-level cache is managed by Hibernate and you get eviction management from hibernate automatically. If you have Hibernate second-level cache enabled, Hibernate will cache the data needed to reconstruct your entities and, if - when seeking to load an entity from the database - it finds that it has a valid cache entry for your entity, it will skip hitting the database and reconstruct your entity from its cache. (Mark the difference to caching an entity graph with its possibly unfetched associations and uninitialized proxies in your custom cache solution). It will also replace stale cache entries when you update an entity. It does all sorts of things related to managing the cache so that you don't have to worry about it.
Here's how can you enable Hibernate second-level cache: in addition to your configuration do the following:
In addition to the hibernate properties you already have for second-level management, namely
<entry key="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
<entry key="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider"/>
<entry key="hibernate.cache.use_second_level_cache" value="true" />
add the following entry:
<entry key="javax.persistence.sharedCache.mode" value="ENABLE_SELECTIVE" />
alternatively, you could add a shared-cache-mode configuration option to your persistence.xml (since you didn't post it, I assumed you don't use it hence the previous alternative; the following one is preferred though):
<persistence-unit name="default">
<!-- other configuration lines stripped -->
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<!-- other configuration lines stripped -->
</persistence-unit>
Add javax.persistence.#Cacheable annotation to your #Entity classes you want to be cacheable.
If you want to add caching for collection valued associations which Hibernate doesn't cache by default, you can add a #org.hibernate.annotations.Cache annotation (with a proper cache concurrency strategy choice) for each such collection:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "product_category", joinColumns = { #JoinColumn(name = "PRODUCT_ID")
}, inverseJoinColumns = { #JoinColumn(name = "CATEGORY_ID") })
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private List<Category> categories;
See Improving performance/The Second Level Cache in the Hibernate Reference Documentation for further details.
This is a nice informative article about the subject: Pitfalls of the Hibernate Second-Level / Query Caches
I have put together a small project based on your posted code snippets which you can check out to see Hibernate second-level cache in action.
The problem is that you are caching references to objects which are loaded lazily. Cache the object once it is all loaded or do not use the cache at all.
Here is how you could load the categories manually before caching it:
Item item = entityManager.find(Item.class, id);
item.getParent().getProduct().getCategories();
return item;
Also a better caching strategy would be to have the cache at the service level of your application instead of the DAO level or no cache at all.
Your issue is caused by the following events:
An Item is being retrieved without its categories then put in the cache in transaction 1. In transaction 2, you call the same method and retrieve the Item and try to read its categories. At that moment hibernate tries to read the categories from transaction 1 which is associated to the Item object but transaction 1 is already completed so it fails.
I used simple type cache with this config as below:
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
spring.jpa.open-in-view=true
spring.cache.type=simple
I'm trying to implement the solution outlined in this answer. The short of it is: I want to set the role for each database connection in order to provide better data separation for different customers. This requires intercepting JDBC queries or transactions, setting the user before the query runs and resetting it afterwards. This is mainly done to comply with some regulatory requirements.
Currently I'm using Tomcat and Tomcat's JDBC pool connecting to a PostgreSQL database. The application is built with Spring and Hibernate. So far I couldn't find any point for intercepting the queries.
I tried JDBC interceptors for Tomcat's built in pool but they have to be global and I need to access data from my Web appliation in order to correlate requests to database users. As far as I see, Hibernate's interceptors work only on entities which is too high level for this use case.
What I need is something like the following:
class ConnectionPoolCallback {
void onConnectionRetrieved(Connection conn) {
conn.execute("SET ROLE " + getRole()); // getRole is some magic
}
void onConnectionReturned(Connection conn) {
conn.execute("RESET ROLE");
}
}
And now I need a place to register this callback... Does anybody have any idea how to implement something like this?
Hibernate 4 has multitenancy support. For plain sql you will need datasource routing which I believe spring has now or is an addon.
I would not mess ( ie extend) the pool library.
Option 1:
As Adam mentioned, use Hibernate 4's multi-tenant support. Read the docs on Hibernate multi-tenancy and then implement the MultiTenantConnectionProvider and CurrentTenantIdentifierResolver interfaces.
In the getConnection method, call SET ROLE as you've done above. Although it's at the Hibernate level, this hook is pretty close in functionality to what you asked for in your question.
Option 2:
I tried JDBC interceptors for Tomcat's built in pool but they have to
be global and I need to access data from my Web appliation in order to
correlate requests to database users.
If you can reconfigure your app to define the connection pool as a Spring bean rather than obtain it from Tomcat, you can probably add your own hook by proxying the data source:
<!-- I like c3p0, but use whatever pool you want -->
<bean id="actualDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${db.url}"/>
<property name="user" value="${db.user}" />
.....
<!-- uses the actual data source. name it "dataSource". i believe the Spring tx
stuff looks for a bean named "dataSource". -->
<bean id="dataSource" class="com.musiKk.RoleSettingDSProxy">
<property name="actualDataSource"><ref bean="actualDataSource" /></property>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource"><ref bean="dataSource" /></property>
....
And then build com.musiKk.RoleSettingDSProxy like this:
public class RoleSettingDSProxy implements DataSource {
private DataSource actualDataSource;
public Connection getConnection() throws SQLException {
Connection con = actualDataSource.getConnection();
// do your thing here. reference a thread local set by
// a servlet filter to get the current tenant and set the role
return con;
}
public void setActualDataSource(DataSource actualDataSource) {
this.actualDataSource = actualDataSource;
}
Note that I haven't actually tried option 2, it's just an idea. I can't immediately think of any reason why it wouldn't work, but it may unravel on you for some reason if you try to implement it.
One solution that comes to mind is to utilize the Hibernate listeners/callbacks. But do beware that is very low level and quite error-prone. I use it myself to get a certain degree of automated audit logging going; it was not a pretty development cycle to get it to work reliably. unfortunately I can't share code since I don't own it.
http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/listeners.html
In my ActiveMQ configuration I would like to change the default DB lock transaction isolation level to TRANSACTION_REPEATABLE_READ.
API documentation writes:
public void setTransactionIsolation(int transactionIsolation)
set the Transaction isolation level to something other that
TRANSACTION_READ_UNCOMMITTED This allowable dirty isolation level may
not be achievable in clustered DB environments so a more restrictive
and expensive option may be needed like TRANSACTION_REPEATABLE_READ
see isolation level constants in Connection
In the XML configuration, the jdbcPersistenceAdapter's transactionIsolation attribute accepts only integer-type values, so I cannot use the Connection.TRANSACTION_REPEATABLE_READ constant directly, but only it's value (4) instead:
<persistenceAdapter>
<jdbcPersistenceAdapter dataDirectory="${activemq.data}" dataSource="#mysql-ds" transactionIsolation="4" lockKeepAlivePeriod="5000">
<locker>
<lease-database-locker lockAcquireSleepInterval="10000"/>
</locker>
</jdbcPersistenceAdapter>
</persistenceAdapter>
Is there a way, that I could specify the constant instead of hardcoding number "4"?
As ActiveMQ is Spring-based, I thought I could try to assign it somehow via using <util:constant>, but could not find how to do it...
Try that way:
<util:constant id="transactionType" static-field="java.sql.Connection.TRANSACTION_REPEATABLE_READ" />
EDIT:
The problem is not in spring but in activemq XML Schema:
<xs:attribute name="transactionIsolation" type="xs:integer">
So it wont accept any other value than hardcoded int - you can try put property placeholder here:
transactionIsolation="#{myproperty}"
but i'm not sure if this will work.
Work around to this problem is to somehow configure activemq by pure spring beans (bean id=...) without using dedicated amq tags.
EDIT2: here you have sample config with pure spring tags http://activemq.apache.org/jms-and-jdbc-operations-in-one-transaction.html