Einführung in Hibernate Envers

Hibernate Envers ist eine Bibliothek, die uns hilft, Audit-Funktionalitäten einfach zu erreichen. Ziel ist es, eine einfache Prüfung/Versionierung persistenter Klassen/Entitäten zu ermöglichen. Es bietet einen integrierten Mechanismus zur Verwaltung des Tabellenverlaufs im Ruhezustand. Hibernate Envers wurde erstellt von Adam Warski. Ab Hibernate 3.5 ist Envers Teil des Kernmoduls.

Envers funktioniert ähnlich wie Versionskontrollsysteme (wie Subversion/CVS usw.), die Revisionen für Änderungen verwalten. In Envers ist jede Transaktion eine Revision (es sei denn, die Transaktion hat keine geprüfte Entität geändert). Revision hat das gleiche Konzept wie „Änderungssatz in Versionskontrollsystemen“.

lesen Sie auch:

  • Welches Hibernate-Framework?
  • Abfangjäger im Hibernate Framework

Mit der Revisionsnummer können Sie verschiedene Details abrufen, wie zum Beispiel:

  • Zeitstempel des Revisions-Commits
  • Abfrage nach verschiedenen Entitäten bei dieser Revision

Envers-Funktionen im Ruhezustand

  • Prüfung aller durch die JPA-Spezifikation definierten Zuordnungen oder Hibernate-Zuordnungen (die JPA erweitern)
  • Abfrage historischer Daten

Envers überwintern

(Quelle : SlideShare)

Umgebungseinrichtung

  • Fügen Sie das Hibernate-Envers-JAR zum Klassenpfad hinzu
  • Fügen Sie Hibernate Annotations oder Entity Manager-JARs zum Klassenpfad hinzu
  • Entitäten müssen unveränderliche eindeutige Identifikatoren (Primärschlüssel) haben.
  • Laden Sie den Quellcode herunter, um die Maven-Abhängigkeiten zu finden, die zum Ausführen eines einfachen Beispiels erforderlich sind

Wie funktioniert Envers?

  • Kommentieren Sie Ihre persistente Klasse oder einige ihrer Eigenschaften, mit denen Sie prüfen möchten @Geprüft.
  • Wenn Sie alte Versionen von Hibernate verwenden, müssen Sie möglicherweise Listener (siehe unten) in der Hibernate-Konfigurationsdatei angeben. Wenn Sie die neuesten Versionen von Hibernate verwenden, fügen Sie einfach die JAR-Datei „Hibernate-Envers“ zum Klassenpfad hinzu. Dadurch werden automatisch Listener registriert.
<listener   type="post-insert"/>
<listener   type="post-update"/>
<listener   type="post-delete"/>
<listener   type="pre-collection-update"/>
<listener   type="pre-collection-remove"/>
<listener   type="post-collection-recreate"/>
  • Für jede geprüfte Entität wird eine neue Tabelle „entity_table_AUD“ erstellt, die den Verlauf der an der Entität vorgenommenen Änderungen enthält.
  • Hibernate erstellt automatisch Prüftabellen, wenn die folgende Eigenschaft in der Hibernate-Konfigurationsdatei auf „Erstellen, Erstellen, Löschen oder Aktualisieren“ eingestellt ist.
<property name="hbm2ddl.auto">update</property>
  • Alternativ können Sie DDL-Anweisungen auch mit Ant-Tasks oder manuell erstellen.
  • Anschließend können Sie ohne großen Aufwand historische Daten abrufen und abfragen. Auf die Prüfung (Historie) einer Entität kann über zugegriffen werden AuditReader Schnittstelle, die mit einem geöffneten EntityManager oder einer Sitzung über die abgerufen werden kann AuditReaderFactory.

Beispiel für den Ruhezustand von Envers

hibernate.cfg.xml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory>

		<!-- DB Connection Details -->
		<property name="connection.url">jdbc:mysql://localhost:3306/testdb</property>
		<property name="connection.username">root</property>
		<property name="connection.password">root</property>
		<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="dialect">org.hibernate.dialect.MySQLDialect</property>

		<!-- Logger Details : to show queries on console-->
		<property name="show_sql">true</property>
		<property name="format_sql">true</property>

		<!-- Envers automatically creates audit tables if below property is set to create, create-drop or update -->
		<property name="hbm2ddl.auto">update</property>

		<property name="connection.pool_size">1</property>
		<property name="current_session_context_class">thread</property>

		<!-- Entity mapping -->
		<mapping   />

	</session-factory>
</hibernate-configuration>

Die obige Datei hibernate.cfg.xml ist leicht zu verstehen. Nachfolgend finden Sie eine wichtige Eigenschaft der obigen XML-Datei:

<property name="hbm2ddl.auto">update</property>

Die obige Eigenschaft ist für die automatische „Audit Table Creation“ erforderlich. Alternativ können Sie Tabellen auch manuell erstellen (folgen Sie dem Audit Table-Format).

Beispiel für den Ruhezustand von Envers

