【问题标题】:Spring JPA/Hibernate Composite key generation in PrePersist, Custom Generator or Service Layer?PrePersist、自定义生成器或服务层中的 Spring JPA/Hibernate 复合键生成?
【发布时间】:2015-11-27 14:58:16
【问题描述】:

我有一个遗留数据库,我正在使用 JPA/Hibernate 开发一个 Spring MVC 应用程序。我的问题与复合主键的生成有关。一个主键的例子是这样组成的:

序列号、年份、OrderID、LineId

LineId 将根据 Serial、Year 和 LineId 的每个元组的 ma​​x(LineId) 生成。

我想过以下几种方式:


  • PrePersist 侦听器:这意味着侦听器必须访问存储库,甚至可能具有对其他实体的引用才能获得下一个 id。编辑:休眠文档说:A callback method must not invoke EntityManager or Query methods!https://docs.jboss.org/hibernate/orm/4.0/hem/en-US/html/listeners.html#d0e3013
  • 自定义生成器:我还没有找到一个示例来说明如何访问实体的实例来检索我需要进行正确选择的属性。
  • 服务层:太冗长了。
  • 覆盖 Spring 的数据的 JPA 存储库 save() 方法实现:在这种情况下,我们可以访问实体的实例属性。

实现此目的的正确方法是什么?谢谢

【问题讨论】:

  • 我建议在存储库中使用 save 方法,如果每次保存都通过此方法。您只需要强制在存储实体时在任何地方都使用此方法。这就是 DAO/repository 模式的力量——它使您可以修改 entityManger 的标准行为以满足您的需求。
  • 拥有复合主键是一个非常糟糕的主意,因此理想情况下,您应该返回并在旧数据库中添加一个 ID 列,并将当前复合键设为唯一索引。不幸的是,这个世界通常远非理想,但您当前的用例超出了 JPA 直接支持的 JPA 功能的范围。如果其他应用程序正在向表中插入数据,则没有好的解决方案。如果您的应用程序是数据库的唯一客户端,那么我将使用工厂来创建实体(带有 id),并像 Table/Sequence 生成器那样预先分配 Id。

标签: java spring hibernate spring-mvc jpa


【解决方案1】:

为了支持这一点,我经常做的是使用领域驱动的设计技术,当我将OrderLineOrder 关联时,我可以控制它。

public class Order {
  private List<OrderLine> lines;

  // don't allow the external world to modify the lines collection.
  // forces them to use domain driven API exposed below.
  public List<OrderLine> getLines() {
    return Collections.unmodifiableList( lines );
  }

  // avoid allowing external sources to set the lines collection
  // Hibernate can set this despite the method being private.
  private void setLines(List<OrderLine> lines) {
    this.lines = lines;
  }

  public OrderLine addLine(String serial, Integer year) {
    final OrderLine line = new OrderLine( this, serial, year );
    lines.add( line );
    return line;
  }

  public void removeLine(Integer lineId) {
    lines.removeIf( l -> l.getId().getLineId().equals( lineId ) );
  }
}

public OrderLine {
  public OrderLine() {
  }

  OrderLine(Order order, String serial, Integer year) {
    this.id = new OrderLineId( order.getLines().size() + 1, serial, year, order.getId() );
  }
}

唯一调用特殊OrderLine 构造函数的代码是从Order 调用的,并且您确保始终通过聚合根Order 委托OrderLine 实体的添加和删除。

这也意味着您只需要公开一个Order 存储库,并且您只能通过Order 而不是直接操作与Order 关联的行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-05
    • 2023-02-17
    • 1970-01-01
    相关资源
    最近更新 更多