Hibernate interceptor for contextual sessions? - java

I'm trying to write a Hibernate interceptor for auditing purpose, but one that will work with thread local contextual sessions (instead of me calling openSession() every time and passing it an interceptor object).
Any guidelines/sample code on how to do this would be greatly appreciated. (My main problem is to figure out a way to give interceptor object to a contextual session when it's opened for the very first time).

why not using hibernate's audit sulotion? http://docs.jboss.org/envers/docs/index.html

if you use Hibernate only, you can set Interceptor for session with two approach.
//interceptor for global, set interceptor when create sessionFactory with Configure
sessionFactory =
new AnnotationConfiguration().configure()
.setInterceptor(new AuditTrailInterceptor())
.buildSessionFactory()
//interceptor for per Session
Session session = sessionFactory.openSession(new XxxInterceptor())
if you use Spring to create SessionFactory
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="entityInterceptor">
<bean class="your.XxxInterceptor"/>
</property>
<!-- other configuration -->
</bean>
I found one blog post which would help you. http://www.sleberknight.com/blog/sleberkn/entry/using_a_hibernate_interceptor_to

Related

Does java web application contains only one hibernate session and how to clear this hibernate session?

I am having an issue in saving and retrieving objects in database in just one request.
I want to clear the cache of our hibernate session to get the updated entity in our database.
My code looks like this:
public class SampleController{
protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)
throws Exception {
myServiceOne.doAllotsOfSaving(parameters);
//some code enhancements to remove cache in hibernate session
//without affecting the session of other user logged in.
//some fields in MyEntity class contains the old values but the actual data in database is already updated
MyEntity entity = myServiceTwo.getMyEntityByOrderNo(orderNo);
}
}
--configurations
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="hibernateProperties">
<ref local="hibernateProperties"/>
</property>
<property name="entityInterceptor">
<ref bean="auditLogInterceptor" />
</property>
</bean>
<bean id="myServiceOne" class="com.test.service.impl.MyServiceOneImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="myServiceTwo" class="com.test.service.impl.MyServiceTwoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
--configurations
Does java web application contains only one hibernate session and how to clear this hibernate session?
No any hibernate based application must use multiple sessions. Each of these sessions must be closed when they perform their task. Hibernate can manage sessions for you if you configure the same in hibernate's configuration file.
However you should have only one instance of SessionFactory per application.
To clear the session you can call the session.clear() method. It clears the session level cache.
without affecting the session of other user logged in
Since you have a web application, you have a different thread for each user for database transactions. This means each user will have a different hibernate session, so you won't have to worry about this. If by some means you're using same session for all the users, you're doing it wrong and results can be catastrophic. After some time you'll get an OutOfMemoryError because of session level cache.
You must note that you cannot disable hibernate session level cache. For this purpose you may use StatelessSession.
Session factory is long live multithreaded object.
Usually one session factory should be created for one database.
A Session is used to get a physical connection with a database. The Session object is lightweight and designed to be instantiated each time an interaction is needed with the database.
The main function of the Session is to offer CRUD operations for instances of mapped entity classes. Instances may exist in one of the following three states at a given point in time:
transient: A new instance of a a persistent class which is not associated with a Session and has no representation in the database and no identifier value is considered transient by Hibernate.
persistent: You can make a transient instance persistent by associating it with a Session. A persistent instance has a representation in the database, an identifier value and is associated with a Session.
detached: Once we close the Hibernate Session, the persistent instance will become a detached instance.

EJB 3 Sessioncontext in Spring Beans

