How to set a Hibernate interceptor for an EntityManagerFactory - java

I'm using JPA, but I need to unwrap my EntityManagerFactory, so I can add an interceptor to the Session. Afterward I want to wrap the Session back to a EntityManager.
"Why not just use Session instead of EntityManager?" We still want to reduce the impact of a possible technology migration
For what do I want to use Interceptor:
I can resume the problem in the following way: The project works running queries on a alarms database. Each place has one database with a Alarm Table, but the client wants to have a single database, where we will have to create multiple "Alarm Table", one for each place (ex: Table_Alarm-Place1, Table_Alarm-Place2). That means we would have multiple tables for the same entity, the interceptor has the goal of changing the Table name generate by hibernate in the final SQL
How I pretend to use the interceptor:
public class SqlInterceptor extends EmptyInterceptor {
private String tableSufix;
private static final Logger LOGGER = LoggerFactory.getLogger(SqlInterceptor.class);
public SqlInterceptor(String tableSufix) {...}
#Override
public String onPrepareStatement(String sql) {
String finalSql;
//Manipulated SQL (parsed by Hibernate)
return finalSql;
}
}
The project uses JPA 2.1 and Hibernate 4.3.11.Final

A super easy way to override Hibernate's EmptyInterceptor is to just this in properties file
spring.jpa.properties.hibernate.session_factory.interceptor=<fully-qualified-interceptor-class-name>
Cheers :)

You can supply the Interceptor when building the EntityManagerFactory:
String persistenceUnitName = ...;
PersistenceUnitInfo persistenceUnitInfo = persistenceUnitInfo(persistenceUnitName);
Map<String, Object> configuration = new HashMap<>();
configuration.put(AvailableSettings.INTERCEPTOR, new SqlInterceptor());
EntityManagerFactoryBuilderImpl entityManagerFactoryBuilder = new EntityManagerFactoryBuilderImpl(
new PersistenceUnitInfoDescriptor(persistenceUnitInfo), configuration
);
EntityManagerFactory emf = entityManagerFactoryBuilder.build();

It looks like you want to have a multi-tenant database. I had a similar issue before and implemented an interceptor using aspectj to set the filter properly. Even if you do not go for the filter option, you can grab te session everytime it is created using aspectj, as below.
public privileged aspect MultitenantAspect {
after() returning (javax.persistence.EntityManager em): execution (javax.persistence.EntityManager javax.persistence.EntityManagerFactory.createEntityManager(..)) {
Session session = (Session) em.getDelegate();
Filter filter = session.enableFilter("tenantFilter");
filter.setParameter("ownerId", ownerId);
}
}
In the sample below, I just set the filter that needs to be configured on the entity you need to filter:
#Entity
#FilterDef(name = "tenantFilter", parameters = #ParamDef(name = "ownerId", type = "long"))
#Filters({
#Filter(name = "tenantFilter", condition = "(owner=:ownerId or owner is null)")
})
public class Party {
}
Of course, to use the filter instead of the table name, you will have to add a column to differentiate the tables - which I believe to be better than having multiple table names.

You can store all the necessary information in a static ThreadLocal instance and read it afterwards.
This way you avoid the complications with session scoped interceptors and you can use other mechanisms to achieve the same goal (for example with a session factory scoped interceptor which is a bit easier to configure).

Why not simply do the following:
EntityManagerFactory entityManagerFactory = // created from somewhere.
SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
// do whatever you need with the session factory here.
// Later in your code, initially use EntityManager and unwrap to Session.
EntityManager entityManager = entityManagerFactory.createEntityManager();
Session session = entityManager.unwrap(Session.class);
Basically, rather than trying to get a Session and then wrapping it back into an EntityManager, simply pass a JPA EntityManager around and unwrap it to Session on an as-needed basis.

Related

Spring Boot EhCache: Storing a List of objects without a key

