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
}
Related
I am working on a legacy application. It has a main controller, JobController which is extended from org.springframework.web.servlet.mvc.multiaction.MultiActionController. This application was running previously (I don't know how) but currently during start-up I get an error related to methodNameResolver.
The error says "Invalid property 'methodNameResolver' of bean class [packageName.JobController]: Bean property 'methodNameResolver' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?"
My dispatcher.xml is like below
<bean class="packageName.JobController">
<property name="methodNameResolver">
<bean class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
<property name="mappings">
<props>
<prop key="/login.htm">hndLoginHTM</prop>
<prop key="/login.ajax">hndLoginAJAX</prop>
<prop key="/logout.ajax">hndLogoutAJAX</prop>
<prop key="/submit.data">hndSubmitData</prop>
<prop key="/get.log">hndGetLog</prop>
<prop key="/getJdbc.log">hndGetJdbcLog</prop>
<prop key="/vtInsert.deneme">vtInsertTest</prop>
</props>
</property>
</bean>
</property>
</bean>
When I replace "packageName.JobController" with "MultiActionController" then I do not get this error, however since MultiActionController is a spring class it does not include my methods (hndLoginHTM, hndLoginAJAX) so this causes a new error normally.
In summary, I thşnk that Spring does not understand that my controller is an instance of MultiActionController so it gives error to the property.
I already attempted to write the bean definition in various ways but nothing worked. Any help will be appreciated.
You may offer to upgrade the Spring version and use annotations.It is in our plans, but before doing that we need to make this version work and do some tests.
Regards,
Ferda
Although this kind of configuration was very common in legacy java applications (my internet search result says so), I could find no way to fix this exception. I waited enough time I also could not get any suggestion to fix this.
So, I changed my plan and upgraded the Spring version and changed the method mappings to annotations. At this point I do not get this exception.
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.
This is regarding Spring OpenSessionInViewFilter using with #Transactional annotation at service layer.
i went through so many stack overflow post on this but still confused about whether i should use OpenSessionInViewFilter or not to avoid LazyInitializationException
It would be great help if somebody help me find out answer to below queries.
Is it bad practice to use OpenSessionInViewFilter in application
having complex schema.
using this filter can cause N+1 problem
if we are using OpenSessionInViewFilter does it mean #Transactional not required?
Below is my Spring config file
<context:component-scan base-package="com.test"/>
<context:annotation-config/>
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="resources/messages" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/jdbc.properties" />
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.databaseurl}" p:username="${jdbc.username}"
p:password="${jdbc.password}" />
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${jdbc.dialect}</prop>
<prop key="hibernate.show_sql">true</prop>
<!--
<prop key="hibernate.hbm2ddl.auto">create</prop>
-->
</props>
</property>
</bean>
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
OpenSessionInView is a servlet filter than just Open a hibernate session and store it in the SessionHolder for the thread that is serving the request. With this session opened, hibernate can read the Lazy initialized collections and objects when you use this in the rendering stage of the request. This session can be accessed when you invoke SessionFactory.getCurrentSession().
But, OpenSessionInView just opens the session and it doesn't begin any transactions. With a session opened you can read objects from database but, if you want to do something in a transaction you need #Transactional annotations or other mechanism to demarcate the begin and the end of the transaction when you want.
Then the answer of the questions:
Is it bad practice to use OpenSessionInViewFilter in application having complex schema.
This is a good practice if you need avoid the LazyInitializationException and the overload is just open new Hibernate Session and close it at the end of the request for each request.
Using this filter can cause N+1 problem
I use this filter in many projects and not cause any problem.
if we are using OpenSessionInViewFilter does it mean #Transactional not required?
No. You only have a Hibernate Session opened in the SessionHolder of the thread, but if you need Transactions you need put #Transactional.
Throwing in my 0.02c here (and expanding on Fernando Rincon's excellent answer):
You shouldn't be using a OpenSessionInView filter just because you need to get around a LazyInitializationException. Its just going to add another layer of confusion and complexity to your system. You should know from your system design exactly where you are going to need to access collections on the front end. From there, it's easy and (in my experience) more logical to build a controller method to call a service method to retrieve your collection.
However if you have another problem that using the OpenSessionInView filter solves, and as a happy side effect you then have a session open, then I don't see the harm in using it to access your collections. However, I'd say that if you use the OpenSessionInView to fetch a collection object in one place, you should refactor your code in other places to do the same thing so as the strategy used to fetch collections is standardised across your application.
Weigh up the costs of this refactor against the cost of writing the controller & service methods to determine if you should be using a OpenSessionInView filter.
OpenSessionInViewFilter is a servlet filter that binds a hibernate session to http request and for all db operations, transactional and non transactional, same hibernate session is used for a given http request. This exposes db layer to web layer that makes it anti-pattern.
My experience is that this makes the code difficult to debug when we want to make changes to java objects and do not want those to get reflected in database. Since the hibernate session is always open, it expects to flush the data in database.
This should be used only when JS base rest services are there with no service layer in between.
The typical usage pattern for OpenSessionInViewFilter is that some Entity is lazily loaded but during the view rendering phase the view needs some attribute of this Entity that was not loaded initially thus necessitating the need to fetch this data from the database. Now typically the transaction demarcation is made to happen in the service layer of your web application so by the time the view rendering takes place the view is working with a detached entity which results in a LazyInitializationException when accessing the unloaded attribute.
From this url https://developer.jboss.org/wiki/OpenSessionInView :
The problem
A common issue in a typical web-application is the rendering of the view, after the main logic of the action has been completed, and therefore, the Hibernate Session has already been closed and the database transaction has ended. If you access detached objects that have been loaded in the Session inside your JSP (or any other view rendering mechanism), you might hit an unloaded collection or a proxy that isn't initialized. The exception you get is: LazyInitializationException: Session has been closed (or a very similar message). Of course, this is to be expected, after all you already ended your unit of work.
A first solution would be to open another unit of work for rendering the view. This can easily be done but is usually not the right approach. Rendering the view for a completed action is supposed to be inside the first unit of work, not a separate one. The solution, in two-tiered systems, with the action execution, data access through the Session, and the rendering of the view all in the same virtual machine, is to keep the Session open until the view has been rendered.
As an alternative, consider loading the Entity with just the right amount of data required by your view. This can be accomplished by using DTO projections. This article lists some of the downsides of using the Open Session In View pattern : https://vladmihalcea.com/the-open-session-in-view-anti-pattern/
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.