Background:
I am using programmatic authentication (basic authentication) using java security.
I have a Stateless session bean(EJB 3). I can inject the Sessioncontext to get the security Principal, to get the user name.
In the same project I am using spring beans for JDBC and Aspect. I want to get the user name in one of the aspect(Audit) which is also a spring bean.
Question:
Is it possible to access the ejb sesssioncontext in the Spring bean. If so how to do that?
If no, how can the username be accessed from the Spring bean which is also an aspect.
Thanks,
Amit
This can be done by injecting the EJB into the spring bean. You can do it through a manual JNDI lookup (like you would in any other EJB Client) or use Spring's JndiObjectFactoryBean. With Spring's JndiObjectFactoryBean:
Inject the SessionContext into the EJB
#Resource
private SessionContext ctxt;
//getter and setter
Configure the factory bean in your bean configuration file. postageService being the EJBRef we're interested in (Configuration poached from Apress Spring Recipes)
<bean id="postageService class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiEnvironment">
<props>
<prop key="java.naming.factory.initial">
org.apache.openejb.client.RemoteInitialContextFactory
</prop>
<prop key="java.naming.provider.url">
ejbd://localhost:4201
</prop>
</props>
</property>
<property name="jndiName" value="PostageServiceBeanRemote"/>
</bean>
Wire the ejb reference into your spring bean
public class PostalServiceClient{
//wired EJB
private PostageService service;
//getter and setter
private SessionContext retrieveSessionContext(){
service.getCtxt(); //get the SessionContext from the EJB injection
}
}
kolossus, Thanks for the reply.
There is another way that I could found. Following is the Link to the previous post. So basically I did exactly the same thing :
1) Added the following line to the spring-context xml
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">
<property name="alwaysUseJndiLookup" value="true" />
</bean>
2) Added following line to the my spring bean to access the EJB's session context
#Resource(mappedName = "java:global/TopoServices/MessageRestService!com.myrest.MessageRestService")
private MessageRestService myBean;
Important thing is Jboss 7.1 does not support custom jndi any more unlike Jboss 5 so I used the default jndi.
I think this is more cleaner way to address my requirement.

With Spring #Transactionnal, how do Spring know which datasource to use?

When using #Transactionnal with Spring
How does it select the datasource on which it should open the transaction?
Is there some magic trick like proxies, threadlocals or anything?
If so, do these tricks work with any JDBC library (for Hibernate it works but what about MyBatis?)
What if there are 2 datasources?
And what if i'm calling, in a #Transactionnal service, DAO's with 2 underlying different datasources? Will it be transactionnal for both datasources or just for one of them or will it fail?
Thanks
To use multiple transaction handlers just specifiy a qualifier, and reference it. For two different DAOs with two different datasources, you'll need two different transaction managers. And of course the transactions should be going on your service classes, not DAO's directly. Its the same for any type of transactionmanager be it hibernate or plain old jdbc.
<bean id="transactionManagerOne"
class="org.springframework.orm.hibernate4.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactoryOne">
<qualifier value="One" />
</bean>
and session factory
<bean id="sessionFactoryOne"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
p:dataSource-ref="dataSourceOne"
and just set up a datasource with the ID dataSourceOne, then you cran reference the transactionmanager in code with the qualifier name :
#Transactional(value = "One")
I can provide a partial answer which is that usually when you use a PlatformTransactionManager from Spring it was associated with a single datasource when it was created. Something like this:
#Bean public PlatformTransactionManager txManager() {
return new HibernateTransactionManager(sessionFactory());
}
The SessionFactory is configured with a datasource. I suspect if you want to have multiple PlatformTransactionManagers then you can't just rely on autowiring them as beans like I did above. You might have to use the TransactionTemplate class instead and code on a slightly lower level.

How to enable multi-tenancy in Hibernate 4 with JPA?

It looks to me as though support for multi tenancy has been added to hibernate for nearly six months now and updated at least once since.
It looks fairly trivial to obtain a multi-tenant Session outside of JPA:
Session session = sessionFactory.withOptions().tenantIdentifier( "jboss" ).openSession();
But how would you enable it in an application that uses hibernate via JPA? (If possible).
Thanks in advance.
You can configure it via properties in persistence.xml as follows:
<property name="hibernate.multiTenancy" value="DATABASE"/>
<property name="hibernate.multi_tenant_connection_provider" value="com.example.MyConnectionProvider" />
<property name="hibernate.tenant_identifier_resolver" value="com.example.MyTenantIdResolver" />
If you use SCHEMA as multi-tenancy strategy hibernate.multi_tenant_connection_provider is not needed.
You can also set these properties in your code and pass them in a map to Persistence.createEntityManagerFactory(). In this case you can pass an object instance, not just a class name.
More info in Hibernate documentation.
EntityManager.getDelegate() will return underlying SessionImpl.