I am using the #PostConstruct annotation on application start to query the entire list result from the DB and am storing it as a static global variable. I am parsing this result list and getting the responses I need. As shown below:
private static List<Object[]> allObjects;
#PostConstruct
public void test() {
System.out.println("Calling Method");
Query q = entityManager.createNativeQuery(query);
List<Object[]> resultList = (List<Object[]>) q.getResultList();
allObjects = resultList;
}
However, I would like to use ehcache to store the result list so I can refresh the cache at any time or remove the items from the cache. Is it possible to store a result list (without a key) in the cache instead of storing it as a global variable?
If you are working with spring boot than using spring cache abstraction is the most natural and recommended way for any caching needs (including with EhCache). It'll also solve the problem that you are trying to solve. Please setup the EhCacheManager as outlined in Spring Boot Ehcache Example article. Post this setup, separate the dbloading routine in a new bean and make it cache enabled. To pre-load the cache on startup, you can invoke this bean's method in any other bean's postconstruct. Following outline will give you fully working solution.
#Component
public class DbListProvider {
#Cacheable("myDbList")
public List<Object[]> getDbList() {
System.out.println("Calling Method");
Query q = entityManager.createNativeQuery(query);
List<Object[]> resultList = (List<Object[]>) q.getResultList();
return resultList;
}
}
// In your existing post construct method, just call this method to pre-load
// these objects on startup Please note that you CAN NOT add this PostConstruct
// method to DbListProvider as it will not trigger spring boot cache
// abstraction responsible for managing and populating the cache
public class Myinitializer {
#Autowired
private DbListProvider listProvider;
#PostConstruct
private void init() {
// load on startup
listProvider.getDbList();
}
}
You can further inject DbListProvider bean anywhere in code base which gives you additional flexibility (should you need that).
You can use #CachePut, #CacheEvict as per your eviction policies without having to worry about EhCache behind the scene. I'll further recommend to understand all options available from spring cache and use them appropriately for your future needs. Following should help -
A Guide To Caching in Spring
Spring Cache Abstraction
Hope this helps!!

How Do I use a bunch of DAO's using in-memory derby database using spring autowiring.?

I have a collection of DALS such as Transaction DAO, Billing DAO etc. Each of them have CRUD operations and I need to use that methods in my project using spring autowiring. In some of the projects that I have checked out, I saw that they are fetching the data inmemory by querying. However, I have to use the DAL's that have already been written and have all the CRUD operations.
For example:
#Autowired
TransactionDAO transactionDAO
#Autowired
BillingDAO billingDAO
#Test
public void testImplementSearchMethodForDAO() throws Exception{
TransactionVO transactionVO = getTransVO();
BillingVO billingVO = getBillingVO();
List<TransactionVO> VOList1 = transactionDAO.searchList(transactionVO);
List<BillingVO> VOList2 = billingDAO.searchList(billingVO);
assertThat(VOList1.size()).isEqualto(1));
assertThat(VOList1.size()).isEqualto(1));
}
(Assuming I added one VO value in each table).
If you need any more clarifications, I will be glad to provide you.
You can use setters and use the #Autowire annotation on the setters. Your test code must inject the DAOs using these setters. In you tests you build in mem: db instance and build a connection pool or datasource or whatever you need and then build the DAOs based of that.
Your setup() would something like this
BoneCPConfig config = new BoneCPConfig();
config.setJdbcUrl("jdbc:hsqldb:mem:test_common;shutDown=false");
config.setUsername("sa");
config.setPassword("");
JdbcTemplate dataSource = new BoneCPDataSource(config);
jdbcTemplate = new JdbcTemplate(dataSource);
//If you are using named queries
NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
//create necessary tables etc here.
setupDb(jdbcTemplate);
SomeBean anotherBean = new SomeBean();
YourDAO dao = new YourDAOImpl();
dao.setNamedJdbcTemplate(namedParameterJdbcTemplate);
dao.setSomeOtherBean(anotherBean);
//Mimic spring container if you implement InitialzingBean
dao.afterPropertiesSet();
Once all the dependencies are injected you run your tests as usual.

