I'm using a hibernate interceptor to intercept queries and alter them before they are sent to the postgresql database.
The change made on the queries is specific to every connected user(the interceptor is getting the user's information from his session).
The problem is, since i'm using spring along with hibernate, the interceptor is a singleton and made at the sessionFactory level, so it's not thread-safe.
This is the configuration of spring related to hibernate :
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:hibernate.cfg.xml"/>
<property name="entityInterceptor">
<ref bean="myEntityInterceptor"/>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${jdbc.dialect}</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="myEntityInterceptor" class="dao.generic.HibernateInterceptor"/>
And the interceptor class :
#Component
public class HibernateInterceptor extends EmptyInterceptor {
private static final long serialVersionUID = 1L;
final static Logger logger = Logger.getLogger(HibernateInterceptor.class);
#Override
public String onPrepareStatement(String sql) {
String ps = super.onPrepareStatement(sql);
if (SecurityContextHolder.getContext().getAuthentication() != null) {
UserDetails ud = (UserDetails) SecurityContextHolder
.getContext().getAuthentication().getDetails();
ps = ps.replaceAll("dynamic.", ud.getDb() + ".");
}
return ps;
}
}
So I'm lookinf for a way to make this interceptor thread-safe by attaching a seperate instance for every client session and not only one instance used by all the users.
Any help will be appreciated..
First, your code seems thread safe to me, so I'm not sure if you really need this, but in case you do, you can set the interceptor to be an instance per session instead of a shared instance by creating it at Session level on HibernateTransactionManager by setting the property entityInterceptorBeanName
From Spring Doc (HibernateTransactionManager.setEntityInterceptorBeanName):
Typically used for prototype interceptors, i.e. a new interceptor instance per session.
So, make sure your interceptor bean is scoped as prototype
Take a look at this post
SingleTon objects can be treated as thread safe, as long as the object does not hold any state information (i.e., instance variables with getters and setters).
So, here in this example, HibernateInterceptor object is thread safe.
Related
I have a Spring Boot 2 + Hibernate 5 Multi-tenant application that connected to multiple oracle databases. when I use JpaRepository, everything is fine, ie after recieving http request, the 'Interceptor' detects the data source and the 'MultiTenantConnectionProvider' implementation selects the correct DataSource to be used by TransactionManager.
What is the problem?
I want to use TransactionInterceptor to handle transactions on some methods(with method names) from specific beans(that extends JdbcDaoSupport or inject JdbcDaoSupport implementations). Sorry if I explained a little confusing.
But for every http request, the data source of that beans is immutable(the default data source). while injected repositories operate on the correct data source within the correct transaction.
<bean id="txProxyTemplate" abstract="true"
depends-on="transactionManager"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>
</props>
</property>
</bean>
<bean id="genericJdbcDao" parent="txProxyTemplate">
<property name="target">
<bean class="org.broker.dao.impl.BaseGenericJdbcDAOImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
</property>
</bean>
Can anyone help solve this problem?
I finally solved the problem. Follow the code section:
#Bean("jdbcTemplate")
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public JdbcTemplate jdbcTemplate(
#Qualifier("tenantIdentifierResolver") TenantIdentifierResolverImpl tenantResolver,
#Qualifier("tenantConnectionProvider") DataSourceMultiTenantConnectionProviderImpl dataSourceProvider) {
return new RhaJdbcTemplate(dataSourceProvider.selectDataSource(tenantResolver.resolveCurrentTenantIdentifier()));
}
#Bean(value = "transactionInterceptor")
public TransactionInterceptor transactionInterceptor(
#Qualifier("txProxyTemplateConfig") Properties txProxyTemplateConfig,
#Qualifier("transactionManager") PlatformTransactionManager transactionManager) {
TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
transactionInterceptor.setTransactionAttributes(txProxyTemplateConfig);
transactionInterceptor.setTransactionManager(transactionManager);
return transactionInterceptor;
}
#Bean("genericJdbcDAO")
#Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public ProxyFactoryBean genericJdbcDAO(
#Qualifier("jdbcTemplate") JdbcTemplate jdbcTemplate) throws ClassNotFoundException {
BaseGenericJdbcDAOImpl genericJdbcDAO = new BaseGenericJdbcDAOImpl();
genericJdbcDAO.setJdbcTemplate(jdbcTemplate);
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setTarget(genericJdbcDAO);
proxyFactoryBean.setProxyInterfaces(new Class[] {BaseGenericJdbcDAO.class});
proxyFactoryBean.setInterceptorNames("transactionInterceptor");
return proxyFactoryBean;
}
Then inject BaseGenericJdbcDAO into the controller, as a sample:
#RestController
public class VoucherLineRest {
#Resource(name = "genericJdbcDAO")
private BaseGenericJdbcDAO genericJdbcDAO;
}
You will see that for every request the correct data source selected and TransactionInterceptor intercept every method called from BaseGenericJdbcDAO bean and apply the correct transaction propagation on it.
I hope you enjoy this experience.
I am working in a project using Spring, Spring Data JPA, Spring Security, Primefaces...
I was following this tutorial about dynamic datasource routing with spring.
In this tutorial, you can only achieve dynamic datasource switching between a pre-defined datasources.
Here is a snippet of my code :
springContext-jpa.xml
<bean id="dsCgWeb1" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName.Cargest_web}"></property>
<property name="url" value="${jdbc.url.Cargest_web}"></property>
<property name="username" value="${jdbc.username.Cargest_web}"></property>
<property name="password" value="${jdbc.password.Cargest_web}"></property>
</bean>
<bean id="dsCgWeb2" class="org.apache.commons.dbcp.BasicDataSource">
// same properties, different values ..
</bean>
<!-- Generic Datasource [Default : dsCargestWeb1] -->
<bean id="dsCgWeb" class="com.cargest.custom.CargestRoutingDataSource">
<property name="targetDataSources">
<map>
<entry key="1" value-ref="dsCgWeb1" />
<entry key="2" value-ref="dsCgWeb2" />
</map>
</property>
<property name="defaultTargetDataSource" ref="dsCgWeb1" />
</bean>
What i want to do is to make the targetDataSources map dynamic same as its elements too.
In other words, i want to fetch a certain database table, use properties stored in that table to create my datasources then put them in a map like targetDataSources.
Is there a way to do this ?
Nothing in AbstractRoutingDataSource forces you to use a static map of DataSourceS. It is up to you to contruct a bean implementing Map<Object, Object>, where key is what you use to select the DataSource, and value is a DataSource or (by default) a String referencing a JNDI defined data source. You can even modify it dynamically since, as the map is stored in memory, AbstractRoutingDataSource does no caching.
I have no full example code. But here is what I can imagine. In a web application, you have one database per client, all with same structure - ok, it would be a strange design, say it is just for the example. At login time, the application creates the datasource for the client and stores it in a map indexed by sessionId - The map is a bean in root context named dataSources
#Autowired
#Qualifier("dataSources");
Map<String, DataSource> sources;
// I assume url, user and password have been found from connected user
// I use DriverManagerDataSource for the example because it is simple to setup
DataSource dataSource = new DriverManagerDataSource(url, user, password);
sources.put(request.getSession.getId(), dataSource);
You also need a session listener to cleanup dataSources in its destroy method
#Autowired
#Qualifier("dataSources");
Map<String, DataSource> sources;
public void sessionDestroyed(HttpSessionEvent se) {
// eventually cleanup the DataSource if appropriate (nothing to do for DriverManagerDataSource ...)
sources.remove(se.getSession.getId());
}
The routing datasource could be like :
public class SessionRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
HttpServletRequest request = ((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
return request.getSession().getId();
}
#Autowired
#Qualifier("dataSources")
public void setDataSources(Map<String, DataSource> dataSources) {
setTargetDataSources(dataSources);
}
I have not tested anything because it would be a lot of work to setting the different database, but I thing that it should be Ok. In real world there would not be a different data source per session but one per user with a count of session per user but as I said it is an over simplified example.
The datasource used by a thread might change from time to time.
Should pay attention to concurrency, applications might get concurrency issues in concurrent environment.
thread-bound AbstractRoutingDataSource sample
It can be achieved with AbstractRoutingDataSource and keeping the information in the thread-local Variable. Here is a beautiful working example you can refer to:
Multi-tenancy: Managing multiple datasources with Spring Data JPA
I have a problem, where Spring is injecting proxy to DAO object into service, but this service is injected into controller it is concrete class. This does not allow me to use service-wide transaction and launches transaction for each DAO call separatly. It's behavious I would expect.
Configuration:
Controller is class with #Controller annotation and constructor DI.
Service:
#Component
#Transactional
public class UserServiceImpl implements UserService { ...}
Dao:
#Component
#Transactional
public class UserDaoImpl implements UserDao {
JPA Config:
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" >
<property name="dataSource" ref="dataSource"/>
<property name="persistenceUnitName" value="xxxPersistenceUnit"/>
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven />
Anyone has any idea why is it happening?
Most likely your UserServiceImpl is created in the servlet context by mistake - please check context:component-scan expressions to check that only Controller classes are included there.
See #Service are constructed twice
for examples of component scan filters.
For example if transaction manager bean and <tx:annotation-driven> are declared in the root web app context, then the transaction proxies will be created only for the beans in the root app context (from Spring Documentation):
BeanPostProcessor interfaces are scoped per-container. This is only
relevant if you are using container hierarchies. If you define a
BeanPostProcessor in one container, it will only do its work on the
beans in that container. Beans that are defined in one container are
not post-processed by a BeanPostProcessor in another container, even
if both containers are part of the same hierarchy.
Less likely is that the transactional configuration of the user service is configured to use another transaction manager(or another default propagation), but in that case TransactionInterceptor invocation would be present in the stack trace of DAO method.
It's absolutely OK to have #Transactional on the DAO classes in Spring, if you understand what you are doing - the idea that repository or DAO cannot open transactions comes from the dark times when you had to create a boilerplate code to open transactions and it was hard to manage the transaction instances(and you could not be sure on how transactions are managed). But when you are using declarative configuration the things are not that bad. Spring promotes convention-over-configuration style where most methods use Propagation.REQUIRED transaction mode. In Spring Propagation.REQUIRED is the default mode when you decorate methods with #Transactional(this propagation is hardcoded to #Transactional annotation declaration), that means that the new logical transaction is mapped to the same physical transaction, so decorating your DAO classes with #Transactional is harmless.
See http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/transaction.html#tx-propagation for the reference on transaction propagation in Spring
In Spring Data JPA(I'm pretty sure that they know what they are doing), for example, CRUD methods on repository instances are transactional by default. That may be useful in some cases, the mechanism is same as when Hibernate allows you to get() some arbitrary objects from the Session for displaying without declaring an explicit transaction(of course it does not mean that the framework somehow manages to go without transaction - it's implicit in this case).
I'm having a little trouble understanding what you're saying, but it appears that you're surprised that you're getting a new transaction for every DAO call, instead of just on the service call. Unfortunately, that's exactly what you've specified by putting "#Transactional" on the DAO class. Your DAO should not be transactional, at least if you're following the usual pattern. If I've understood you correctly, you should remove the #Transactional annotation on your DAO class.
The other responders are correct in that you should not be annotate your DAO as #Transactional, but to really understand what is happening you should refer to the Transaction Propagation section in the Spring Reference Manual. The default propagation when using #Transactional is REQUIRES_PROPAGATION, so review that specifically.
Your question isn't that specific so I'm not sure exactly what you're looking for.
Edit: Upon re-reading your question it's possible that there may be an issue with your component scanning. Check to make sure that your <tx:annotation-driven /> is in the same application context where you're component scanning your service classes.
You shouldn't use that "#Transactional" annotation in your DAO object. You are defining it in your Service and that will grant that all your DAOs methods, called inside a service method, are executed within the same transaction, which seems to be exactly what you want, when you say "service-wide transaction", right?
Also, as suggested, you might want to change your annotation from "#Component" to "#Service" in UserServiceImpl and to "#Repository" in UserDaoImpl.
Best regards.
I'm working on a Spring MVC + Hibernate + JPA app with a user registration form and I decided to use a JSR-303 validator to check whether the username already existed in the DB:
public class UniqueUsernameValidator implements ConstraintValidator<VerifyUniqueUsername, String> {
#Autowired
UserService userService;
#Override
public void initialize(VerifyUniqueUsername constraintAnnotation) {
}
#Override
public boolean isValid(String username, ConstraintValidatorContext context) {
return username!=null && userService.findByUsername(username) == null;
}
}
It is very straightforward and validation worked great on my controller:
....
public String signup(#Valid #ModelAttribute("newUser") User user, BindingResult newUserBeanResult)
.....
The current problem I'm facing is that after I get my User object validated and I call:
userService.save(user);
Which implements CrudRepository, I get a NullPointerException. For some reason UserService is injected during validation on the controller but not when I call CrudRepository.save().
I saw similar posts such as this:
#Autowired bean null in ConstraintValidator when invoked by Sessionfactory.getCurrentSession.merge
and this:
hibernate validator without using autowire
but I was wondering if someone has run into this before. I would think that injecting beans to access the database on a validator is fairly common.
As a workaround I added a check for null on userService but it doesn't feel right.
Is this expected behaviour? Are these validations supossed to fire before calling CrudRepository.save()?
Am I suppossed to handle "manually" hibernate events? In this case pre-insert
I ended up solving this issue by instructing Spring's EntityManagerFactoryBean to use my validator bean (more accurately, hibernate will now be using Spring's validator):
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="packagesToScan" value="some.packages"/>
<property name="jpaPropertyMap">
<map>
<entry key="javax.persistence.validation.factory" value-ref="validator" />
</map>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.max_fetch_depth">3</prop>
<prop key="hibernate.jdbc.fetch_size">50</prop>
<prop key="hibernate.jdbc.batch_size">10</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
However, this threw a StackOverflow error :)
Apparently the cause of this issue was that my validator was using a finder method (findByUsername) and finder methods trigger a hibernate flush, which in turn triggers a validation. This loops indefinitely until you get the most famous exception there is.
So...I fixed this by changing the validators to use the EntityManager directly (instead of the CRUD repository) and temporarily change the FlushModeType to COMMIT. Here is the example:
public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, String> {
#PersistenceContext
private EntityManager em;
#Autowired
UserService userService;
#Override
public void initialize(UniqueUsername constraintAnnotation) {
}
#Override
public boolean isValid(String username, ConstraintValidatorContext context) {
try {
em.setFlushMode(FlushModeType.COMMIT);
return userService.findByUsername(username) == null;
} finally {
em.setFlushMode(FlushModeType.AUTO);
}
}
}
This solves the issue where the validator was using the finder function which triggered a hibernate flush which in turn was triggering the validator causing a StackOverflowError.
When the validation logic is invoked in response to the save method it is done by hibernate. The validator object is created by hibernate so the spring #AutoWired will not work.
One option to fix this is to use the #Configurable annotation and enable load time weaving to make sure that even when hibernate instantiates the validator object spring gets to inject dependencies into it.
Situation - Story time:
I "inherited" a program, a rather simple webservice for accessing a databse. This program had a flaw somewhere: It tried to update a table it had no update grant for. The program has only the right to update a Database Queue (Oracle), to save information who accessed what. This was undesired behaviour, and by now I fixed it. Note: This is not relevant to this question per se, its just the cause that led me to this question.
The program uses Spring + Hibernate for managing and accessing the data and the transactions.
Because the program is in high demand and the error was deemed intolerable, I had the quick idea to add a hotfix, simply to force a rollback on each transaction until I find the part of the software that actually manipulates data it should not manipulate.
The software uses 2 #Transactional annotations. One over the whole process, and another on the part thats writing the log data. This second one was used with the Requires_New propagation setting. As far as I understood it, this second one would always create a new Transaction and flush it when its span (in this case: one method) was over.
I then added a direct Rollback-Statement TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); after the nested transaction was already over, to rollback the outer transaction (therefor undoing the manipulation). But it didn't work that way. Both transaction were rolled back. Can anyone give me any pointers to why this happened? Its kinda conflicting what I thought I knew how the system works. Does currentTransactionStatus() include ALL transactions associated with the current session?
Relevant Code snippet (shortend for clarity)
#Override
#Transactional
#PreAuthorize(Rollen.INFOVN)
public InfoVNAntwort infoVNAnfrage(final InfoVNAnfrage infoVNAnfrage) {
// extract data from request
final InfoVNDatenhalter datenhalter = (InfoVNDatenhalter) this.getController().erzeugeNeuenDatenhalter(ProzessNamen.INFOVN);
datenhalter.setAnfrageFin(StringUtils.trimToNull(infoVNAnfrage.getFIN()));
datenhalter.setAnfrageZB2(StringUtils.trimToNull(infoVNAnfrage.getZB2()));
final String username = this.getCurrentUserName();
datenhalter.setBenutzerkennung(username);
datenhalter.setErgaenzungstext(infoVNAnfrage.getErgaenzungstext());
datenhalter.setAnfragegrund(infoVNAnfrage.getAnfrageanlass());
// actual fetch of database data
final DialogAntwort da = (DialogAntwort) this.getController().verarbeite(datenhalter);
// convert to ws response
final InfoVNAntwort vnAntwort = this.getMapper().map(da, InfoVNAntwort.class);
// log who did what
this.erstelleBewirt(vnAntwort, infoVNAnfrage, new DateTime(), username);
// roll back outer transaction
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return vnAntwort;
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
private void erstelleBewirt(final InfoVNAntwort vnAntwort, final InfoVNAnfrage infoVNAnfrage, final DateTime zeitpunktAuskunft, final String username) {
final InfoVNBewirt eintrag = new InfoVNBewirt();
eintrag.setZeitpunktErteilungAuskunft(zeitpunktAuskunft);
eintrag.setSteuerelement(STEUERELEMENT_INFOVN);
eintrag.setAnfrageAnlass(infoVNAnfrage.getAnfrageanlass());
this.completeEintrag(username, eintrag);
this.logdatenPersister.persistiereLogdaten(eintrag);
}
Database-Connection:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="packagesToScan">
<list>
<value>de.mm.vwn</value>
<value>de.mm.cfo.allgemein.kenauthent</value>
<value>de.mm.cfo.infovn.logdaten</value>
</list>
</property>
<property name="hibernateProperties" ref="hibernateProperties"></property>
</bean>
<bean id="hibernateProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.hbm2ddl.auto">none</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">false</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.region.factory_class">net.sf.ehcache.hibernate.EhCacheRegionFactory</prop>
<prop key="hibernate.jdbc.use_scrollable_resultset">true</prop>
<prop key="hibernate.jdbc.batch_size">25</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="dataSource" ref="dataSource" />
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<tx:annotation-driven />
OK. The problem is the one I thought.
Here's how Spring makes beans transactional: when you get a transactional bean from the Spring bean factory, or thanks to dependency injection, Spring doesn't give you an instance of your bean class. It gives you a proxy which has the same interface as your bean class, and delegates all the method calls to an instance of your bean class, except it starts a transaction (if needed) before invoking the method, and rollbacks/commits the transaction (if needed) after the method has returned:
Client ----> transactional proxy ----> bean.infoVNAnfrage()
If, from your bean class instance (InfoVNAntwort), you call another method of the same bean, the method invocation doesn't go through the proxy:
Client ----> transactional proxy ----> bean.infoVNAnfrage() ----> bean.erstelleBewirt()
So Spring has no way to start a new transaction for erstelleBewirt().
The easier way is to put the erstelleBewirt() method into another transactional Spring bean, and to inject this other Spring bean into your current bean:
Client ----> transactional proxy ----> bean.infoVNAnfrage() ----> transactional proxy ----> otherBean.erstelleBewirt()
I think you just need to use single #Transactional on method(You can also use on class level). Like this example :
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {//transaction1
// do something
updateFoo1();
}
public void updateFoo1() {//transaction 2
// do something
}