【问题标题】:Id of entity is different after hibernate save from oracle database with sequence autogeneration id从具有序列自动生成 ID 的 Oracle 数据库休眠保存后,实体 ID 不同
【发布时间】:2015-10-30 16:16:07
【问题描述】:

从 oracle 触发序列中自动生成 id 的实体。

@Entity
@Table(name = "REPORT", schema = "WEBPORTAL")
public class Report {
    private Integer id;
....


    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="report_sequence")
    @SequenceGenerator(name="report_sequence", sequenceName = "report_id_seq")
    @Column(name="REPORT_ID", unique = true, nullable = false)
    public Integer getId() {
        return id;
    }
....
}

服务

@Service("reportService")
public class ReportServiceImpl implements ReportService {
....

    @Transactional(readOnly=false)
    public void saveOrUpdate(Report report) {
        reportDAO.saveOrUpdate(report);
    }
}

@Repository
public class ReportDAOImpl implements ReportDAO {
....

    @Override
    public Report save(Report report) {
        try {
           Session session = sessionFactory.getCurrentSession();
           session.save(report);
        } catch (Exception e) {
           logger.error("error", e);
        }
        return report;
    }
}

当我调用服务的 saveOrUpdate 然后尝试访问实体的 id 时,我得到的值与数据库中的持久值不同。 自动生成数据库上的值一切正常。有什么建议吗?

reportService.saveOrUpdate(report);
System.out.println(report.getId());

打印:4150 但在数据库中保存的 id 是:84

注意:我获得 Id 的目的来自于我想通过级联来拯救孩子。但是数据库中子项的外键不同(我使用 getId() 获得的 id 值)。

并且数据库中生成的Id加2。EX: 80, 82, 84.

更新:

用于序列生成的 Oracle 触发器

CREATE OR REPLACE TRIGGER REPORT_ID_TRIG    
BEFORE INSERT ON WEBPORTAL.REPORT  
FOR EACH ROW   
BEGIN   
    SELECT report_id_seq.NEXTVAL  
    INTO :new.report_id  
    FROM dual;  
END;  

【问题讨论】:

    标签: java oracle hibernate


    【解决方案1】:

    问题在于有两种不同的机制来生成密钥:

    • 在 Hibernate 级别调用一个序列并使用该值填充 Id 列并将其作为插入键发送到数据库

    • 还有一种 Hibernate 不知道的数据库机制:列通过触发器递增。

    Hibernate 认为插入是使用序列的值进行的,但在数据库中发生了其他事情。最简单的解决方案可能是移除触发机制,让 Hibernate 仅根据序列填充密钥。

    另一种解决方案: 检查应该采用这种格式的触发器定义 (WHEN (new.report_id is null)) 很重要。

    CREATE OR REPLACE TRIGGER TRIGGER_NAME
       BEFORE INSERT ON TABLE_NAME
       FOR EACH ROW
       WHEN (new.id is null)
         BEGIN 
           SELECT SEQUENCE_NAME.NEXTVAL
           INTO :new.id
           FROM dual;
         END
    

    【讨论】:

      【解决方案2】:

      回答:触发器应该检查 id 是否为空

      CREATE OR REPLACE TRIGGER REPORT_ID_TRIG
      BEFORE INSERT ON WEBPORTAL.REPORT
      FOR EACH ROW
      WHEN (new.report_id is null)
      BEGIN 
          SELECT report_id_seq.NEXTVAL
          INTO :new.report_id
          FROM dual;
      END;
      

      说明:
      @GeneratedValue 不仅仅是一个序列生成器。这是 HiLo 算法的一部分。当它第一次从数据库请求 id 时,它将它乘以 50(它可以不同),然后接下来的 50 个新实体将被赋予 id,然后是下一个对数据库的请求。这是为了减少对数据库的请求。
      我从 java 得到的数字是正确的数字,应该保存在报告中。

      没有 id 空值检查 Hibernate 首先从数据库请求 id 并调用 sequence.nextval。当休眠将其持久化(完成事务)时,数据库第二次调用 sequence.next 并将该值设置为数据库。所以在 ReportDetails 上有报告的真实 id 值,而在 Report id 上它是从数据库中设置的 id。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-07-16
        • 1970-01-01
        • 2015-10-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多