no-image

使用envers記錄資料變更版本

                                    

做APP,小程式就上開源眾包。世界盃期間免費領2600元啟動金。
>>>
  

hibernate的envers模組提供了一整套機制可以用來記錄資料的變更。這裡簡單介紹一下。

1.自動配置

@SpringBootApplication
@EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class)
@EnableJpaAuditing(auditorAwareRef = "auditorAwareImpl")
public class EnversDemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(EnversDemoApplication.class, args);
	}
}

這裡配置@EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class)表示開啟envers模組。

2.Audited註解

@Entity
@Audited
public class Book extends AuditableEntity{

    @javax.persistence.Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String title;

    private String author;

    private String description;

    private long price;

    private boolean valid;

    // getter and setter
}

使用@Audited標註一下這個實體類需要審計。

繼承RevisionRepository

public interface BookDao extends RevisionRepository<Book, Long, Integer>,JpaRepository<Book, Long> {
}

通過繼承RevisionRepository獲取查詢revisions的查詢方法,主要如下:

@NoRepositoryBean
public interface RevisionRepository<T, ID extends Serializable, N extends Number & Comparable<N>> {
    Revision<N, T> findLastChangeRevision(ID var1);

    Revisions<N, T> findRevisions(ID var1);

    Page<Revision<N, T>> findRevisions(ID var1, Pageable var2);

    Revision<N, T> findRevision(ID var1, N var2);
}

這裡的N是指變更版本號的型別,一般Integer夠用,如果覺得不夠用可以改為Long型別。這裡的T就是實體類。ID就是實體類的主鍵型別。

查詢變更

經過以上配置之後,就可以正常記錄變更的每個版本了,可以通過如下方法來查詢,比如:

Revisions<Integer,Book> revision = bookDao.findRevisions(id);
List<Revision<Integer,Book>> data = revision.getContent();

這裡的id為實體的id

revision的表結構

hibernate預設以實體類字尾_AUD來記錄每個變更的版本,比如

-- ----------------------------
--  Table structure for book_aud
-- ----------------------------
DROP TABLE IF EXISTS "public"."book_aud";
CREATE TABLE "public"."book_aud" (
	"id" int8 NOT NULL,
	"rev" int4 NOT NULL,
	"revtype" int2,
	"author" varchar(255) COLLATE "default",
	"description" varchar(255) COLLATE "default",
	"price" int8,
	"title" varchar(255) COLLATE "default",
	"valid" bool
)
WITH (OIDS=FALSE);
ALTER TABLE "public"."book_aud" OWNER TO "postgres";

-- ----------------------------
--  Primary key structure for table book_aud
-- ----------------------------
ALTER TABLE "public"."book_aud" ADD PRIMARY KEY ("id", "rev") NOT DEFERRABLE INITIALLY IMMEDIATE;

-- ----------------------------
--  Foreign keys structure for table book_aud
-- ----------------------------
ALTER TABLE "public"."book_aud" ADD CONSTRAINT "fk2u9iq76nh69r6f989ae7xft9" FOREIGN KEY ("rev") REFERENCES "public"."revinfo" ("rev") ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE;

如果想改表字尾的話,可以通過org.hibernate.envers.audit_table_suffix的屬性進行配置。裡頭的rev欄位表示revision的版本號,一般全域性遞增。revtype欄位表示操作型別,0表示新增,1表示更新,2表示刪除。

另外還有一個表是revinfo,記錄每個變更的版本號和時間:

-- ----------------------------
--  Table structure for revinfo
-- ----------------------------
DROP TABLE IF EXISTS "public"."revinfo";
CREATE TABLE "public"."revinfo" (
	"rev" int4 NOT NULL,
	"revtstmp" int8
)
WITH (OIDS=FALSE);
ALTER TABLE "public"."revinfo" OWNER TO "postgres";

-- ----------------------------
--  Primary key structure for table revinfo
-- ----------------------------
ALTER TABLE "public"."revinfo" ADD PRIMARY KEY ("rev") NOT DEFERRABLE INITIALLY IMMEDIATE;

自定義revision entity

如果預設的envers的實現不滿足你的要求的話,使用@RevisionEntity註解,替換@Audited,然後自定義listener,比如

@Entity
@RevisionEntity( ExampleListener.class )
public class ExampleRevEntity extends DefaultRevisionEntity {
    private String username;

    public String getUsername() { return username; }
    public void setUsername( String username ) { this.username = username; }
}
public class ExampleListener implements RevisionListener {

    public void newRevision( Object revisionEntity ) {
        ExampleRevEntity exampleRevEntity = ( ExampleRevEntity ) revisionEntity;
        Identity identity =
            (Identity) Component.getInstance( "org.jboss.seam.security.identity" );

        exampleRevEntity.setUsername( identity.getUsername() );
    }
}

具體的這裡就不細講了,具體可以參考hibernate。

doc

envers


(adsbygoogle = window.adsbygoogle || []).push({});

function googleAdJSAtOnload() {
var element = document.createElement(“script”);
element.src = “//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js”;
element.async = true;
document.body.appendChild(element);
}
if (window.addEventListener) {
window.addEventListener(“load”, googleAdJSAtOnload, false);
} else if (window.attachEvent) {
window.attachEvent(“onload”, googleAdJSAtOnload);
} else {
window.onload = googleAdJSAtOnload;
}