Transactional annotation does not save on exit - spring

I have configured spring jpa with annotation driven. I would expect the following code to persist the changes to the database upon method exist.
#Transactional
public Foo changeValue(int id){
final Foo foo = fooRepository.findOne(id);
if(foo != null){
foo.setValue("new value");
//fooRepository.save(foo);
}
}
FooRepository is a JPARepository and the foo object is getting fetched so it is managed. Based on what I read about #Transactional I would expect that even without the fooRepository.save(foo) call the changes in foo's value column in the database would be persisted upon method exist. However, this only happens if I uncomment the call to fooRepository.save(foo)
No exceptions are thrown and the configuration for the jpa datasource is as below.
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" />
<tx:annotation-driven transaction-manager="transactionManager" />
<context:component-scan
base-package="com.example.package.data" />
<jpa:repositories
base-package="com.example.package.data.repository"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager" />
Any ideas?
Update
I do have a ContextLoaderListener and a DispatcherServlet but I do not have component scan for both.
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
</servlet>
....
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Update
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
And there I do
<context:component-scan base-package="com.example.package">
<context:exclude-filter type="regex"
expression="com.example.package.data.*" />
<context:exclude-filter type="regex"
expression="com.example.package.web.controller.*" />
<context:exclude-filter type="regex"
expression="com.example.package.web.service.*" />
</context:component-scan>
And in servlet-context
<context:component-scan base-package="com.example.package.web" />

Your general approach is correct. The changes should get saved and committed on exit of the method. While this approach works great when it works it is a bitch to debug.
Your configuration looks ok to me, so I would double check, that you actually have Spring Beans at your disposal and not some instance that you created simply by calling the constructor.
To verify this, just put a breakpoint at the code point where the annotated method gets called. The bean with the annotated method should NOT by of the class you wrote, but some ProxySomething class.
This article also provides some pointers what might be wrong with your setup.

If you have both a ContextLoaderListener and DispatcherServlet they both load a configuration file.
Judging from your configuration the services are loaded by the DispatcherServlet and your <tx:annotation-driven /> is loaded by the ContextLoaderListener. They both create an ApplicationContext and as AOP is only applied to the beans in the same context your services will not be transactional (Transaction management is done by using AOP).
As a rule of thumb your ContextLoaderListener should contain all shared or global resources (like services, daos, datasources etc.) whereas your DispatcherServlet should only contain web related beans like controllers, view resolvers etc.

Related

Transaction not working correctly - Spring/MyBatis

