Spring transactions not working + JAX WS + JDBC
Asked Answered
N

3

6

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");

    }
    ...
}
Nordau answered 2/8, 2018 at 13:20 Comment(0)
N
1

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!

Nordau answered 9/8, 2018 at 15:10 Comment(1)
you said: The transactional method should be in other class, not in the Webservice annotated class. But with the public strategy: by making the Controller layer the transaction owner and working with the assumption that a public Controller method shouldn’t invoke another in any scenario. We have an ambivalence with your words, So, My Question. how could we manage transaction across the application that has WS layer? Hint:- the Controller layer and WebService Layer are always be the same layers.Transposal
S
2

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.

Slater answered 6/8, 2018 at 15:7 Comment(0)
O
1

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

Orthorhombic answered 6/8, 2018 at 10:13 Comment(1)
I already tried that, in fact, that is the correct version with public method, but when i posted this it was changed due to the thousands tries i have made. Thanks anywayNordau
N
1

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!

Nordau answered 9/8, 2018 at 15:10 Comment(1)
you said: The transactional method should be in other class, not in the Webservice annotated class. But with the public strategy: by making the Controller layer the transaction owner and working with the assumption that a public Controller method shouldn’t invoke another in any scenario. We have an ambivalence with your words, So, My Question. how could we manage transaction across the application that has WS layer? Hint:- the Controller layer and WebService Layer are always be the same layers.Transposal

© 2022 - 2024 — McMap. All rights reserved.