Hibernate.org社区文档

第 6 章 实体侦听器和回调方法

6.1. 定义
6.2. 回调和侦听器继承
6.3. XML 定义

让应用程序响应持久化机制内发生的某些事件往往很有用。这允许实现某种通用功能和扩展内置功能。JPA 规范为此目的提供了两种相关的机制。

可以将实体的方法指定为回调方法,以便接收特定实体生命周期事件的通知。回调方法由回调注释进行注释。您还可以定义一个实体侦听器类,以替代直接在实体类内部定义的回调方法。实体侦听器是一个无状态类,带有一个无参数构造函数。通过使用 @EntityListeners 注释对实体类进行注释,来定义实体侦听器

@Entity

@EntityListeners(class=Audit.class)
public class Cat {
    @Id private Integer id;
    private String name;
    private Calendar dateOfBirth;
    @Transient private int age;
    private Date lastUpdate;
    //getters and setters
    /**
     * Set my transient property at load time based on a calculation,
     * note that a native Hibernate formula mapping is better for this purpose.
     */
    @PostLoad
    public void calculateAge() {
        Calendar birth = new GregorianCalendar();
        birth.setTime(dateOfBirth);
        Calendar now = new GregorianCalendar();
        now.setTime( new Date() );
        int adjust = 0;
        if ( now.get(Calendar.DAY_OF_YEAR) - birth.get(Calendar.DAY_OF_YEAR) < 0) {
            adjust = -1;
        }
        age = now.get(Calendar.YEAR) - birth.get(Calendar.YEAR) + adjust;
    }
}
public class LastUpdateListener {
    /**
     * automatic property set before any database persistence
     */
    @PreUpdate
    @PrePersist
    public void setLastUpdate(Cat o) {
        o.setLastUpdate( new Date() );
    }
}

相同的回调方法或实体侦听器方法可以用多个回调注释进行注释。对于给定的实体,您不能使用同一个回调注释对两个方法进行注释,无论该方法是回调方法还是实体侦听器方法。回调方法是没有返回类型且名称任意的一个无参数方法。实体侦听器的签名为 void <METHOD>(Object),其中 Object 属于实际实体类型(请注意,Hibernate 实体管理器放宽了此约束,并允许使用 Object 作为 java.lang.Object 的类型(允许多个实体共享侦听器)

回调方法可以引发 RuntimeException。如果有当前事务,则必须回滚。已定义了以下回调


回调方法不得调用 EntityManagerQuery 方法!

你可以在不同层级的实体上定义多个实体监听程序。也可以在不同层级上定义多个回调。但不能在同一实体或同一实体监听程序中为同一事件定义两个监听程序。

当触发某事件时,按以下顺序执行监听程序

可以使用 @ExcludeSuperclassListeners 停止实体监听程序继承,这样将忽略所有超类的 @EntityListeners

JPA 规范允许通过 JPA 部署描述符覆盖注释。另外,还有一个可能有用的功能: 默认事件监听程序。


<?xml version="1.0" encoding="UTF-8"?>

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_2_0.xsd"
                 version="2.0"
        >
    <persistence-unit-metadata>
        <persistence-unit-defaults>
            <entity-listeners>
                <entity-listener class="org.hibernate.ejb.test.pack.defaultpar.IncrementListener">
                    <pre-persist method-name="increment"/>
                </entity-listener>
            </entity-listeners>
        </persistence-unit-defaults>
    </persistence-unit-metadata>
    <package>org.hibernate.ejb.test.pack.defaultpar</package>
    <entity class="ApplicationServer">
        <entity-listeners>
            <entity-listener class="OtherIncrementListener">
                <pre-persist method-name="increment"/>
            </entity-listener>
        </entity-listeners>


        <pre-persist method-name="calculate"/>
    </entity>
</entity-mappings>

你可以在给定实体上覆盖实体监听程序。实体监听程序对应于给定类,并且一个或多个事件都会触发给定的方法调用。你也可以在实体本身上定义事件以描述回调。

最后但并非最不重要的是,你可以定义一些默认实体监听程序,这些实体监听程序将首先应用于给定持久化单元所有映射实体的实体监听程序堆栈上。若不想让实体继承默认监听程序,可以使用 @ExcludeDefaultListeners(或 <exclude-default-listeners/>)。