Calling a Native Query in the same request life cycle as a JPA managed query causes automatic persist

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;
}

Proper Usage of Entity Manager from EntityManagerFactory

I am having some trouble understanding the proper usage of entity manager when querying for objects and or deleting/creating. Right now for any database transactions I have several service methods that open and close new entity managers like so:
public static Long getCountApplicants(String active){
EntityManager entityManager = factory.createEntityManager();
long value = (Long) entityManager.createQuery("select count(distinct a) from Applicant a where a.active = " +active).getSingleResult();
System.out.println("get count app query");
entityManager.close();
return value;
}
Since I have a Java EE app with a persistence.xml file that defines my unit, why can't I declare only one from my factory and use it throughout the life of my app? Is there any performance or memory leak issues with using just one of these objects?
Update:
I am using the following stack, Spring Framework/JPA Eclipselink
Long story short: Since you're using Spring, you're better off defining the Entity Manager as an attribute of your DAO like so:
#PersistenceContext
EntityManager em;
Your code then becomes:
public static Long getCountApplicants(String active){
long value = (Long) em.createQuery("select count(distinct a) from Applicant a where a.active = " +active).getSingleResult();
System.out.println("get count app query");
return value;
}
This will work only with Non Extended Persistence Contexts.
The EntityManager injected by Spring will be threadsafe.
Whether you need to configure an LocalEntityManagerFactorBean application-managed or LocalContainerEntityManagerFactoryBean container-managed Entity Manager is just a matter of configuration in the Spring configuration files.
EntityManager is generally not threadsafe when application managed.
http://docs.oracle.com/javaee/6/tutorial/doc/bnbqw.html
However, if you're using a container managed version, it should be. You would inject it:
#PersistenceContext
EntityManager entityManager;
Spring is one such container that can do this. The link above is a helpful resource.

hibernate jpa: Session is closed!

Application is based on Spring 2.5.5 and hibernate 3.2.0 GA.
I have the following method in my DAO that gets MessageEntities attached to the specified User:
public MessageEntity findByUserId(int userId) {
List<MessageEntity> result = (List<MessageEntity>) em.createNamedQuery(MessageEntity.Q_BY_USER_ID).setParameter("userId", userId).getResultList();
if (!result.isEmpty()) {
return result.get(0);
} else {
return null;
}
}
I need to call this method from my integration test to check whether system's behaviour is valid. As long as this method is not transactional, all I get is org.hibernate.SessionException: Session is closed!. The easiest way to avoid this is to mark findByUserId method with #Transactional(readOnly = true). But as I understand, transaction management should be the duty of service tier to avoid unnecessary transactions creation. So, my question is: how can I properly get away from SessionException?
You need to perform all your database actions within a transaction scope. As you identified its usually considered good design to let the service layer of your database model deal with transactions. The only constraint then becomes that you must invoke your service model to get within the transaction scope, which might be undesirable during test.
I would recommend to make use of the testing fascilites provided by spring. See 9.3.2.3 Transaction management
You could also manually create a transaction before testing your method, e.g., by
Session sess = factory.openSession();
Transaction tx = null;
// try catch
tx = sess.beginTransaction();
findByUserId(userId);
tx.commit();
tx.rollBack();
Put the following annotations on the top of your test class.
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional
#ContextConfiguration(locations = "classpath:/META-INF/spring/applicationContext.xml")
Also I wouldn't worry about putting additional #Transactional in DAOs.
Spring usually checks to see if you are already in a transaction (with in the same thread) before it creates another.
"But as I understand, transaction
management should be the duty of
service tier to avoid unnecessary
transactions creation."
This is more of a design choice (Spring Roo for example violates this)
You can use this annotation on your controller method:
#Transactional(readOnly = true)

Categories

Resources