Hibernate envers开发指南
测试版本:
Hibernate 3.3.2
Hibernate envers 1.2.2.ga-hibernate-3.3
介绍:
Hibernate Envers目的是根据对实体的设置,提供记录执行数据变更历史的功能(数据变更版本)。它实现原理是通过对Hibernate的操作事件监听并根据
基于Annoatation的配置来记录修改数据的内容。
1. 配置方法
基于Spring的配置内容如下:
1.1 配置Envers事件监听器
1
<
bean
id
="sessionFactory"
2 class ="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" >
3 < property name ="dataSource" >
4 < ref local ="dataSource" />
5 </ property >
6 < property name ="eventListeners" >
7 < map >
8 < entry key ="post-insert" value-ref ="enversEventListener" />
9 < entry key ="post-update" value-ref ="enversEventListener" />
10 < entry key ="post-delete" value-ref ="enversEventListener" />
11 < entry key ="post-collection-recreate" value-ref ="enversEventListener" />
12 < entry key ="pre-collection-remove" value-ref ="enversEventListener" />
13 < entry key ="pre-collection-update" value-ref ="enversEventListener" />
14 </ map >
15 </ property >
16 < property name ="configLocation" value ="classpath:conf/hibernate.cfg.xml" />
17 </ bean >
18
19 < bean name ="enversEventListener" class ="org.hibernate.envers.event.AuditEventListener" />
2 class ="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" >
3 < property name ="dataSource" >
4 < ref local ="dataSource" />
5 </ property >
6 < property name ="eventListeners" >
7 < map >
8 < entry key ="post-insert" value-ref ="enversEventListener" />
9 < entry key ="post-update" value-ref ="enversEventListener" />
10 < entry key ="post-delete" value-ref ="enversEventListener" />
11 < entry key ="post-collection-recreate" value-ref ="enversEventListener" />
12 < entry key ="pre-collection-remove" value-ref ="enversEventListener" />
13 < entry key ="pre-collection-update" value-ref ="enversEventListener" />
14 </ map >
15 </ property >
16 < property name ="configLocation" value ="classpath:conf/hibernate.cfg.xml" />
17 </ bean >
18
19 < bean name ="enversEventListener" class ="org.hibernate.envers.event.AuditEventListener" />
所有的监听事件都由 AuditEventListener 来处理。
1.2 配置 Envers相关属性
org.hibernate.envers.audit_table_prefix 配置数据修改记录表名的前缀规则 默认值:空
org.hibernate.envers.audit_table_suffix 配置数据修改记录表名的后缀规则 默认值:_AUD
org.hibernate.envers.revision_field_name 配置数据修改记录表版本号字段名 默认值: REV
org.hibernate.envers.revision_type_field_name 配置数据修改记录表修改类型字段名 默认值: REVTYPE . 0表示新增加,1修改 2删除
org.hibernate.envers.revision_on_collection_change 配置是否支持关联表修改时记录修改记录 默认值:true
org.hibernate.envers.do_not_audit_optimistic_locking_field 配置是否不对乐观锁字段修改时记录修改记录,即使用(@Version)字段 默认值:true
org.hibernate.envers.store_data_at_delete 配置是否在删除操作时,只保存id值还是全部的值。 默认值:false 只记录id
org.hibernate.envers.default_schema 配置数据修改记录表的schema 默认值:null
org.hibernate.envers.default_catalog 配置数据修改记录表的catalog 默认值:null
属性配置方法: hibernate.cfg.xml
<property name="org.hibernate.envers.audit_table_suffix">_Audit</property>
<property name="org.hibernate.envers.audit_table_prefix">log_</property>
<property name="org.hibernate.envers.revision_field_name">rev</property>
<property name="org.hibernate.envers.revision_type_field_name">revtype</property>
<property name="org.hibernate.envers.revision_on_collection_change">true</property>
<property name="org.hibernate.envers.do_not_audit_optimistic_locking_field">true</property>
<property name="org.hibernate.envers.store_data_at_delete">true</property>
<property name="org.hibernate.envers.default_schema"></property>
<property name="org.hibernate.envers.default_catalog"></property>
使用hibernate auto-generate功能,自动创建数据修改记录表
在hibernate.cfg.xml 加入以下内容
<property name="hibernate.hbm2ddl.auto">update</property>
2. 代码开发
Envers目前提供的常用几类API如下:
@Audited 标记该Entity类或属性支持数据修改记录支持。 Target(value={java.lang.annotation.ElementType.TYPE,java.lang.annotation.ElementType.METHOD,java.lang.annotation.ElementType.FIELD}
@NotAudited 标记该属性不支持数据修改记录支持 Target(value={java.lang.annotation.ElementType.METHOD,java.lang.annotation.ElementType.FIELD})
@RevisionEntity 实现为数据修改记录表保存其它自定义内容实现。如修改时间,操作人等。该类必须是一个实体类,会将数据存放一个单独表中。
@RevisionTimestamp 记录修改时间 必须配合 @RevisionEntity使用
@RevisionNumber 修改记录表的版本id 通常是配置成主键
2.1 演示示例
有一个student实体。添了@Audited设置
1
@Audited
2 @Entity
3 public class Student implements Serializable {
4 /**
5 * serial Version UID
6 */
7 private static final long serialVersionUID = 478336850989535510L ;
8
9 private int age;
10
11 private int id;
12
13 private String name;
14
15 private byte [] data;
16
17 private int version;
18
19 /**
20 * @return the data
21 */
22 @NotAudited
23 @Column(name = " data " , length = 5000 )
24 public byte [] getData() {
25 return data;
26 }
27
28 /**
29 * @param data the data to set
30 */
31 public void setData( byte [] data) {
32 this .data = data;
33 }
34
35 @Override
36 public String toString() {
37 return new ToStringBuilder( this ).append( " age " , age).append( " id " , id)
38 .append( " name " , name).toString();
39 }
40
41 @Column(name = " age " )
42 public int getAge() {
43 return age;
44 }
45
46 @Id
47 @GeneratedValue
48 public int getId() {
49 return id;
50 }
51
52 @Column(name = " name " , length = 100 )
53 public String getName() {
54 return name;
55 }
56
57 public void setAge( int age) {
58 this .age = age;
59 }
60
61 public void setId( int id) {
62 this .id = id;
63 }
64
65 public void setName(String name) {
66 this .name = name;
67 }
68
69 /**
70 * @return the version
71 */
72 @Version
73 public int getVersion() {
74 return version;
75 }
76
77 /**
78 * @param version the version to set
79 */
80 public void setVersion( int version) {
81 this .version = version;
82 }
83 }
2 @Entity
3 public class Student implements Serializable {
4 /**
5 * serial Version UID
6 */
7 private static final long serialVersionUID = 478336850989535510L ;
8
9 private int age;
10
11 private int id;
12
13 private String name;
14
15 private byte [] data;
16
17 private int version;
18
19 /**
20 * @return the data
21 */
22 @NotAudited
23 @Column(name = " data " , length = 5000 )
24 public byte [] getData() {
25 return data;
26 }
27
28 /**
29 * @param data the data to set
30 */
31 public void setData( byte [] data) {
32 this .data = data;
33 }
34
35 @Override
36 public String toString() {
37 return new ToStringBuilder( this ).append( " age " , age).append( " id " , id)
38 .append( " name " , name).toString();
39 }
40
41 @Column(name = " age " )
42 public int getAge() {
43 return age;
44 }
45
46 @Id
47 @GeneratedValue
48 public int getId() {
49 return id;
50 }
51
52 @Column(name = " name " , length = 100 )
53 public String getName() {
54 return name;
55 }
56
57 public void setAge( int age) {
58 this .age = age;
59 }
60
61 public void setId( int id) {
62 this .id = id;
63 }
64
65 public void setName(String name) {
66 this .name = name;
67 }
68
69 /**
70 * @return the version
71 */
72 @Version
73 public int getVersion() {
74 return version;
75 }
76
77 /**
78 * @param version the version to set
79 */
80 public void setVersion( int version) {
81 this .version = version;
82 }
83 }
生成对应的数据修改记录表sql脚本为:
1
CREATE
TABLE
`log_student_audit` (
2 `id` int ( 11 ) NOT NULL ,
3 `rev` int ( 11 ) NOT NULL ,
4 `revtype` tinyint ( 4 ) DEFAULT NULL ,
5 `age` int ( 11 ) DEFAULT NULL ,
6 `name` varchar ( 100 ) DEFAULT NULL ,
7 PRIMARY KEY (`id`,`rev`),
8 KEY `FKD8A956DCF6C3C1B7` (`rev`)
9 ) ENGINE = InnoDB DEFAULT CHARSET = utf8;
2 `id` int ( 11 ) NOT NULL ,
3 `rev` int ( 11 ) NOT NULL ,
4 `revtype` tinyint ( 4 ) DEFAULT NULL ,
5 `age` int ( 11 ) DEFAULT NULL ,
6 `name` varchar ( 100 ) DEFAULT NULL ,
7 PRIMARY KEY (`id`,`rev`),
8 KEY `FKD8A956DCF6C3C1B7` (`rev`)
9 ) ENGINE = InnoDB DEFAULT CHARSET = utf8;
为 Student修改记录内容扩展保存一些其它内容实现如下:
增加 SimpleRevEntity 实体类
1
@Entity
2 @RevisionEntity(SimpleListener. class )
3 @Table(name = " global_revisions_info " )
4 public class SimpleRevEntity {
5 @Id
6 @GeneratedValue
7 @RevisionNumber
8 @Column(name = " revision_id " )
9 private int id;
10 @RevisionTimestamp
11 @Column(name = " revision_timestamp " )
12 private Date timestamp;
13 /**
14 * @return the id
15 */
16 public int getId() {
17 return id;
18 }
19 /**
20 * @param id the id to set
21 */
22 public void setId( int id) {
23 this .id = id;
24 }
25 /**
26 * @return the timestamp
27 */
28 public Date getTimestamp() {
29 return timestamp;
30 }
31 /**
32 * @param timestamp the timestamp to set
33 */
34 public void setTimestamp(Date timestamp) {
35 this .timestamp = timestamp;
36 }
37 }
2 @RevisionEntity(SimpleListener. class )
3 @Table(name = " global_revisions_info " )
4 public class SimpleRevEntity {
5 @Id
6 @GeneratedValue
7 @RevisionNumber
8 @Column(name = " revision_id " )
9 private int id;
10 @RevisionTimestamp
11 @Column(name = " revision_timestamp " )
12 private Date timestamp;
13 /**
14 * @return the id
15 */
16 public int getId() {
17 return id;
18 }
19 /**
20 * @param id the id to set
21 */
22 public void setId( int id) {
23 this .id = id;
24 }
25 /**
26 * @return the timestamp
27 */
28 public Date getTimestamp() {
29 return timestamp;
30 }
31 /**
32 * @param timestamp the timestamp to set
33 */
34 public void setTimestamp(Date timestamp) {
35 this .timestamp = timestamp;
36 }
37 }
SimpleListener 代码如下:
1
public
class
SimpleListener
implements
RevisionListener {
2 public void newRevision(Object revisionEntity) {
3 SimpleRevEntity revEnitty = (SimpleRevEntity) revisionEntity;
4 // add your additional info to the SimpleRevEntity here
5 }
6 }
2 public void newRevision(Object revisionEntity) {
3 SimpleRevEntity revEnitty = (SimpleRevEntity) revisionEntity;
4 // add your additional info to the SimpleRevEntity here
5 }
6 }
rev entity生成的数据库表脚本如下:
CREATE
TABLE
`global_revisions_info` (
`revision_id` int ( 11 ) NOT NULL AUTO_INCREMENT,
`revision_timestamp` datetime DEFAULT NULL ,
PRIMARY KEY (`revision_id`)
) ENGINE = InnoDB AUTO_INCREMENT = 6 DEFAULT CHARSET = utf8;
`revision_id` int ( 11 ) NOT NULL AUTO_INCREMENT,
`revision_timestamp` datetime DEFAULT NULL ,
PRIMARY KEY (`revision_id`)
) ENGINE = InnoDB AUTO_INCREMENT = 6 DEFAULT CHARSET = utf8;
3. 数据库版本修改信息查询
1
#获得 AuditReader
2 AuditReader reader = AuditReaderFactory.get(session);
3
4 #根据版本号, 实体主键,找到修改之前该版本的数据
5 Person oldPerson = reader.find(Person. class , personId, revision)
6
7
8 #得到某条记录的所有修改的版本号
9 List < Number > revisions = reader.getRevisions(Person. class , personId);
10
11 #根据修改版本号,得到修改时间
12 Date date = reader.getRevisionDate(revision)
13
14 #根据时间,得到修改的版本号
15 Number revision = reader.getRevisionNumberForDate(date)
16
17 #得到当前最新的版本号. persist参数据,当为true时,当前的revisionEntityClass如果未进行持久操作,则进行持久操作
18 < T > T getCurrentRevision(Class < T > revisionEntityClass, boolean persist);
2 AuditReader reader = AuditReaderFactory.get(session);
3
4 #根据版本号, 实体主键,找到修改之前该版本的数据
5 Person oldPerson = reader.find(Person. class , personId, revision)
6
7
8 #得到某条记录的所有修改的版本号
9 List < Number > revisions = reader.getRevisions(Person. class , personId);
10
11 #根据修改版本号,得到修改时间
12 Date date = reader.getRevisionDate(revision)
13
14 #根据时间,得到修改的版本号
15 Number revision = reader.getRevisionNumberForDate(date)
16
17 #得到当前最新的版本号. persist参数据,当为true时,当前的revisionEntityClass如果未进行持久操作,则进行持久操作
18 < T > T getCurrentRevision(Class < T > revisionEntityClass, boolean persist);
Good Luck!
Yours Matthew!