提问者:小点点

当有两个更新和中间的搜索时,使用Envers的Hibernate审核会跳过一个版本


问题我在JPA中使用hibernate envers(@Auded)并具有以下行为。如果在两次更新之间对实体进行了搜索,它似乎会跳过历史表中的一个版本。

使用Hibernate 5.5.7、MSSQL Server 15、Java1.8

以下是步骤

  1. 创建一个实体。(刷新并提交)(我的实体有一个带有@Version的版本列)
  2. 启动事务
  3. 检索实体并更新属性(例如更改名称)。不要刷新
  4. 再次检索实体并更新另一个属性(例如更改id)
  5. 提交事务。

预期结果在我的历史记录表中,应该有相同数量的记录与我的版本列中的数字匹配。(例如,如果版本为2,则应该有3条记录用于0、1和2)

实际结果历史表中只有两行一个版本=0,另一个版本=2所以审计表中缺少版本1。

更多想法这似乎正在发生,因为在第3步中,Hibernate在我们搜索时会自动刷新。当有自动刷新时,我的实体版本会更新,因为它发生了变化。但是对于那个版本,没有生成历史记录。

是否有一些解决方法来获取历史表中的“缺失”版本?或者,在设置@审核表时,我可能缺少一些步骤。

这是项目的链接:

这是我的代码Main.java

    package se.navod.platform.test.hibernate;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

public class Main {
    private static EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("PlatformTest");

    public static void main(String[] args) {
    EntityTransaction transaction = null;
    try {
        int id = ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE - 1);
        String name = "Name_" + id;
        create(id, name, 10);// Create the student

        EntityManager manager = entityManagerFactory.createEntityManager();
        transaction = manager.getTransaction();

        transaction.begin();// Start the transaction

        Student student1 = findStudentByPropertyValue(manager, name, "name");// Retrieve the student by name
        student1.setName("NewName");// Change the name

        // Retrieve the same student again (by id)
        Student student2 = findStudentByPropertyValue(manager, "" + id, "studentId");

        student2.setAge(50);// Change the age
        manager.persist(student2);// Persist and commit

        transaction.commit();

    } catch (Exception e) {
        e.printStackTrace();
        if (transaction != null) {
        transaction.rollback();
        }
    } finally {
        if (entityManagerFactory != null) {
        entityManagerFactory.close();
        }
    }
    }

    public static Student create(int id, String name, int age) {
    EntityManager manager = entityManagerFactory.createEntityManager();
    EntityTransaction transaction = null;

    try {
        transaction = manager.getTransaction();
        transaction.begin();
        Student stu = new Student(id, name, age);
        manager.persist(stu);
        transaction.commit();
        return stu;
    } catch (Exception ex) {
        if (transaction != null) {
        transaction.rollback();
        }
        ex.printStackTrace();
        throw ex;
    } finally {
        manager.close();
    }
    }

    private static Student findStudentByPropertyValue(EntityManager manager, String value, String property) {
    CriteriaBuilder criteriaBuilder = manager.getCriteriaBuilder();
    CriteriaQuery<Student> criteriaQuery = criteriaBuilder.createQuery(Student.class);
    Root<Student> itemRoot = criteriaQuery.from(Student.class);

    Predicate predicate = criteriaBuilder.equal(itemRoot.get(property), value);
    criteriaQuery.where(predicate);
    criteriaQuery.orderBy(criteriaBuilder.asc(itemRoot.get("id")));

    List<Student> items = manager.createQuery(criteriaQuery).getResultList();
    return items.size() == 0 ? null : items.get(0);
    }

}

实体:Student.java

    package se.navod.platform.test.hibernate;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Version;

import org.hibernate.envers.Audited;

@Entity
@Table(name = "student")
@Audited
public class Student implements Serializable {

    private static final long serialVersionUID = -834531085311709309L;

    @Column(name = "student_id", unique = true)
    private int studentId;

    @Id
    @Column(name = "primaryKey")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(name = "student_name", nullable = false)
    private String name;

    @Column(name = "student_age", nullable = false)
    private int age;

    @Version
    private long version;

    public Student() {
    super();
    }

    public Student(int studentId, String name, int age) {
    super();
    this.studentId = studentId;
    this.name = name;
    this.age = age;
    }

    public void setId(int id) {
    this.id = id;
    }

    public long getVersion() {
    return version;
    }

    public void setVersion(long version) {
    this.version = version;
    }

    public int getStudentId() {
    return studentId;
    }

    public void setStudentId(int studentId) {
    this.studentId = studentId;
    }

    public int getId() {
    return id;
    }

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }

    public int getAge() {
    return age;
    }

    public void setAge(int age) {
    this.age = age;
    }

    @Override
    public String toString() {
    return id + "\t" + name + "\t" + age;
    }
}

上一页. xml

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
    version="2.0">
    <persistence-unit name="PlatformTest"
        transaction-type="RESOURCE_LOCAL">
        <!-- Persistence provider -->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

        <!-- Entity classes -->
        <class>se.navod.platform.test.hibernate.Student</class>

        <properties>

            <property name="javax.persistence.jdbc.url"
                value="jdbc:jtds:sqlserver://localhost:1433/Test;prepareSQL=0;" />

            <!-- The database username -->
            <property name="javax.persistence.jdbc.user" value="spider3" />

            <!-- The database password -->
            <property name="javax.persistence.jdbc.password"
                value="spider3" />
            <property name="hibernate.dialect"
                value="org.hibernate.dialect.SQLServer2012Dialect" />


            <property name="org.hibernate.envers.audit_table_suffix"
                value="_History" />
            <property
                name="org.hibernate.envers.do_not_audit_optimistic_locking_field"
                value="false" />

            <property name="org.hibernate.envers.revision_field_name"
                value="revision" />
            <property
                name="org.hibernate.envers.revision_type_field_name"
                value="revisionType" />
                
        </properties>
    </persistence-unit>
</persistence>

DB表脚本

    USE [Test]
    GO
    
    /****** Object:  Table [dbo].[student]    Script Date: 9/13/2021 12:07:13 PM ******/
    SET ANSI_NULLS ON
    GO
    
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE TABLE [dbo].[student](
        [student_id] [int] NULL,
        [student_name] [nchar](1000) NULL,
        [student_age] [int] NULL,
        [primaryKey] [bigint] IDENTITY(1,1) NOT NULL,
        [version] [bigint] NOT NULL,
     CONSTRAINT [PK_persionId] PRIMARY KEY NONCLUSTERED 
    (
        [primaryKey] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
    ) ON [PRIMARY]
    GO

CREATE TABLE [dbo].[student_History](
    [student_id] [int] NULL,
    [student_name] [nchar](1000) NULL,
    [student_age] [int] NULL,
    [primaryKey] [bigint] NOT NULL,
    [version] [bigint] NOT NULL,
    [revision] [int] NOT NULL,
    [revisionType] [tinyint] NULL
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[REVINFO](
    [id] [int] NULL,
    [REVTSTMP] [bigint] NOT NULL
) ON [PRIMARY]
GO

共1个答案

匿名用户

这是意料之中的。如果您希望创建多个版本,请查看以下讨论:https://discourse.hibernate.org/t/envers-create-multiple-revisions-in-one-transaction/5138/2