How do I clear a Hibernate cache from a JPA EntityManager? - java

I have an application with entities which Hibernate is caching for me. Works great except that I now I have to clear the cache (due to asynchronous data changes). Queries are cached as follows:
query.setHint(QueryHints.HINT_CACHEABLE, true);
query.setHint(QueryHints.HINT_CACHE_REGION, "CACHE_REGION_NAME");
The entities have the appropriate annotations (as far as I can tell):
#Cacheable
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, region = "CACHE_REGION_NAME")
So far so good. Now I need to clear the cache. I do the following:
#Override
#Transactional(isolation = Isolation.SERIALIZABLE, propagation = Propagation.REQUIRES_NEW, value = "xProdTransaction")
public void clearClientCache() throws CacheClearingException {
clearSession();
clearCacheRegion();
}
void clearSession() throws CacheClearingException {
Session session = null;
try {
session = em.unwrap(Session.class);
if(session==null) {
throw new CacheClearingException("Unable to find valid session");
} else {
session.clear();
}
} catch(PersistenceException pe) {
throw new CacheClearingException("Unable to clear session", pe);
}
}
void clearCache() throws CacheClearingException {
Cache cache = null;
try {
SessionFactory sessionFactory = em.unwrap(SessionFactory.class);
if(sessionFactory == null) {
throw new CacheClearingException("Unable to find valid session factory");
}
cache = sessionFactory.getCache();
if (cache == null) {
throw new CacheClearingException("Unable to find valid cache");
}
cache.evictCollectionRegion("CACHE_REGION_NAME");
} catch (PersistenceException pe) {
throw new CacheClearingException("Unable to clear cache", pe);
}
}
The session cache is no problem, but I get an exception attempting to unwrap the SessionFactory from the EntityManager:
com.cambia.shc.core.dao.xproduct.CacheClearingException: Unable to clear cache
at com.cambia.shc.core.dao.xproduct.CacheManagerImpl.clearCache(CacheManagerImpl.java:63)
at com.cambia.shc.core.dao.xproduct.CacheManagerImpl.clearClientCache(CacheManagerImpl.java:29)
...
Caused by: javax.persistence.PersistenceException: Hibernate cannot unwrap interface org.hibernate.SessionFactory
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.unwrap(AbstractEntityManagerImpl.java:1524)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:291)
at com.sun.proxy.$Proxy66.unwrap(Unknown Source)
at com.cambia.shc.core.dao.xproduct.CacheManagerImpl.clearCache(CacheManagerImpl.java:51)
... 94 more
My JPA beans are configured in Spring as follows:
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
<bean id="xproductEntityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="xproduct"/>
<property name="jpaProperties">
<props>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
<prop key="hibernate.generate_statistics">true</prop>
</props>
</property>
<property name="jpaDialect" ref="jpaDialect" />
</bean>
<bean id="xProdTransaction" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="xproductEntityManagerFactory"/>
</bean>
Any ideas?
Thanks!

You cannot get every class directly with unwrap. You can get the Session first and then get the SessionFactory from it:
em.unwrap(Session.class).getSessionFactory();

Related

JDBC query does not see session.flush modifications on the same transaction

