Почему не откатывается транзакция?
Пробую пример из книги, где объясняется распределенная транзакция. Но у меня не получается повторить откат транзакции при выбросе исключения. Вот мое приложение:
POM.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>jta</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.0.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.2.10.Final</version>
</dependency>
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-hibernate4</artifactId>
<version>4.0.4</version>
<exclusions>
<exclusion>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.0.11.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
</dependencies>
</project>
XAJpaConfig.java
import com.atomikos.jdbc.AtomikosDataSourceBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;
import static org.hibernate.cfg.AvailableSettings.*;
@Configuration
@EnableJpaRepositories
public class XAJpaConfig {
@SuppressWarnings("unchecked")
@Bean(initMethod = "init", destroyMethod = "close")
public DataSource dataSourceA() {
try {
AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean();
dataSource.setUniqueResourceName("XADBMSA");
dataSource.setXaDataSourceClassName("com.mysql.cj.jdbc.MysqlXADataSource");
dataSource.setXaProperties(xaAProperties());
dataSource.setPoolSize(1);
return dataSource;
} catch (Exception e) {
return null;
}
}
@Bean
public Properties xaAProperties() {
Properties xaProp = new Properties();
xaProp.put("databaseName", "MUSICDB_A");
xaProp.put("useSSL", false);
xaProp.put("user", "prospring5_A");
xaProp.put("password", "prospring5_A");
return xaProp;
}
@Bean
public Properties hibernateProperties() {
Properties hibernateProp = new Properties();
hibernateProp.put("hibernate.transaction.factory_class", "org.hibernate.transaction.JTATransactionFactory");
hibernateProp.put(JTA_PLATFORM, "com.atomikos.icatch.jta.hibernate4.AtomikosPlatform");
// required by Hibernate 5
hibernateProp.put(TRANSACTION_COORDINATOR_STRATEGY, "jta");
hibernateProp.put(CURRENT_SESSION_CONTEXT_CLASS, "jta");
hibernateProp.put(AUTOCOMMIT, false);
hibernateProp.put(FLUSH_BEFORE_COMPLETION, false);
hibernateProp.put(DIALECT, "org.hibernate.dialect.MySQL5Dialect");
// this will work only if users/schemas are created first, use ddl.sql script for this
hibernateProp.put(HBM2DDL_AUTO, "create");
hibernateProp.put(SHOW_SQL, true);
hibernateProp.put(MAX_FETCH_DEPTH, 3);
hibernateProp.put(STATEMENT_BATCH_SIZE, 10);
hibernateProp.put(STATEMENT_FETCH_SIZE, 50);
return hibernateProp;
}
@Bean
public EntityManagerFactory emfA() {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSourceA());
factoryBean.setPackagesToScan("org.example");
factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
factoryBean.setJpaProperties(hibernateProperties());
factoryBean.setPersistenceUnitName("emfA");
factoryBean.afterPropertiesSet();
return factoryBean.getObject();
}
}
ServicesConfig.java
package org.example;
import com.atomikos.icatch.config.UserTransactionService;
import com.atomikos.icatch.config.UserTransactionServiceImp;
import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.jta.JtaTransactionManager;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import java.util.Properties;
@Configuration
@EnableTransactionManagement
@ComponentScan
public class ServicesConfig {
private Logger logger = LoggerFactory.getLogger(ServicesConfig.class);
@Bean(initMethod = "init", destroyMethod = "shutdownForce")
public UserTransactionService userTransactionService(){
Properties atProps = new Properties();
atProps.put("com.atomikos.icatch.service", "com.atomikos.icatch.standalone.UserTransactionServiceFactory");
return new UserTransactionServiceImp(atProps);
}
@Bean(initMethod = "init", destroyMethod = "close")
@DependsOn("userTransactionService")
public UserTransactionManager atomikosTransactionManager(){
UserTransactionManager utm = new UserTransactionManager();
utm.setStartupTransactionService(false);
utm.setForceShutdown(true);
return utm;
}
@Bean
@DependsOn("userTransactionService")
public UserTransaction userTransaction(){
UserTransactionImp ut = new UserTransactionImp();
try {
ut.setTransactionTimeout(300);
} catch (SystemException se) {
logger.error("Configuration exception.", se);
return null;
}
return ut;
}
@Bean
public PlatformTransactionManager transactionManager(){
JtaTransactionManager ptm = new JtaTransactionManager();
ptm.setTransactionManager(atomikosTransactionManager());
ptm.setUserTransaction(userTransaction());
return ptm;
}
}
SingerService.java
package org.example;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;
@Service("singerService")
@Repository
@Transactional
@SuppressWarnings("unchecked")
public class SingerService {
@PersistenceContext(unitName = "emfA")
private EntityManager emA;
@Transactional
public Singer save(Singer singer) {
if (singer.getId() == null) {
emA.persist(singer);
if (true) {
throw new JpaSystemException(
new PersistenceException("Simulation of something going wrong.")
);
}
}
return singer;
}
}
И сам запуск приложения в файле App.java
package org.example;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import java.util.Date;
import java.util.GregorianCalendar;
public class App {
public static void main(String[] args) {
GenericApplicationContext ctx = new AnnotationConfigApplicationContext(ServicesConfig.class,
XAJpaConfig.class);
SingerService singerService = ctx.getBean(SingerService.class);
Singer singer = new Singer();
singer.setFirstName("John");
singer.setLastName("Mayer");
singer.setBirthDate(new Date(
(new GregorianCalendar(1977, 9, 16)).getTime().getTime()));
singerService.save(singer);
}
}
Но в БД запись с певцом все равно сохраняется:

Подскажите, пожалуйста, в чем может быть ошибка?
Ответы (1 шт):
Автор решения: Галина
→ Ссылка
Методом проб и ошибок, оказалось, что диалект Hibernate должен быть org.hibernate.dialect.MySQL8Dialect, потому что у меня 8 версия MySQL.Также пришлось из-за этого поменять версию Hibernate на 5.3.7.Final.