说明
数据库是有触发器Trigger。但我们希望业务逻辑能从底层的具体数据存储抽象出来(有需要时可以更好存储机制),也就是要在应用中编写trigger,而不是依赖底层数据库来实现。
Entity内部触发器
我们随便挑选以前的某个entity,在里面加入触发器。
@Entity
public class Person {
... ...
// 【触发器说明】
//1、多个触发器的标记可以加到同一个方法中。
//2、每种触发器最多只允许有一个。如果我们在superclass中定义了某个触发器,这这里也定义一个,
// 则只有这里定义的有效。
//3、方法可以是public,protected,package-private,private,名字无所谓。返回是void,无输入参数。
//【注意】为避免异常,不要在这些方法中调用EntityManager的方法,或者访问其他entity。
// 一旦出现异常,事务会回滚。
//@PostLoad:在执行select语句后触发。如果返回多条数据,那么在ResultSet中所创建的每个entity对象均会触发。
@PostLoad
void readTrigger(){
log.debug("Person entity {} read.",this.id);
}
//@PrePersist:顾名思义,这是在代码中调用了EntityManager相关的persist()语句后马上执行,在
// entity提交给EntityManager之前。注意这并不代表在数据库中马上执行,需要得到
// transaction.commit()或者flush()时执行。在大的复杂事务中,时间差可能会挺大的。
//【注意】如果我们在这里修改entity的属性,则最终在数据库保存的值为修订后的值
@PrePersist
void beforeInsertTrigger(){
log.debug("Person entity {} about to be inserted.",this.id);
}
//@PostPersist:当真实insert到数据库的时候会调用。和后面介绍的PostXXXX一样,事务在之后是可能回滚的。
@PostPersist
void afterInsertTrigger() {
log.debug("Person entity {} inserted into database.",this.id);
}
@PreUpdate //只要调用了setXXX(),就会触发。
void beforeUpdateTrigger() {
log.debug("Person entity {} just updated by call to mutator method.",this.id);
}
@PostUpdate
void afterUpdateTrigger(){
log.debug("Person entity {} just updated in the database.",this.id);
}
@PreRemove
void beforeDeleteTrigger(){
log.debug("Person entity {} about to be deleted.",this.id);
}
@PostRemove
void afterDeleteTrigger(){
log.debug("Person entity {} about deleted from database.",this.id);
}
}
Entity外部触发器:EntityListener
定义两个EntityListener,如下:
public class FirstEntityListener {
private static final Logger log = LogManager.getLogger();
//【1】EntityListener的构造函数必须是public,且无参数,本小例子无需设定
//【2】代码编写和entity内部的触发器相似,不同时的是带有Entity的参数,可以是某个具体的Entity
// 类,也可以是Object这种通用类。
@PostLoad void readTrigger(Person person)
{
log.debug("Person entity {} read.",person.getId());
}
@PrePersist void beforeInsertTrigger(Person person)
{
person.setFirstName(person.getFirstName()+ "@");
log.debug("Person entity {} about to be inserted.",person.getId());
}
}
public class SecondEntityListener {
private static final Logger log = LogManager.getLogger();
@PostLoad void readTrigger(Person person)
{
log.debug("Person entity {} read.",person.getId());
}
}
将FirstEntityListener和SecondEntityListener加载Entity Person上,在上一个小例子中,我们已经在Person中加入内部的触发器。
//【1】Entity或者MappedSuperclass可以加入EntityListener,用@EntityListeners进行标记
//【2】可以加入多个EntityListner,将按顺序触发
//【3】EntityListener将优先于内部触发器执行
//【4】如果我们不想触发superclass的EntityListener,可以加上@ExcludeSuperclassListeners,指出使
// 用哪个superclass的entityListener,如果不想触发任何superclass的entityListener,可以加上
// @ExcludeDefaultListeners
@EntityListeners({FirstEntityListener.class,SecondEntityListener.class})
@Entity
public class Person {
.....
}
执行结果如下:
15:20:28.682 [http-nio-8080-exec-5] [DEBUG] FirstEntityListener:13 readTrigger() - Person entity 1 read.
15:20:28.683 [http-nio-8080-exec-5] [DEBUG] SecondEntityListener:12 readTrigger() - Person entity 1 read.
15:20:28.684 [http-nio-8080-exec-5] [DEBUG] Person:83 readTrigger() - Person entity 1 read.
15:20:28.684 [http-nio-8080-exec-5] [DEBUG] FirstEntityListener:13 readTrigger() - Person entity 2 read.
15:20:28.685 [http-nio-8080-exec-5] [DEBUG] SecondEntityListener:12 readTrigger() - Person entity 2 read.
15:20:28.685 [http-nio-8080-exec-5] [DEBUG] Person:83 readTrigger() - Person entity 2 read.
附:关于entity数据变更的自动同步
@Transactional
public void secondTableTest(long id, String nickName){
MyEmployee employee = myEmployRepository.findOne(id);
if(employee == null)
return;
employee.setNickName(nickName);
// 即使我们不执行下面的save,由于已经修改了entity的内容,在transaction结束时(commit)会执行Update的SQL。
// myEmployRepository.save(employee);
}