问题
I'm a bit exasperated with this issue. Lets check if someone has implemented something similar.
I have a java 8 web application with 8 WS implemented. Some of this WS, make inserts and updates through JDBCTemplate (Hibernate is not a choice due to performance needs) and i need them to rollback if execution crashes with an exception.
I have the following configuration of datasource and transaction manager in spring app context file (jndi resource in server.xml/context.xml of Tomcat):
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/source" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
On the other hand I have a unique accesspoint to the dataBase DBcontroller.class, which has a generic method for inserts, deletes and updates:
private NamedParameterJdbcTemplate jdbcTemplate;
private DataSource datasource;
@Autowired
public void setDataSource(DataSource dataSource) {
this.datasource = dataSource;
this.jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
@Override
public boolean queryForModifying(String query, SqlParameterSource parameters) {
int modifiedRows= 0;
try {
modifiedRows= this.jdbcTemplate.update(query, parameters);
} catch (Exception e) {
e.printStackTrace();
numRegistrosAfectados = 0;
}
return (modifiedRows> 0) ? true : false;
}
Finally I have a WS Interface This way:
@WebService
public interface IService{
@WebMethod
public method(MethodRequestType request) throws IllegalArgumentException, IllegalAccessException;
}
with its implementation:
@WebService(endpointInterface = "com.package.IService")
@HandlerChain(file = "handler-chain.xml")
public class Service implements IService{
@Autowired
IDBController dbController;
with a "transactional" method:
@Transactional
private boolean inserts(HashMap<String, Object> input, MethodRequestType request) {.....
It should be working ok on a non WS project, but as I have discovered there is no so easy way for making this work.
First I thought it didn't rollback, but now I'm quite sure it doesn't create transactions.
There are some similar post in stackoverflow, but none of them fix my problem. I have google it a lot, and the only way suggested is WS-AtomicTransactions, which I have never heard about.
I have try almost everything in XML configuration file, I have even tried to manage transactions programatically, but as it is a pool of connections I'm not able to set autocommit to false so that I can force rollbacks.
For if it is useful for anyone, I have soap handlers implemented for each WS, that look this way:
public class ServiceHandler implements SOAPHandler<SOAPMessageContext> {
private SoapFaultHandler soapFaultHandler;
@Override
public boolean handleMessage(SOAPMessageContext context) {
SOAPMessage message = context.getMessage();
soapFaultHandler = new SoapFaultHandler(message);
return SoapMessageHandler.handleMessage(context, "Service name", logger);
}
@Override
public boolean handleFault(SOAPMessageContext context) {
return soapFaultHandler.handleFault(context, logger, "ServiceName");
}
...
}
回答1:
It matters where you call the method annotated with @Transactional
. For example if you have:
@Service
public class Service1 {
@Transactional
public void method1() { ... }
public void method2() {
method1();
}
}
@Service
public class Service2 {
@Autowired
private Service1 service1;
public void method1() {
service1.method1();
}
public void method2() {
service1.method2();
}
}
- Calling
service2.method2()
will NOT create a transaction. - Calling
service2.method1()
will create one.
Because of the way Spring handles Proxies and AOP.
回答2:
by reading the spring documentation you can read the following (in bold the important sentences)
It is not sufficient to tell you simply to annotate your classes with the @Transactional annotation, add @EnableTransactionManagement to your configuration, and then expect you to understand how it all works. This section explains the inner workings of the Spring Framework’s declarative transaction infrastructure in the event of transaction-related issues.
The most important concepts to grasp with regard to the Spring Framework’s declarative transaction support are that this support is enabled via AOP proxies, and that the transactional advice is driven by metadata (currently XML- or annotation-based). The combination of AOP with transactional metadata yields an AOP proxy that uses a TransactionInterceptor in conjunction with an appropriate PlatformTransactionManager implementation to drive transactions around method invocations.
When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods
So please try to change the modifier of your "inserts" method from private to public
回答3:
Finally I have found the way! I was really close allways:
The transactional method (as Gavi said) must be public ( but that was not the problem, cause I tried it before).
The transactional method should be in other class, not in the Webservice annotated class.
This class needs to be autowired so that it is in the Spring context.
In this class I have autowired a DbController instance.
And that was everything.
My main problem, was that I mocked this class and instantiated it with a "new", instead letting Spring makes its magic.
Thanks for responses, hope to help others!
来源:https://stackoverflow.com/questions/51654734/spring-transactions-not-working-jax-ws-jdbc