I have a project with Spring and Hibernate.
After an insert made with hibernate I cannot see the data in the jdbc sql even thou I've called the session.flush method. Any idea why this is happening?
My configuration is like this:
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="packagesToScan">
<list>
<value>ro.asf.capone.common.model</value>
</list>
</property>
<property name="dataSource" ref="dataSource"></property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${agency.hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${agency.hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${agency.hibernate.format_sql}</prop>
<prop key="hibernate.jdbc.batch_size">100</prop>
<prop key="hibernate.jdbc.batch_versioned_data">true</prop>
<prop key="hibernate.jdbc.use_streams_for_binary">true</prop>
<prop key="hibernate.order_updates">true</prop>
<prop key="hibernate.connection.release_mode">auto</prop>
<prop key="hibernate.connection.autocommit">true</prop>
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.use_structured_entries">true</prop>
<prop key="hibernate.cache.provider_configuration_file_resource_path">ehcache.xml</prop>
<prop key="hibernate.generate_statistics">false</prop>
<prop key="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</prop>
<prop key="hibernate.query.factory_class">org.hibernate.hql.internal.classic.ClassicQueryTranslatorFactory</prop>
</props>
</property>
<property name="entityInterceptor" ref="auditInterceptor" />
</bean>
I've also tried <prop key="hibernate.connection.autocommit">false</prop> with the same result.
The datasource is hiraki ds but I've tried bonecp with the same result.
#Bean
public DataSource dataSource() {
final HikariDataSource dataSource = new HikariDataSource();
// dataSource.setAutoCommit(false);
dataSource.setDriverClassName(jdbcDriver);
dataSource.setJdbcUrl(jdbcUrl);
dataSource.setUsername(jdbcUsername);
dataSource.setPassword(jdbcPassword);
dataSource.setIdleTimeout(60000);
dataSource.setMinimumIdle(0);
dataSource.setMaximumPoolSize(2);
return dataSource;
}
The code that I am using is:
final AuditUsers typ = new AuditUsers();
typ.setEntityId(1l);
typ.setLevel(29);
final Serializable typid = getSession().save(typ);
System.out.println(">>>>>>>>>>>>>> id: " + typid);
getSession().flush();
getSession().clear();
final String sqltyp = "select * from AUDIT_USERS where id = " + typid;
try(Connection con = getConnection(); Statement stm = con.createStatement()){
System.out.println("!!!" + con.getAutoCommit());
final ResultSet rs = stm.executeQuery(sqltyp);
while(rs.next()){
System.out.println("RS filename: " + rs.getString("ENTITY_ID"));
}
}catch (final Exception e) {
e.printStackTrace();
}
And I do not get the rs's next value.
If I use the doWork method the result set has values but I do not want to use it like this.
getSession().doWork(new Work() {
#Override
public void execute(final Connection connection) throws SQLException {
final Statement stm = connection.createStatement();
final ResultSet rs = stm.executeQuery(sqltyp);
while(rs.next()){
System.out.println("RS filename: " + rs.getString("ENTITY_ID"));
}
}
});
I have a HibernateDAOSupport class for setting accessing datasource and sessionfactory that are set from spring configuration xml:
public abstract class HibernateDAOSupport {
private SessionFactory sessionFactory;
private DataSource dataSource;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(final SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(final DataSource dataSource) {
this.dataSource = dataSource;
}
public void setDs(final DataSource dataSource) {
this.dataSource = dataSource;
}
public Session getSession() {
return sessionFactory.getCurrentSession();
}
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
}
And the xml:
<bean id="hibernateDAO" class="ro.asf.capone.server.dao.HibernateDAOSupport" abstract="true">
<property name="sessionFactory" ref="sessionFactory" />
<property name="dataSource" ref="dataSource" />
</bean>
I found a solution for this by changing the getConnection implementation in HibernateDAOSupport class with this:
public Connection getConnection(){
return org.springframework.jdbc.datasource.DataSourceUtils.getConnection(dataSource);
}
Thanks M. Deinum for pointing out that a new connection is offered from the datasource and not the same one gets used. I was under the impression that a single connection spans a transaction.

two transaction within one transaction hibernate

I'm making the maintenance of a app with Struts 1, Spring 3 and Hibernate 3.
I want join two services, but each service have 1 transaction. The solution that i am thinking is make 1 service with 1 transaction that include the two services. It's possible this?
-The transaction located in the DAO's.
*Service Expediente:
public void saveExpedienteCoactivo(CoaTExpc coaTExpc, Auditoria auditoria, String numeracionAutomaticaExpediente) throws Exception, BusinessException{
IExpedienteCoactivoDAO dao = (IExpedienteCoactivoDAO)getDao();
try{
//business rules
dao.saveExpedienteCoactivo(coaTExpc, numeracionAutomaticaExpediente);
catch{...}
}
*DAO Expediente:
public synchronized void saveExpedienteCoactivo(CoaTExpc coaTExpc, String numeracionAutomaticaExpediente) throws Exception {
Session sessionHibernate = this.getSessionFactory().getCurrentSession();
Savepoint savePoint = sessionHibernate.connection().setSavepoint();
try{
sessionHibernate.saveOrUpdate(coaTExpc);
sessionHibernate.connection().commit();
}catch(Exception e) {
sessionHibernate.connection().rollback(savePoint);
sessionHibernate.getTransaction().rollback();
System.out.println(this.getClass().getName()+", Error="+e);
throw new Exception(e);
}
}
*Service Resolucion:
public void saveResolucion(CoaTRcoa coaTRcoa, Auditoria auditoria) throws Exception, BusinessException{
IResolucionDAO dao = (IResolucionDAO)getDao();
try{
//business rules
dao.saveResolucion(coaTRcoa);
}catch{...}
}
*DAO Resolucion:
public synchronized void saveResolucion(CoaTRcoa coaTRcoa) throws Exception {
Session sessionHibernate = this.getSessionFactory().getCurrentSession();
Savepoint savePoint = sessionHibernate.connection().setSavepoint();
try{
//more code and sql here
sessionHibernate.saveOrUpdate(coaTRcoa);
//more code sql
sessionHibernate.connection().commit();
}catch(Exception e) {
sessionHibernate.connection().rollback(savePoint);
sessionHibernate.getTransaction().rollback();
System.out.println(this.getClass().getName()+", Error="+e);
throw new Exception(e);
}
}
*applicationContext.xml:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="dataSource"/>
</property>
<property name="mappingDirectoryLocations">
<list>
<value>classpath:gob/osinerg/spjc/configuracion/domain/mapping</value>
<value>classpath:gob/osinerg/spjc/notificaciones/domain/mapping</value>
<value>classpath:gob/osinerg/spjc/judicial/domain/mapping</value>
<value>classpath:gob/osinerg/spjc/coactiva/domain/mapping</value>
<value>classpath:gob/osinerg/spjc/common/domain/mapping</value>
<value>classpath:gob/osinerg/alfresco/domain/mapping</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>
<bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref local="transactionManager"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
Now, It's possible this?
public class ServiceTransactionImp implements ServiceTransaction{
public void doTwoSaveTransaction(){
Session sessionHibernate = this.getSessionFactory().getCurrentSession();
Savepoint savePoint = sessionHibernate.connection().setSavepoint();
try{
serviceExpediente.saveExpedienteCoactivo(coaTExpc,auditoria,numeracionAutomaticaExpediente);
serviceResolucion.saveResolucion(coaTRcoa,auditoria);
sessionHibernate.connection().commit();
}catch(Exception e) {
sessionHibernate.connection().rollback(savePoint);
sessionHibernate.getTransaction().rollback();
System.out.println(this.getClass().getName()+", Error="+e);
throw new Exception(e);
}
}
I'm not sure if it works, because the session is distinct between the proceses and if the possible rollback of saveResolucion will affect to the service of saveExpedienteCoactivo as have a commit(); the idea is execute rollback to all the proceses.
Or any other solution?

Hibernate 4 Spring 4 Could not obtain transaction-synchronized Session for current thread

#Repository
public class Init {
public static void main(String[] args) {
Init init = new Init();
init.addUser(init.getSessionFactory());
}
private SessionFactory getSessionFactory() {
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "Spring_Hibernate.xml" });
SessionFactory sf = (SessionFactory) context.getBean("sessionFactory");
return sf;
}
#Transactional
private void addUser(SessionFactory sf) {
Session session = sf.getCurrentSession();
User user = new User();
user.setName("123");
session.save(user);
session.close();
sf.close();
}
}
xml:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.warmmer.bean" />
<property name="hibernateProperties">
<!-- <value> hibernate.dialect=org.hibernate.dialect.HSQLDialect </value> -->
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop>
</props>
</property>
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate4.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
err:
INFO: Using DataSource [org.apache.commons.dbcp.BasicDataSource#6098b14d] of Hibernate SessionFactory for HibernateTransactionManager
Exception in thread "main" org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
If:
hibernate.current_session_context_class set 'thread'
then :save is not valid without active transaction
what should I do please?
You are not creating your "Init" object within the spring context, so spring would never get a chance to wrap a proxy around the method with the annotation that will manage the transaction
Try changing your class to...
package my.pkg;
// Imports etc
#Repository
public class Init {
#Autowired
private SessionFactory sessionFactory;
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "Spring_Hibernate.xml" });
Init init = context.getBean(Init.class);
init.addUser();
}
#Transactional
private void addUser() {
Session session = sessionFactory.getCurrentSession();
User user = new User();
user.setName("123");
session.save(user);
// session.close(); DON'T NEED THESE!
// sf.close();
}
}
Now you might need to add the following to your beans file so that it finds your repository...
<context:component-scan base-package="my.pkg"/>

Transaction does not rollback in hibernate

I have a problem with rollback transaction. Below I wrote some of the configuration of beans. I do 2 SQL-queries: delete and update. And when UPDATE generates exception (constraint of foreign ket), the first query (DELETE) does not rollback. Could anyone please tell me where the problem is? I wrote only some of the configuration for sake of clarity, so if it's needed more information please let me know. Thanks in adnvance!
CONTEXT:
I have DAO layer with method removeUser:
public void removeUser(final Long id) {
getHibernateTemplate().execute(new HibernateCallback() {
#Override
public Object doInHibernate(Session session) throws HibernateException, SQLException {
executeUpdate("delete from table1 where user_id = ?", session, id);
executeUpdate("update table2 set user_id = null where user_id = ?", session, id);
return null;
}
private void executeUpdate(String queryString, Session session, Long... params) {
SQLQuery query = session.createSQLQuery(queryString);
for (int paramIndex = 0; paramIndex < params.length; paramIndex++) {
Long param = params[paramIndex];
query.setParameter(paramIndex, param);
}
query.executeUpdate();
}
});
}
This method is called from within service:
public void removeUser(Long id) {
userDao.removeUser(id);
}
This service is configured via spring:
<bean name="adminUserService" parent="txProxyServiceTemplate">
... setting properties ...
</bean>
<bean id="txProxyServiceTemplate" abstract="true"
class="com.xalmiento.desknet.ui.server.service.transaction.GWTTransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="remove*">PROPAGATION_NESTED</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
<property name="nestedTransactionAllowed" value="true"/>
</bean>
Try with
<prop key="remove*">PROPAGATION_REQUIRED</prop>
I don't think all the database server does support the transaction.

Hibernate custom schema creation

<prop key="hibernate.hbm2ddl.auto">create</prop> creates a new database schema and <prop key="hibernate.hbm2ddl.auto">update</prop> create if it is not exists and update existing database schema. If I want to check whether database schema exists or not and depending on that a database schema will be created, how can I achieve this. Currently the configuration of my applicationContext.xml is:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="annotatedClasses">
<list>
<value>info.ems.models.User</value>
<value>info.ems.models.Role</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
</bean>
<bean id="dao" class="info.ems.hibernate.HibernateEMSDao" init-method="createSchema">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
And the HibernateEMSDao.java:
public class HibernateEMSDao implements EMSDao {
private final Logger logger = LoggerFactory.getLogger(getClass());
private HibernateTemplate hibernateTemplate;
public void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
public void saveUser(User user) {
hibernateTemplate.saveOrUpdate(user);
}
public List<User> listUser() {
return hibernateTemplate.find("from User");
}
public void createSchema() {
logger.info("inserting default admin user into database");
User admin = new User();
admin.setUsername("admin");
admin.setName("Admin");
admin.setEmail("admin");
admin.setPassword("21232f297a57a5a743894a0e4a801fc3");
saveUser(admin);
logger.info("Admin inserted into database");
try {
System.out.println(listUser().get(0).getId());
} catch (Exception e) {
logger.error("===================Error================");
}
}
}
It is working. What configuration will help me to gain this?
Something like:
Check an user with id=1 exists
If not create the schema
Thanks and regards.
You could disable the hibernate.hbm2ddl.auto option, check the conditions (probably using plain JDBC) and call (or don't) the create method of the SchemaExport class. This would be done in your application's initialization code (a ServletContextListener in case you are working with a web app).
An example on how to use the SchemaExport class:
AnnotationConfiguration config = new AnnotationConfiguration();
config.addAnnotatedClass(info.ems.models.User.class);
config.addAnnotatedClass(info.ems.models.Role.class);
config.configure();
new SchemaExport(config).create(true, true);

Categories

Resources