【发布时间】:2019-10-05 16:51:06
【问题描述】:
我在@Service 类中有一个方法,它在两个不同的@Service 类中调用两个不同的方法。这两种不同的方法在数据库中保存了两个实体(通过休眠),它们都可能抛出一些异常。 我希望如果抛出异常,独立于哪个 @Service 方法,所有更改都会回滚。所以在数据库中创建的所有实体都被删除了。
//entities
@Entity
public class ObjectB{
@Id
private long id;
...
}
@Entity
public class ObjectC{
@Id
private long id;
...
}
//servicies
@Service
@Transactional
public class ClassA{
@Autowired
private ClassB classB;
@Autowired
private ClassC classC;
public void methodA(){
classB.insertB(new ObjectB());
classC.insertC(new ObjectC());
}
}
@Service
@Transactional
public class ClassB{
@Autowired
private RepositoryB repositoryB;
public void insertB(ObjectB b){
repositoryB.save(b);
}
}
@Service
@Transactional
public class ClassC{
@Autowired
private RepositoryC repositoryC;
public void insertC(ObjectC c){
repositoryC.save(c);
}
}
//repositories
@Repository
public interface RepositoryB extends CrudRepository<ObjectB, String>{
}
@Repository
public interface RepositoryC extends CrudRepository<ObjectC, String>{
}
我想要ClassA的methodA,一旦methodB或methodC抛出异常,它会回滚数据库内的所有更改。 但它不会那样做。异常后所有更改仍然存在... 我错过了什么? 我应该添加什么才能使其按我的意愿工作? 我正在使用 Spring Boot 2.0.6! 我没有特别配置任何东西来使交易生效!
编辑 1
如果有帮助的话,这是我的主要课程:
@SpringBootApplication
public class JobWebappApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(JobWebappApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(JobWebappApplication.class, args);
}
}
当抛出异常时,这就是我在控制台中看到的:
Completing transaction for [com.example.ClassB.insertB]
Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@31d4fbf4] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@df9d400] bound to thread [http-nio-8080-exec-7]
Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@1d1ad46b] for key [HikariDataSource (HikariPool-1)] bound to thread [http-nio-8080-exec-7]
Getting transaction for [com.example.ClassC.insertC]
Completing transaction for [com.example.ClassC.insertC] after exception: java.lang.RuntimeException: runtime exception!
Applying rules to determine whether transaction should rollback on java.lang.RuntimeException: runtime exception!
Winning rollback rule is: null
No relevant rollback rule found: applying default rules
Completing transaction for [com.example.ClassA.methodA] after exception: java.lang.RuntimeException: runtime exception!
Applying rules to determine whether transaction should rollback on java.lang.RuntimeException: runtime exception!
Winning rollback rule is: null
No relevant rollback rule found: applying default rules
Clearing transaction synchronization
Removed value [org.springframework.jdbc.datasource.ConnectionHolder@1d1ad46b] for key [HikariDataSource (HikariPool-1)] from thread [http-nio-8080-exec-7]
Removed value [org.springframework.orm.jpa.EntityManagerHolder@31d4fbf4] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@df9d400] from thread [http-nio-8080-exec-7]
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: runtime exception!] with root cause
似乎每次它调用一个方法都会创建一个新事务!在 RuntimeException 发生后没有回滚任何东西!
编辑 2
这是 pom.xml 依赖文件:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.10.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.5</version>
</dependency>
</dependencies>
这是 application.properties 文件:
spring.datasource.url=jdbc:mysql://localhost:3306/exampleDB?useSSL=false
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.show-sql=true
logging.level.org.springframework.transaction=TRACE
spring.jpa.database=MYSQL
spring.jpa.hibernate.ddl-auto=update
spring.datasource.driver.class=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.locationId.new_generator_mappings=false
解决方案
感谢@M.Deinum,我找到了解决方案!
我使用了错误的数据库引擎 (MyISAM),它不支持事务!所以我用支持事务的“InnoDB”改变了表引擎类型。我做的是这样的:
- 我在 application.properties 文件中添加了这个属性,以便告诉 JPA 它应该使用哪个引擎类型来“操作”表格:
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
- 我删除了数据库中所有现有的表(使用错误的引擎类型),并让 JPA 使用正确的引擎 (InnoDB) 重新创建所有表。
现在抛出的所有 RuntimeExceptions 使事务回滚其中完成的所有更改。
警告:我注意到,如果抛出不是 RuntimeException 子类的异常,则不会应用回滚,并且所有已完成的更改都保留在数据库中。
【问题讨论】:
-
确保
@Transactional是org.springframework.transaction.annotation.Transactional但不是javax.transaction.Transactional? -
@KenChan 谢谢人!你说的对!我把所有的导入都弄错了……现在我要全部更改并再次测试!
-
应该像那样工作。
-
@Stefano。谢谢。让我知道它是否有效。如果可行,我如何将我的评论更改为答案,然后您接受它并在可以的情况下给它投票? ????????????
-
删除
spring-test依赖项(即已经包含在spring-boot-starter-test依赖项中)。您使用的是 MYSQL,请确保您将 MySQL 与 InnoDB 表一起使用,而不是与 MyISAM 表一起使用。后者不支持事务。
标签: java spring hibernate spring-boot