1. 使用过程
2. 背景
3. 遇到问题
3.1 不指定 Hibernate 数据库方言, 默认 SQL 生成方式
3.2 抛出异常 Hibernate 加入了 @Transactional 事务不会回滚
3.3 Hibernate 使用 Spring Test 测试加入了 @Transactional 事务无论如何数据库插入不成功
3.4 Hibernate 在使用 MyISAM 引擎也可以回滚?
3.5 Hibernate 在使用生成策略是 IDENTITY 不能回滚事务, AUTO 可以
3.6 使用 MyISAM 引擎下 @Controller 下使用事务回滚不成功,@Service 下成功
4. 总结
1. 使用过程
在项目中我使用 Spring Boot 2.0.0.RELEASE 开发并且集成了 Spring JPA; 我这里将 Hibernate 版本设置为 5.1.2.Final, 这里做修改主要是因为 LocalDate 跟数据库映射时有些问题才做更换, 其他基本没什么大的问题; 数据库使用了 MySQL
2. 背景
使用 MySQL, 所以先对 MySQL 做一些了解, 这里 MySQL 数据库中有四种存储引擎, 主要介绍两种, 分别为 MyISAM 和 InnoDB, 它们两个的区别如下:
InnoDB | MyISAM | |
---|---|---|
访问速度 | 相对慢 | 快 |
事务 | 支持 | 不支持 |
外键 | 支持 | 不支持 |
应用场景 | 需要事务提交、回滚功能应用 | 以查询为主的应用 |
各种引擎简要介绍
https://blog.csdn.net/qq_27028821/article/details/52267991
3. 遇到问题
3.1 不指定 Hibernate 数据库方言, 默认 SQL 生成方式
在不指定方言的情况下默认使用了 MySQL5Dialect, 这样在打印 create table 命令时会在后面指定它的数据库引擎为 MyISAM, 这样生成的数据库是不支持外键的, 也是不支持事务性操作的. 请注意是数据库.
3.2 抛出异常 Hibernate 加入了 @Transactional 事务不会回滚
现在这里有个实体类:
- @Entity
- @Data
- public class Test{
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Integer id;
- private String name;
- }
我们保存需要保存一个 Test 实例时发现抛出了一个异常数据库居然保存成功, 代码如下:
- Service:
- @Resource
- private TestDao testDao;
- @Transactional
- public void test(){
- Test test = new Test();
- test.setName("test");
- testDao.save(test);
- int a = 1 / 0;
- }
这里我们要看回去 MySQL 生成的方式, 默认是使用了 MyISAM 这是不支持事务的, 如果向数据库保存了数据, 那么事务回滚也是不管用的, 所以, 现在我们需要指定配置数据库的方言, 在 Spring Boot 中可在
application.properties
配置如下:
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
如果是 xml 配置的可以这么配置
<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
这样我们重新运行生成的表可以有外键生成也可以支持数据库事务, 当然数据就可以回滚了
当然了, 除了以上的解决方法之外, 其实还有一个不怎么好的解决方法, 那就是将 Test 中的 id 生成策略注解改为如下内容:
- @Entity
- @Data
- public class Test{
- @Id
- @GeneratedValue(strategy = GenerationType.AUTO)
- private Integer id;
- private String name;
- }
对于这个为什么可以, 请接着往下面看
主键生成策略参考
https://blog.csdn.net/lwt976647637/article/details/53352369
3.3 Hibernate 使用 Spring Test 测试加入了 @Transactional 事务无论如何数据库插入不成功
这里使用上面修改成 AUTO 的实体类
在测试类里面我们可以保存我们一个 Test 对象, 测试代码如下:
- junit.Test
- @Test
- @Transactional
- public void test(){
- Test test = new Test();
- test.setName("test");
- testDao.save(test);
- }
上面没有任何异常抛出, 进度条也绿了, 但是数据插入数据库不成功, 这时, 可以看下控制台打印输出的语句:
//.... 前面有很多, 不写出来了
transaction manager [org.springframework.orm.jpa.JpaTransactionManager@5e5ddfbc]; rollback [true]
这里我大胆的猜测就是 Spring Test 自动帮我们把事务回滚了, 我们如果要看到把数据插入数据库的话只是需要在 test 方法加入注解, 如
- @Test
- @Transactional
- @Rollback(false) // 设置事务不自动回滚
- public void test(){
- Test test = new Test();
- test.setName("test");
- testDao.save(test);
- }
3.4 Hibernate 在使用 MyISAM 引擎也可以回滚?
我在之前写的会员管理系统中是没有指定生成方言的, 默认是使用了 MyISAM, 但是在 Service 中出现了异常还是可以回滚, 怎么回事?
这是因为我们在 save 之后 Hibernate 不会立马执行 SQL 的, 除非是事务提交了, 我在事务提交之前抛出了异常, 所以在 Hibernate 的缓存中的 SQL 是不会执行成功的, 因此就用假的回滚现象, 这不是数据库提供的回滚功能.
3.5 Hibernate 在使用生成策略是 IDENTITY 不能回滚事务, AUTO 可以
这是因为如果使用了 IDENTITY 是数据库维护我们的主键, Hibernate 为了获取 id 是需要向数据库插入数据才能获得 id 的值的, 所以会执行 SQL, 在使用 MyISAM 引擎的情况下是不能回滚事务的
如果用了 AUTO 那么不需要数据库维护, Hibernate 自己维护是不需要向数据库要主键的, 那么不会立马执行 SQL 就跟上一个问题一样的结果. 所以这里的生成策略使用 uuid 也是可以的, 只要不是数据库维护.
3.6 使用 MyISAM 引擎下 @Controller 下使用事务回滚不成功,@Service 下成功
在 Controller 层使用 @Transactional 事务是不会回滚的, 这个具体原因也是不清楚的, 但是 Service 层就可以, 如果把 @Controller 替换成 @Component 也是成功的.
4. 总结
这里遇到大部分问题终其原因都是 MySQL 引擎使用或是没有指定方言的原因吧, 所以, 没有性能方面的要求或者是小白请使用 InnoDB 引擎.
而对于我来说, 这些异常手动 catch 算是学到了些东西, 但是会遇到问题说明自己还是没有了解它, 不说了, 好好学习.
来源: https://www.cnblogs.com/lger/p/8895525.html