Using JobStoreCMT in Quartz - preventing automatic commit

I'm trying to use a JDBC Job Store in Quartz with the following code:
DateTime dt = new DateTime().plusHours(2);
JobDetail jobDetail = new JobDetail(identifier, "group", TestJob.class);
SimpleTrigger trigger = new SimpleTrigger(identifier, dt.toDate());
trigger.setJobName(identifier);
trigger.setJobGroup("group");
quartzScheduler.addJob(jobDetail, true);
quartzScheduler.scheduleJob(trigger);
And am configuring the scheduler as follows:
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false">
<property name="autoStartup" value="true" />
<property name="waitForJobsToCompleteOnShutdown" value="false" />
<property name="dataSource" ref="schedulerDataSource" />
<property name="nonTransactionalDataSource" ref="nonTXdataSource" />
<property name="quartzProperties">
<props>
<!--Job Store -->
<prop key="org.quartz.jobStore.driverDelegateClass">
org.quartz.impl.jdbcjobstore.StdJDBCDelegate
</prop>
<prop key="org.quartz.jobStore.class">
org.quartz.impl.jdbcjobstore.JobStoreCMT
</prop>
<prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
</props>
</property>
</bean>
The schedulerDataSource is a standard JNDI data source, the nonTXdataSource is configured via a simple org.springframework.jdbc.datasource.DriverManagerDataSource I have specified the job store class to be: org.quartz.impl.jdbcjobstore.JobStoreCMT and was hoping that the code:
quartzScheduler.addJob(jobDetail, true);
quartzScheduler.scheduleJob(trigger);
would not commit the job to the database when the each method is called. Basically when I call addJob the job is immediately saved to the database, the scheduleJob method causes the trigger information to be immediately saved in the database as well, but this tends to happen over two separate transactions already.
There is a fair bit of subsequent logic in the code that needs to be committed to the database together with the scheduled jobs in one transactions, however no matter what I try the jobs are committed by the scheduler to the database as soon as they methods are called. I tried in various environments Testing/Tomcat/Glassfish and various configurations of data sources but to no avail.
Can somebody point me into the direction of where I am going wrong?
Thank you.
Having thought this over a bit, now i believe you can achieve this providing your own wrapping DataSource but you should not do this. I think Quartz maintains some internal state in memory that must be in sync with the database (or at least it can do so). If you rollback a transaction or otherwise modify database state not notifying Quartz about this fact, it may not work as expected.
On the other hand you can use Quartz's pausing of the jobs to achieve similar effect: you simply create new job and pause it before adding any triggers. Then, you resume it only after you commit your transaction.
---------------------- my original answer ----------------------
I think, but I'm not sure, not tried this, that you can try the following:
You need a transaction around a code that uses DataSource.getConnection internally. To achieve that you have to use data source that'd be aware of global transaction state. I suppose that JBoss application server gives you just that (even with plain data source).
JBoss comes with a transaction manager (Arjuna) and data sources wrappers (JBoss app server internal) that are at least aware of global transaction state.
Other options include Atomikos and a XA data source, but i have less experience here.
Edit: if Quartz uses explicit COMMIT or setAutocommit(true) internally, both my suggestions would not work.
When you set datasource on SchedulerFactoryBean, spring uses below class as JobStore ( extension to Quartz's JobStoreCMT )
LocalDataSourceJobStore
This supports both transactional and non-transactional DataSource access.
Please try following
Remove property org.quartz.jobStore.class [Edit : Its ignored ,anyways]
Make sure the method which does addJob / ScheduleJob is in spring managed transaction.

Categories

Resources