I have a Spring MVC Controller method which is tagged as "Transactional" which makes several service calls which are also tagged "Transactional" but they are treated as independent transactions and are committed separately instead of all under one transaction as I desire.
Based on the debug output, it appears Spring is only creating transactions when it reaches the service calls. I will see the output "Creating new transaction with name..." only for them but not one for the controller method who calls them.
Is there something obvious I am missing?
Sample code (abbreviated) below, controller:
#Controller
public class BlahBlah etc...
#Autowired
SomeService someService;
#Transactional
#RequestMapping(value="windows/submit", method=RequestMethod.POST)
#ResponseBody
public String submit (#RequestBody SomeObject blah) throws Exception {
someService.doInsert(blah);
if (true) throw Exception("blah");
... other stuff
}
service code:
public interface SomeService {
#Transactional
public void doInsert (SomeObject blah);
}
I tried removing the Transactional tag from the service call thinking maybe I messed it up and I am telling it to create one for each call but then in the debug output no transaction is created.
So the result is once I get my forced exception and check the table, the insert has committed instead of rolled back like I want.
So what did I do wrong?
Why is Spring ignoring the Transactional tag on the controller?
Posting relevant part of my context as per request from commenter:
Web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring.xml
classpath:spring-security.xml
classpath:spring-datasource.xml
</param-value>
</context-param>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
sping-mvc.xml:
<context:annotation-config/>
<context:component-scan base-package="com.blah"/>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver" p:order="1" />
... non-relevent stuff
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean id="webContentInterceptor" class="org.springframework.web.servlet.mvc.WebContentInterceptor">
<property name="cacheSeconds" value="0"/>
<property name="useExpiresHeader" value="true"/>
<property name="useCacheControlHeader" value="true"/>
<property name="useCacheControlNoStore" value="true"/>
</bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean id="httpInterceptor" class="com.blah.BlahInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
<mvc:view-controller path="/" view-name="blah"/>
<context:component-scan base-package="com.blah"/>
Hmm, thats interested and unintended - I have component scan duplicated. Could that be causing problems with this?
I think Spring ignores the #Transactional annotation here because it creates a proxy for the transaction, but the dispatcher isn't calling the controller through the proxy.
There's an interesting note in the Spring MVC documentation, 17.3.2, about annotating controllers, it doesn't describe your exact problem but shows that there are problems with this approach:
A common pitfall when working with annotated controller classes
happens when applying functionality that requires creating a proxy for
the controller object (e.g. #Transactional methods). Usually you will
introduce an interface for the controller in order to use JDK dynamic
proxies. To make this work you must move the #RequestMapping
annotations, as well as any other type and method-level annotations
(e.g. #ModelAttribute, #InitBinder) to the interface as well as the
mapping mechanism can only "see" the interface exposed by the proxy.
Alternatively, you could activate proxy-target-class="true" in the
configuration for the functionality applied to the controller (in our
transaction scenario in ). Doing so indicates
that CGLIB-based subclass proxies should be used instead of
interface-based JDK proxies. For more information on various proxying
mechanisms see Section 9.6, “Proxying mechanisms”.
I think you'd be better off creating another service to wrap the existing ones and annotating that.

For web MVC Spring app should #Transactional go on controller or service?

For WebApplicationContext, should I put #Transactional annotations in the controller or in services? The Spring docs have me a bit confused.
Here is my web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>Alpha v0.02</display-name>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.json</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
Here is my application-context.xml defining a spring dispatcher servlet:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:annotation-config />
<mvc:annotation-driven />
<tx:annotation-driven />
<context:component-scan base-package="com.visitrend" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="org.postgresql.Driver" />
<property name="jdbcUrl" value="jdbc:postgresql://localhost:5432/postgres" />
<property name="user" value="someuser" />
<property name="password" value="somepasswd" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:test.hibernate.cfg.xml" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="dataSource" ref="dataSource" />
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
Here's a service interface:
public interface LayerService {
public void createLayer(Integer layerListID, Layer layer);
}
Here's a service implementation:
#Service
public class LayerServiceImpl implements LayerService {
#Autowired
public LayerDAO layerDAO;
#Transactional
#Override
public void createLayer(Integer layerListID, Layer layer) {
layerDAO.createLayer(layerListID, layer);
}
}
And here's my controller:
#Controller
public class MainController {
#Autowired
private LayerService layerService;
#RequestMapping(value = "/addLayer.json", method = RequestMethod.POST)
public #ResponseBody
LayerListSetGroup addLayer(#RequestBody JSONLayerFactory request) {
layerService.createLayer(request.getListId(), request.buildLayer());
return layerService.readLayerListSetGroup(llsgID);
}
}
The Spring documentation has me a bit confused. It seems to indicate that using a WebApplicationContext means only controllers will be investigated for #Transactional annotations and not services. Meanwhile I see tons of recommendations to make services transactional and not controllers. I'm thinking that using <context:component-scan base-package="com..." /> in our spring-servlet.xml above so that it includes the services packages means the services are part of the context, and therefore will be "investigated" for transactional annotations. Is this accurate?
Here's the Spring documentation blurb that got me confused:
#EnableTransactionManagement and only looks
for #Transactional on beans in the same application context they are
defined in. This means that, if you put annotation driven
configuration in a WebApplicationContext for a DispatcherServlet, it
only checks for #Transactional beans in your controllers, and not your
services.
Further, is there any performance implications or "badness" if I define a controller method as transactional, and it calls a transactional method in a another class? My hunch is no, based on the documentation, but would love validation on that.
There is no requirement for whether the #Transactional annotation should go on a Controller or on a Service, but typically it would go on a Service that would perform the logic for one request that logically should be performed within one ACID transaction.
In a typical Spring MVC application, you would have, minimally, two contexts: the application context and the servlet context. A context is a sort of configuration. The application context holds the configuration that is relevant for your entire application, whereas the servlet context holds configuration relevant only to your servlets. As such, the servlet context is a child of the application context and can reference any entity in the application context. The reverse is not true.
In your quote,
#EnableTransactionManagement and only looks for #Transactional on beans in the same application context they are defined in. This means that, if you put annotation driven configuration in a WebApplicationContext for a DispatcherServlet, it only checks for #Transactional beans in your controllers, and not your services.
#EnableTransactionManagement looks for #Transactional in beans in packages declared in the #ComponentScan annotation but only in the context (#Configuration) they are defined in. So If you have a WebApplicationContext for your DispatcherServlet (this is a servlet context), then #EnableTransactionManagement will look for #Transactional in classes you told it to component scan in that context (#Configuration class).
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages = "my.servlet.package")
public class ServletContextConfiguration {
// this will only find #Transactional annotations on classes in my.servlet.package package
}
Since your #Service classes are part of the Application context, if you want to make those transactional, then you need to annotate your #Configuration class for the Application Context with #EnableTransactionManagement.
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages = "my.package.services")
public class ApplicationContextConfiguration {
// now this will scan your my.package.services package for #Transactional
}
Use your Application Context configuration with a ContextLoaderListener and your Servlet Context configuration when instantiating your DispatcherServlet. (See the javadoc for a full java based config, instead of xml, if you aren't doing it already.)
Addendum: #EnableTransactionManagement has the same behavior as <tx:annotation-driven /> in a java configuration. Check here for using ContextLoaderListener with XML.
The Service is the best place for putting transactional demarcations. The service should hold the detail-level use case behavior for a user interaction, meaning stuff that would logically go together in a transaction. Also that way a separation is maintained between web application glue code and business logic.
There are a lot of CRUD applications that don't have any significant business logic, for them having a service layer that just passes stuff through between the controllers and data access objects is not useful. In those cases you could get away with putting the transaction annotation on the data access objects.
Putting the transactional annotation on the controller can cause problems, see [the Spring MVC documentation][1], 17.3.2:
A common pitfall when working with annotated controller classes
happens when applying functionality that requires creating a proxy for
the controller object (e.g. #Transactional methods). Usually you will
introduce an interface for the controller in order to use JDK dynamic
proxies. To make this work you must move the #RequestMapping
annotations, as well as any other type and method-level annotations
(e.g. #ModelAttribute, #InitBinder) to the interface as well as the
mapping mechanism can only "see" the interface exposed by the proxy.
Alternatively, you could activate proxy-target-class="true" in the
configuration for the functionality applied to the controller (in our
transaction scenario in ). Doing so indicates
that CGLIB-based subclass proxies should be used instead of
interface-based JDK proxies. For more information on various proxying
mechanisms see Section 9.6, “Proxying mechanisms”.
The transaction propagation behaviors that you set up on the attributes decide what happens when a transactional method calls another transactional method. You can configure it so that the method called uses the same transaction, or so that it always uses a new transaction.
By having multiple calls to your service in the example code you're defeating the transactional purpose of the service. The different calls to your service will execute in different transactions if you put the transactional annotations on the service.
Sometimes it is very convenient to have #Transactional controller methods, especially when performing trivial operations using Hibernate. To enable this using XML configuration, add this to your dispatch-servlet.xml:
<beans ...
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="...
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
<tx:annotation-driven transaction-manager="transactionManager"
proxy-target-class="true" />
..
</beans>
The purpose of proxy-target-class is to use the CGLIB proxies which are required for AOP on controllers to work. If you don't add this, you'll get an error when starting up. Also, if you have any final methods in your controllers, note that they cannot be proxied (in particular, made transactional), and you will also get a warning from CGLIB for each of these methods, when starting up.

#RequestMapping not working with no exception

I have a class
#Controller
#RequestMapping("/auth")
public class LoginController {
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String getLoginPage(#RequestParam(value="error", required=false) boolean error,
ModelMap model) {
}
}
web.xml
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:META-INF/spring-servlet.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
appcontext.xml
<mvc:annotation-driven/>
<context:component-scan base-package="mysrc.src"/>
spring servlet.xml
<context:component-scan base-package="mysrc.src"/>
<mvc:annotation-driven/>
I WAS getting an error like "no mapping found in dispatcher blah blah"
But NOW i can see no exception on console.
I only see 404
http://localhost:9090/app/auth/login
I put breakpoint in getLoginPage method in debug mode but no flow.
Beside suggestions i want to ask how can i be sure abour Spring has scanned an realised my #RequestMapping class? Searching on starting up console maybe?
Usually when you get a 404 with no error message from Spring, it usually means your app is deployed under a different context than you are expecting. For example, your code sample indicates you are deploying your webapp to /, are you sure thats where its being deployed? Are you using tomcat? If so, your webapp should be named ROOT.war. Can you provide more details like where/how you are deploying your webapp and maybe the web.xml as well?
As far as your mappings go, Spring will log which controllers it scanned along with their request path at the INFO logging level. The dispatcher servlet will give always you an error message if it cannot match your request to a controller, so no error message indicates to me your requests are not going through the dispatcher servlet.
Your request mapping is looking right.
You can create an constructor to your controller and put a log statement or a break point there to see if the controller is created by spring.
Note that your component-scan would also initialize the controller twice (once for appcontext.xml and once for servlet.xml). You should add filters to make sure controllers are only created from the servlet.xml:
appcontext.xml (everything except controllers):
<context:component-scan base-package="x.y.z">
<context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation" />
</context:component-scan>
servlet.xml (only controllers):
<context:component-scan base-package="x.y.z" use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
And just to be sure: The base-package attribute of component-scan has to be a valid java package and not a path to your src directory (mysrc.src looks a bit like your src folder ;-) )

Beans injected into Apache Wink with Spring aren't registered

Following on from How do I inject a Spring bean into Apache Wink?
I'm now using wink-spring-support and I thought I had things set up correctly.
web.xml includes:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:META-INF/wink/wink-core-context.xml
classpath:applicationContext.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>restServlet</servlet-name>
<servlet-class>org.apache.wink.server.internal.servlet.RestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>restServlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
META-INF/wink/wink-core-context.xml contains:
<bean class="org.apache.wink.spring.Registrar">
<property name="instances">
<set>
<ref bean="myservice" />
</set>
</property>
</bean>
<bean id="myservice" class="mystuff.ServiceImpl"/>
There's a #Autowired annotation in mystuff.ServiceImpl that injects other Spring stuff, and mystuff.ServiceImpl implements a JAX-RS annotated interface and itself includes a JAX-RS #Path("/services") annotation.
I can see Spring loading up this stuff just fine, including the myservice bean. However when I request my resources, I get a 404 not found. As Wink starts, I can see a couple of log entries that might indicate the problem:
applicationConfigLocation property was not defined
Using application classes null named in init-param applicationConfigLocation
Have I missed something somewhere? Any advice?
The problem was my misunderstanding the docs.
There is a Spring configuration META-INF/server/wink-core-context.xml provided with wink-spring-support. This registers the BeanPostProcessors that actually do the setup and must be referenced from contextConfigLocation.
I thought that I put my configuration in there, which explains why the application didn't get registered with Wink on startup.

#Service are constructed twice

I have a problem with my Spring application where my #Service classes are being created twice when the application starts. I know this is a problem with my configuration, as I've experienced it before, but what exactly am I doing wrong?
Is there anything fundamentally wrong with the way I've laid out my config, below? (I have omitted everything I deem to be irrelevant)
web.xml:
<servlet>
<servlet-name>myapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/myapp-config.xml
/WEB-INF/myapp-security.xml
/WEB-INF/myapp-mvc.xml
</param-value>
</context-param>
<listener>
<listener-class>com.myapp.servlet.MyAppContextListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
myapp-servlet.xml
<context:component-scan base-package="com.myapp" annotation-config="true" />
<mvc:annotation-driven />
myapp-config.xml
<context:component-scan base-package="com.myapp" annotation-config="true" />
<context:annotation-config />
In addition to #GaryF's answer, there is a following beautiful solution for this problem (used in projects generated by Spring Roo):
myapp-config.xml
<!-- Load everything except #Controllers -->
<context:component-scan base-package="com.myapp">
<context:exclude-filter expression="org.springframework.stereotype.Controller"
type="annotation"/>
</context:component-scan>
myapp-servlet.xml
<!-- Load #Controllers only -->
<context:component-scan base-package="com.myapp" use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller"
type="annotation"/>
</context:component-scan>
EDIT:
Removing <context:component-scan> from myapp-config.xml means, that all your autodiscovered annotated beans are registered in DispatcherServlet's context (that is, the context loaded from myapp-servlet.xml).
However the recommended approach is to use servlet's context for presentation-specific things (such as controllers), and use the root context (myapp-config.xml) for the core services of your application. The solution above make this approach easy.
Regarding the practical considerations, when your core services are placed in servlet's application context, they can't be accessed from outside the scope of that servlet, for example, from another servlets (you may need to use another servlets to implement another access technologies) or filters (such as Spring Security filters). That's the reason for having core services in the root application context.
As an addition to the answer #axtavt gave, I’d like to give the matching Spring JavaConfig here.
In RootConfig.java:
#ComponentScan(basePackages = { "com.myapp" },
excludeFilters = #Filter({Controller.class, Configuration.class}))
In WebMvcConfig.java:
#ComponentScan(basePackages = { "com.myapp" },
useDefaultFilters = false, includeFilters = #Filter(Controller.class))
You're doing two separate component-scans over the same base-package. Remove one of them.

Resources