I am trying to create a transaction comprising two REST web services, whose data source point to the same data base. The first service, named 1
, invokes another web service named 2
using Spring RestTemplate.
To implement the transaction I am using a JNDI connection pool, the MySql JDBC driver (version 5.1.35), JTA, XA, Spring and the GlassFish 5 AppServer.
Now, I have downloaded the maven dependencies in the Spring project, defined a configuration class using JtaTransactionManager
, and configured the datasource and JTA properties in an application.yml
file, like in the following code:
Configuration class:
@Configuration
@EnableTransactionManagement
public class Transacciones {
@Bean
public PlatformTransactionManager platformTransactionManager(){
return new JtaTransactionManager();
}
}
application.yml file
spring:
datasource:
jndi-name: jdbc/Prueba
driver-class-name: com.mysql.jdbc.Driver
jta:
enabled: true
I configured the JNDI datasource in GlassFish 5 defining a "JDBC Resource" named jdbc/Prueba
in the "Connections pools" page using a javax.sql.XADataSource
datasource named pruebaXA
:
In the control layer of the web service 1
, the method calls the service 2
using the RestTemplate
class of Spring Framework:
Service 1 code:
@RestController
@RequestMapping("/servicio")
@EnableTransactionManagement
public class a {
@Autowired
private JdbcTemplate objJdbcTemplate;
@Transactional(rollbackFor = RuntimeException.class)
@GetMapping("/1")
public Integer getValor(){
try{
int numero;
int n=50;
RestTemplate restTemplate = new RestTemplate();
Integer intRes1;
Integer intRes2;
numero = (int) (Math.random() * n) + 1;
intRes2 = restTemplate.postForObject("http://localhost:8080/servicio2-1.0-SNAPSHOT/servicio/2",numero,Integer.class);
intRes1=objJdbcTemplate.update("INSERT INTO A VALUES(" +numero + ")");
return numero;
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
Service 2 code:
@RestController
@RequestMapping("/servicio")
public class a {
@Autowired
private JdbcTemplate objJdbcTemplate;
@Transactional(rollbackFor = RuntimeException.class)
@PostMapping("/2")
public Integer getValor(@RequestBody Integer intNum){
try{
Integer intRes;
intRes=objJdbcTemplate.update("INSERT INTO B VALUES(" + intNum + ")");
return intRes;
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
If the two services work without errors, there is not problem. However, when the service 1
falls, the service 2
does not know about the error and does not do the rollback.
I do not know if I need to configure another feature/option in the GlassFish 5, or in the Spring program.
I have read that in Spring only needs a JtaTransactionManager
bean and the framework performs all the work related to configure and use JTA-transactions.Spring and JTA
JTA
Now, if you still need to use JTA, at least the choice is yours to
make. There are two common scenarios: using JTA in a heavyweight
application server (which has all the nasty disadvantages of being
tied to a JavaEE server), or using a standalone JTA implementation.
Spring provides support for JTA-based global transaction
implementations through a PlatformTransactionManager implementation
called JtaTransactionManager. If you use it on a JavaEE application
server, it will automatically find the correct
javax.transaction.UserTransaction reference from JNDI. Additionally,
it will attempt to find the container-specific
javax.transaction.TransactionManager reference in 9 different
application servers for more advanced use cases like transaction
suspension. Behind the scenes, Spring loads different
JtaTransactionManager subclasses to take advantage of specific, extra
features in different servers when available, for example:
WebLogicJtaTransactionManager, WebSphereUowTransactionManager and
OC4JJtaTransactionManager.
So, if you’re inside a Java EE application server, and can’t escape,
but want to use Spring’s JTA support, then chances are good that you
can use the following namespace configuration support to factory a
JtaTransactionManager correctly (and automatically):
Alternatively, you can register a
JtaTransactionManager bean instance as appropriate, with no
constructor arguments, like this:
@Bean public PlatformTransactionManager platformTransactionManager(){
return new JtaTransactionManager(); } Either way, the end result in a JavaEE application server is that you can now use JTA to manage
your transactions in a unified manner thanks to Spring.
Thanks for your help and time.
See Question&Answers more detail:
os