java - Hibernate 错误在@PostPersist 方法中保留一个实体

标签 java hibernate jpa entity lifecycle

我在数据库中出现双重插入的奇怪错误。我有以下类(class):

  • TestEntity - 具有@PrePersist 和@PostPersist 方法的实体。
  • Auditoria - 审计实体
  • Dataset - DatasetBean 的接口(interface)
  • DatasetBean - 实现数据集的无状态 bean
  • DatasetFactory - 实例化数据集的 EJB(查找)

我将问题放在一个 junit 测试中(我使用的是嵌入式 Glassfish):

@Test
public void test() throws NamingException {
    Dataset<TestEntity> dataset = this.lookupBy(DatasetBean.class);
    Assert.assertNotNull(dataset);

    TestEntity t = new TestEntity();        
    t.setName(UUID.randomUUID().toString());

    dataset.insert(t);
    System.out.println("end");
}

测试流程如下:

  1. 获取 Dataset 对象后,我尝试插入 TestEntity 对象

    @无国籍 @EJB(name = "...", beanInterface = Dataset.class) 公共(public)类 DatasetBean 实现数据集 {

    @PersistenceContext(type = PersistenceContextType.TRANSACTION)
    private EntityManager entityManager;
    
    @Override
    public void insert(T entidade) {
        LOG.info("Inserting: " + entidade);
        entityManager.persist(entidade);
    }
    //...
    

  2. 使用 DatasetFactory,我尝试在 TestEntity 的 @PostPersist 方法中插入审计实体

    公共(public)类数据集工厂{ 公共(public)静态数据集 createDataset() { 尝试 { 返回(数据集)新的 InitialContext().lookup("..."); } catch (异常前){ 抛出新的 RuntimeException(ex); } }

    @实体 公共(public)类 TestEntity 实现 MyEntity { @ID 私有(private)整数 id; 私有(private)字符串名称; //设置和获取

    @PrePersist
    public void fillId() {
        if (getId() == null || getId() == 0) {
            Dataset d = DatasetFactory.createDataset();
            Integer i = (Integer) d.fetchJPQLFirstResult("SELECT MAX(te.id) FROM TestEntity te");
            if (i == null || i < 100) {
                setId(100);
            } else {
                setId(i + 1);
            }
        }
    }  
    
    @PostPersist
    public void audit() {
        Dataset<Auditing> dataset = DatasetFactory.createDataset();
        // dataset.getEntityManager().clear();
        Auditing auditing = new Auditing();
        auditing.setIdEntidade(String.valueOf(this.getId()));
        dataset.insert(auditing);
    }
    

    @实体 公共(public)类 Auditoria 实现 MyEntity { @ID @GeneratedValue(策略= GenerationType.IDENTITY) 私有(private)整数 id; 私有(private)字符串 idEntity; //设置和获取

    公共(public)接口(interface) MyEntity 扩展序列化 { 整数 getId();

日志:

信息:嵌入式已在 47.154 毫秒内成功部署。 PlainTextActionReporterSUCCESSSDescription: deploy AdminCommandApplication deployed with name embedded.

2012-01-06 02:56:54,826 [main] INFO com.joaosavio.model.db.DatasetBean (DatasetBean.java:30) - 插入:TestEntity{id=null, name=ea5c2af4-0ca7-48a2- a82a-dbf582c570a9}

Hibernate:从 TestEntity testentity0_ 中选择 max(testentity0_.id) 作为 col_0_0_

Hibernate:插入到 TestEntity (name, id) 值 (?, ?)

2012-01-06 02:56:56,344 [main] INFO com.joaosavio.model.db.DatasetBean (DatasetBean.java:30) - 插入:Auditoria{id=null, idEntidade=100}

Hibernate:插入到 TestEntity (name, id) 值 (?, ?)

2012-01-06 02:56:56,350 [main] WARN org.hibernate.engine.jdbc.spi.SqlExceptionHelper (SqlExceptionHelper.java:143) - SQL 错误:2627,SQLState:23000

2012-01-06 02:56:56,352 [main] 错误 org.hibernate.engine.jdbc.spi.SqlExceptionHelper (SqlExceptionHelper.java:144) - 违反 PRIMARY KEY 约束 'PK_TestEntity_76818E95'。无法在对象“dbo.TestEntity”中插入重复键。

06/01/2012 02:56:56 com.sun.ejb.containers.BaseContainer postInvoke

警告:在调用 EJB DatasetBean 方法时发生系统异常 public void com.joaosavio.model.db.DatasetBean.insert(java.lang.Object) javax.ejb.TransactionRolledbackLocalException:从 bean 抛出的异常 ...

引起:javax.persistence.PersistenceException:org.hibernate.exception.ConstraintViolationException:违反 PRIMARY KEY 约束 'PK_TestEntity_76818E95'。无法在对象“dbo.TestEntity”中插入重复键。 ...

由以下原因引起:org.hibernate.exception.ConstraintViolationException:违反 PRIMARY KEY 约束 'PK_TestEntity_76818E95'。无法在对象“dbo.TestEntity”中插入重复键。

注意事项:

如果我在插入审计实体之前清除实体管理器(在 TestEntity 的@PostPersist 方法中注释代码),我相信一切正常,我相信 TestEntity 会卡在事务中。

我做错了什么???

最佳答案

我曾经见过一个非常相似的问题......你应该 ---

使用@PostPersist 时要非常小心! hibernate bean 持久化或保存操作与数据库插入不同!

问题可能是您假设在插入数据后调用@PostPersist 方法....但是,情况并非总是如此! PostPersist 方法是回调,但它们不是来自数据库的回调!如您所知 - hibernate 可能没有提交您的事务并完全刷新它。如果您尝试使用 PostPersist 来协调数据库事务之间的障碍,那您就犯了一个错误。

解决方案是在一个正确计划和管理的事务中完成所有插入,并制定出键和级联,以便 hibernate 能够以正确的方式为您组织插入——或者只是对存储的硬编码为您完成工作的程序。

我认为您可能会在此处合并有状态和无状态事务逻辑。

关于java - Hibernate 错误在@PostPersist 方法中保留一个实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8753749/

相关文章:

java - Spring + Hibernate session 生命周期

java - JPA 持续一段时间

java - 标题中没有内容类型!尝试访问我的 Web 服务时出错

java - MySql 左外连接未按预期工作

java - 合并网络摄像头流和视频文件并通过 RTP 或 RTSP 发送结果

java - 使用 JPA 创建对象并将其保存到表中,但将表名称作为参数

java - @ElementCollection 和字符串列表的异常

java - Hibernate: LazyInitializationException: 延迟初始化角色集合失败。无法初始化代理 - 没有 session

java - 如何使第二行数字与第一行不同?

java - 如何使用 Jackson 2 将集合的子类序列化和反序列化为已知集合