启动数据库、后台、前后并准备一些基础的测试数据后,来查看一个前面单元测试中遗漏的BUG。 ![](https://img.kancloud.cn/a3/3d/a33d768326f4ff5976f2ca21c04248ce_1199x270.gif) 在编辑学生的过程时,可以任意的变更学位的位数,任意的修改学生的姓名。而完全忽略掉了本章第五节新增学生时对学号、姓名的长度做的校验。相信任何程序员都接受不了当前的现实。 只所以会这样还要看看历史上对学号、姓名进行校验的代码: entity/Student.java ```java /** * 在实体保存到数据库以前,执行1次 * 1. 校验name 字段长度为2-20 * 2. 校验sno 字段长为为6 */ @PrePersist // ★ public void perPersis() { if (this.name != null ) { if (this.name.length() < 2) { throw new DataIntegrityViolationException("name length less than 2"); } if (this.name.length() > 20) { throw new DataIntegrityViolationException("name length more than 20"); } } if (this.sno != null) { if (this.sno.length() != 6) { throw new DataIntegrityViolationException("sno length must be 6"); } } } ``` * ★ 重点在这。 @PrePersist注解的作用时,在持久化(保存)之前。而非在更新之前。 虽然在代码层面,保存与更新都是调用了`save`方法。但hibernate还是能自动的区分出当前执行的是保存操作还是更新操作的。 # @PreUpdate `@PrePersist`中`@`代表注解,`Pre`意为在什么之前,`Persist`译为持久化也就是保存。起到的作用是:在数据保存之前执行此注解下的内容。Hibernate同时还提供了另一个用于更新之前执行某个方法的注解:`@PreUpdate`。测试如下: entity/Student.java ```java @PreUpdate public void perUpdate() { System.out.println("正在执行更新操作: " + this.id.toString()); } ``` 然后临时将`src/main/resources/application.propertions`中的`spring.jpa.hibernate.ddl-auto=create-drop`改为`spring.jpa.hibernate.ddl-auto=update`。然后重新启动后台应用并于前面重新进行测试数据的初始化工作。 > [info] create-drop:启动应用时创建数据表,停止应用时删除数据表。update:启动应用时更新数据表(如有更新内容),停止应用时什么也不做。 测试如下: ![](https://img.kancloud.cn/8b/8a/8b8a1a5f45778d92c333634b41b62bab_1278x395.gif) 接下来,可以将prePersist()方法中的代码复制到 preUpdate()方法中来达到与新增学生相同的校验效果。也可以这样: entity/Student.java ```java @PreUpdate public void perUpdate() { this.perPersis(); } ``` 也可以达到相同的验证效果。 ![](https://img.kancloud.cn/47/98/47988467be6a5c948520a6be22a831cf_1278x466.gif) # 单元测试 虽然前台后依赖式开发(前台的开发依赖于后台,后台的开发也依赖于前台)貌似很简单,但每次修改完代码都要用鼠标键盘执行相同的操作,这对做为不甘寂寞的程序员而言却显得难以接受。相较于这种依赖式的,每次变更代码后都要复要的操作鼠标键盘式的测试,笔者更愿意选择用代码来测试代码的方式。 有了新增学生的测试经验,编辑的学生的测试也就不难了。测试的思想为:先新增一个学生,然后分别使用不符合要求的姓名、学号来更新此学生,断言在更新过程中发生异常。最后再用符合要求的姓名、学号来更新此学生,断言更新成功。 请按上述思路参考新增学生的校验尝试自行完成单元测试。**提示:**更新操作校验失败时将发生`TransactionSystemException`异常。 <hr> 参考代码如下: entity/StudentTest.java ```java @Test(expected = TransactionSystemException.class) public void updateNameLengthToLongTest() { // 第一次调用save执行保存操作 this.studentRepository.save(student); this.student.setName("123456789012345678901"); // 第二次调用save执行更新操作 this.studentRepository.save(student); } @Test(expected = TransactionSystemException.class) public void updateNameLengthToShortTest() { this.studentRepository.save(student); this.student.setName("1"); this.studentRepository.save(student); } /** * 先后执行100次随机学号 * 当学号为6位时,更新成功 * 当学号不是6位时,更新时发生异常 */ @Test public void updateSnoLengthTest() { this.studentRepository.save(student); for (int i = 1; i <= 100; i++) { this.student.setSno(RandomString.make(i)); boolean called = false; try { this.studentRepository.save(student); } catch (TransactionSystemException e) { called = true; } if (i != 6) { Assertions.assertThat(called).isTrue(); } else { Assertions.assertThat(called).isFalse(); } } } ``` 除此以外Spring还提供了功能相似的 `PostLoad`、`PostPersist`、`PostRemove`、`PreRemove`注解,官方文档说明如下: | Type | Description | | --- | --- | | @PrePersist | Executed before the entity manager persist operation is actually executed or cascaded. This call is synchronous with the persist operation. | | @PreRemove | Executed before the entity manager remove operation is actually executed or cascaded. This call is synchronous with the remove operation. | | @PostPersist | Executed after the entity manager persist operation is actually executed or cascaded. This call is invoked after the database INSERT is executed. | | @PostRemove | Executed after the entity manager remove operation is actually executed or cascaded. This call is synchronous with the remove operation. | | @PreUpdate | Executed before the database UPDATE operation. | | @PostUpdate | Executed after the database UPDATE operation. | | @PostLoad | Executed after an entity has been loaded into the current persistence context or an entity has been refreshed. | 请自行翻译尝试。 # 参考文档 | 名称 | 链接 | 预计学习时长(分) | | --- | --- | --- | | 源码地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step4.7.9](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step4.7.9) | - | | Hibernate: Entity listeners and Callback methods | [https://docs.jboss.org/hibernate/core/4.0/hem/en-US/html/listeners.html#d0e2985](https://docs.jboss.org/hibernate/core/4.0/hem/en-US/html/listeners.html#d0e2985) | 10 |