Ich habe ein einfaches Beispiel erstellt, das veranschaulicht, wie Hibernate Envers die Revisionsdaten in verschiedenen Szenarien speichert. Dieses Beispiel besteht aus den folgenden Komponenten. Am Ende dieses Tutorials finden Sie einen Download-Link zum Herunterladen des Quellcodes für dieses Beispiel.

  • HibernateUtil.java – Sitzungsfabrik erstellen
  • Beispiel 1 – Verwendung von @Audited auf Klassenebene
  • Beispiel 2 – Verwendung von @Audited auf Klassenebene und @NotAudited für Eigenschaften
  • Beispiel 3 – Verwendung von @Audited auf Objektebene
  • HibernateUtil.java

    Es handelt sich lediglich um eine Utility-Klasse zum Aufbau der Hibernate Session Factory.

    package com.util;
    
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    
    public class HibernateUtil {
    	private static final SessionFactory sessionFactory = buildSessionFactory();
    	private static SessionFactory buildSessionFactory() {
    		try {
    			return new Configuration().configure().buildSessionFactory();
    		} catch (Exception ex) {
    			System.err.println("Build SessionFactory Failed:" + ex);
    			throw new ExceptionInInitializerError(ex);
    		}
    	}
    	public static SessionFactory getSessionFactory() {
    		return sessionFactory;
    	}
    	public static void shutdown() {
    		getSessionFactory().close();
    	}
    }
    

    Beispiel 1

    package com.domain;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    import org.hibernate.envers.Audited;
    
    @Entity
    @Table(name = "article")
    @Audited
    public class Article {
    
    	@Id
    	@GeneratedValue
    	@Column(name = "id")
    	private int id;
    
    	@Column(name = "subject")
    	private String subject;
    
    	@Column(name = "content")
    	private String content;
    
    	@Column(name = "author")
    	private String author;
    
    	public int getId() {
    		return id;
    	}
    	public void setId(int id) {
    		this.id = id;
    	}
    	public String getSubject() {
    		return subject;
    	}
    	public void setSubject(String subject) {
    		this.subject = subject;
    	}
    	public String getContent() {
    		return content;
    	}
    	public void setContent(String content) {
    		this.content = content;
    	}
    	public String getAuthor() {
    		return author;
    	}
    	public void setAuthor(String author) {
    		this.author = author;
    	}
    }
    

    In der obigen Entität können Sie sehen, dass „@Audited“ auf Klassenebene verwendet wird, was bedeutet, dass alle Eigenschaften der Klasse in die Prüfung einbezogen werden.

    Testklasse:

    package com.test;
    import java.util.List;
    import org.hibernate.Session;
    import org.hibernate.envers.AuditReader;
    import org.hibernate.envers.AuditReaderFactory;
    import com.domain.Article;
    import com.util.HibernateUtil;
    public class TestArticle {
    	public static void main(String[] args) {
    		TestArticle testArticle = new TestArticle();
    		// creating two articles
    		Article firstArticle = getFirstArticle(createArticle());
    		Article secondArticle = getSecondArticle(createArticle());
    		// saving both articles
    		testArticle.saveArticle(firstArticle);					// Transaction 1
    		testArticle.saveArticle(secondArticle);					// Transaction 2
    		// updating first article
    		firstArticle.setContent("Test Content2");
    		testArticle.saveOrUpdateArticle(firstArticle);			// Transaction 3
    
    		// deleting second article
    		testArticle.deleteArticle(secondArticle);				// Transaction 4
    		AuditReader reader = AuditReaderFactory.get(HibernateUtil.getSessionFactory().openSession());
    		// List all revisions of an entity
    		List<Number> versions = reader.getRevisions(Article.class, new Integer(2));
    		for (Number number : versions) {
    		  System.out.print(number + " ");
    		}
    
    		// Retrieve an object in a previous version
    		Article firstArticleFromDB = (Article) reader.find(Article.class, new Integer(1), 2);
    		System.out.println("Output 1: " + firstArticleFromDB.getId() + " " + firstArticleFromDB.getSubject() + " " +firstArticleFromDB.getContent() + " " + firstArticleFromDB.getAuthor());
    
    	}
    	private static Article createArticle() {
    		return new Article();
    	}
    
    	private static Article getFirstArticle(Article firstArticle) {
    		firstArticle.setSubject("First Article : Hibernate Envers");
    		firstArticle.setContent("Test Content");
    		firstArticle.setAuthor("Author1");
    		return firstArticle;
    	}
    	private static Article getSecondArticle(Article secondArticle) {
    		secondArticle.setSubject("Second Article : Hibernate Envers II");
    		secondArticle.setContent("Test Content");
    		secondArticle.setAuthor("Author1");
    		return secondArticle;
    	}
    	public void saveArticle(Article article) {
    		Session session = HibernateUtil.getSessionFactory().openSession();
    		session.beginTransaction();
    
    		session.save(article);
    
    		session.getTransaction().commit();
    	}
    	public void saveOrUpdateArticle(Article article) {
    		Session session = HibernateUtil.getSessionFactory().openSession();
    		session.beginTransaction();
    
    		session.saveOrUpdate(article);
    
    		session.getTransaction().commit();
    	}
    	public void deleteArticle(Article article) {
    		Session session = HibernateUtil.getSessionFactory().openSession();
    		session.beginTransaction();
    
    		session.delete(article);
    
    		session.getTransaction().commit();
    	}
    }
    
    

    In der oben genannten Testklasse führen wir 4 Transaktionen durch:

    • Transaktion 1: Speichern von „firstArticle“
    • Transaktion 2: Speichern von „secondArticle“
    • Transaktion 3: Aktualisierung von „firstArticle“
    • Transaktion 4: Löschen von „secondArticle“

    Als wir das obige Testprogramm ausführen, wurden zwei Tabellen erstellt (außer der Tabelle „Artikel“, bei der es sich um eine Domänentabelle handelt):

    • Article_aud: Dies enthält den Verlauf (Änderung/Erstellung/Löschung) unserer Entitäten. Der Primärschlüssel besteht aus den Feldern ID und REV.

    Sie können 4 Revisionen in der Tabelle „article_aud“ sehen:

    Ausgabe_1
    
    Since we added @Audited annotation at class level, all property of the class are available in Audit Table.
    

    Abgesehen davon haben wir zwei zusätzliche Spalten in der Audit-Tabelle:

  • REV: Revisionsnummer, die die gleiche Reihenfolge beibehält, in der Sie Transaktionen durchgeführt haben.
  • REVTYPE: Definiert die Art der Transaktion, ob es sich um Speichern/Aktualisieren/Löschen handelt
    • 0 bedeutet Schöpfung
    • 1 bedeutet Modifikation
    • 2 bedeutet Löschen

    revinfo: Enthält den Zeitstempel der Revision

    Ausgabe_2

    Der Zeitstempel hat das Format „Lang“. Sie können es ganz einfach in ein für Menschen lesbares Format konvertieren (Beispielcode finden Sie unten):

    long val = 1429237881803l;
    Date date=new Date(val);
    SimpleDateFormat df2 = new SimpleDateFormat("dd/MM/yy");
    String dateText = df2.format(date);
    System.out.println(dateText);
    

    Das Testprogramm enthält auch Code für:

    • Auflistung aller Revisionen einer Entität
    • Außerdem wird ein Objekt bei einer bestimmten Revision abgerufen

    Wir verwenden die „AuditReader“-API zum Abfragen von Details aus Audit-Tabellen.

    Beispiel 2:

    Jetzt ändere ich meine Domain-Klasse noch ein wenig @Geprüft Anmerkungen werden auf Klassenebene verwendet, aber ich habe sie hinzugefügt @NotAudited für die Eigenschaft „Autor“.

    package com.domain;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.Table;
    import org.hibernate.envers.Audited;
    import org.hibernate.envers.NotAudited;
    @Entity
    @Table(name = "article")
    @Audited
    public class Article {
     @Id
     @GeneratedValue
     @Column(name = "id")
     private int id;
     @Column(name = "subject")
     private String subject;
     @Column(name = "content")
     private String content;
     @Column(name = "author")
     @NotAudited
     private String author;
    
     public int getId() {
     return id;
     }
     public void setId(int id) {
     this.id = id;
     }
     public String getSubject() {
     return subject;
     }
     public void setSubject(String subject) {
     this.subject = subject;
     }
     public String getContent() {
     return content;
     }
     public void setContent(String content) {
     this.content = content;
     }
     public String getAuthor() {
     return author;
     }
     public void setAuthor(String author) {
     this.author = author;
     }
    }
    

    Wenn wir das Testprogramm ausführen und die Tabelle „article_aud“ überprüfen, können wir die folgende Ausgabe sehen:

    Ausgabe_3

    Sie können feststellen, dass es jetzt keine Spalte „Autor“ in der Audit-Tabelle gibt.

    Beispiel 3:

    Jetzt ändere ich meine Domänenklasse erneut und verwende anstelle der Annotation „@NotAudited“ die Annotation „@Audited“ auf Eigenschaftsebene (ich habe die Annotation @Audited von der Klassenebene entfernt).

    package com.domain;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    import org.hibernate.envers.Audited;
    @Entity
    @Table(name = "article")
    public class Article {
    
     @Id
     @GeneratedValue
     @Column(name = "id")
     private int id;
    
     @Column(name = "subject")
     @Audited
     private String subject;
    
     @Column(name = "content")
     private String content;
    
     @Column(name = "author")
     private String author;
    
     public int getId() {
     return id;
     }
     public void setId(int id) {
     this.id = id;
     }
     public String getSubject() {
     return subject;
     }
     public void setSubject(String subject) {
     this.subject = subject;
     }
     public String getContent() {
     return content;
     }
     public void setContent(String content) {
     this.content = content;
     }
     public String getAuthor() {
     return author;
     }
     public void setAuthor(String author) {
     this.author = author;
     }
    }
    

    Wenn Sie das Testprogramm ausführen, können Sie die folgende Ausgabe in der Audit-Tabelle sehen:

    Ausgabe_4

    Sie sehen, dass die Audit-Tabelle nur „id“ und „subject“ aus der Domänenklasse enthält.

    Laden Sie SourceCode herunter

    [wpdm_file id=118 ]

    Kommentar verfassen

    Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

    Nach oben scrollen