【问题标题】:Hibernate OneToMany using annotations with legacy databaseHibernate OneToMany 使用带有遗留数据库的注释
【发布时间】:2026-02-06 02:40:01
【问题描述】:

我正在使用一个完全没有标准化的遗留数据库。他们一直在程序中直接编码关系(用 Uniface 编写)。现在,他们正在迁移到 Java。遗憾的是,我无法更改数据库,但我需要映射表之间的 OneToMany 关系。

例如,我有一个表格 Invoice 和一个表格 Item。

发票有这些键。其中前三个用作与Item的关系的外键:

invoice_serial_id,  
invoice_number_id,  
invoice_date,
invoice_otherkey1,
invoice_otherkey2  

Item 有这些键,前三个与其对应的 Invoice 键具有相同的类型和值。

item_serial_id,
item_number_id,
item_date,
item_otherkey1

我使用 hibernate 的逆向工程创建了表,生成了 Invoice.java、InvoiceId.java、Item.java 和 ItemId.java,然后手动创建了 @OneToMany 和 @ManyToOne 注释。到目前为止的代码如下:

Invoice.java

@Entity
@Table(name = "INVOICE")
public class Invoice implements java.io.Serializable {

    private static final long serialVersionUID = -2073462863929322522L;

    @EmbeddedId
        @AttributeOverrides( {
            @AttributeOverride(name = "invoiceSerialId", column = @Column(name = "INVOICE_SERIAL_ID", nullable = false, length = 9)),
            @AttributeOverride(name = "invoiceNumberId", column = @Column(name = "INVOICE_NUMBER_ID", nullable = false, length = 9)),
            @AttributeOverride(name = "invoiceDate", column = @Column(name = "INVOICE_DATE", nullable = false, length = 7)),
            @AttributeOverride(name = "invoiceOtherkey1", column = @Column(name = "INVOICE_OTHERKEY1", nullable = false, length = 7)),
            @AttributeOverride(name = "invoiceOtherkey2", column = @Column(name = "INVOICE_OTHERKEY2", nullable = false, length = 7))
    })
    private InvoiceId id;

    @OneToMany(mappedBy = "invoice")
    private Set<Item> items;
}

Item.java

@Entity
@Table(name = "ITEM")
public class Item implements java.io.Serializable {
    private static final long serialVersionUID = -2840038244580784867L;

    @EmbeddedId
        @AttributeOverrides( {
            @AttributeOverride(name = "itemSerialId", column = @Column(name = "ITEM_SERIAL_ID", nullable = false, length = 9)),
            @AttributeOverride(name = "itemNumberId", column = @Column(name = "ITEM_NUMBER_ID", nullable = false, length = 9)),
            @AttributeOverride(name = "itemDate", column = @Column(name = "ITEM_DATE", nullable = false, length = 7)),
            @AttributeOverride(name = "itemOtherkey1", column = @Column(name = "ITEM_OTHERKEY1", nullable = false, length = 7))
    })
    private ItemId id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumns({
        @JoinColumn(name = "ITEM_SERIAL_ID", referencedColumnName = "INVOICE_SERIAL_ID"),
        @JoinColumn(name = "ITEM_NUMBER_ID", referencedColumnName = "INVOICE_NUMBER_ID"),
        @JoinColumn(name = "ITEM_DATE", referencedColumnName = "INVOICE_DATE")
    })
    private Invoice invoice;
}

我收到以下错误:

org.hibernate.AnnotationException: referencedColumnNames(INVOICE_SERIAL_ID, INVOICE_NUMBER_ID, INVOICE_DATE) of com.just.an.stupid.example.app.Item.invoice referencing com.just.an.stupid.example.app.Invoice not mapped to a single property

我也尝试过按照here 的步骤,并在 Invoice 上创建一个@joinTable,例如

@JoinTable(name = "INVOICE_ITEM",
    joinColumns = {
        @JoinColumn(name = "ITEM_SERIAL_ID", referencedColumnName = "INVOICE_SERIAL_ID"),
        @JoinColumn(name = "ITEM_NUMBER_ID", referencedColumnName = "INVOICE_NUMBER_ID"),
        @JoinColumn(name = "ITEM_DATE", referencedColumnName = "INVOICE_DATE")
    },
    inverseJoinColumns = {
        @JoinColumn(name = "INVOICE_SERIAL_ID", referencedColumnName = "ITEM_SERIAL_ID"),
        @JoinColumn(name = "INVOICE_NUMBER_ID", referencedColumnName = "ITEM_NUMBER_ID"),
        @JoinColumn(name = "INVOICE_DATE", referencedColumnName = "ITEM_DATE")
    }
)

但我得到了这个:

org.hibernate.AnnotationException: A Foreign key refering com.just.an.stupid.example.app.Invoice from com.just.an.stupid.example.app.Item has the wrong number of column. should be 5

如何使用此数据库创建关系?还是我应该放弃并手动选择项目?

【问题讨论】:

    标签: java hibernate annotations one-to-many


    【解决方案1】:

    由于时间限制,我无法等待正确的答案。

    我最终在示例中创建了一个连接这些表的视图,如下所示:

    create or replace table invoice_items as
    select distinct
      i.field_1,
      i.field_2,
      i.field_3,
      it.field_4
    from
      invoice i
    inner join
      item it
    on
      (i.invoice_serial_id = it.item_serial_id and
       i.invoice_number_id = it.item_number_id and
       i.invoice_date = it.item_date
      )
    

    然后我在 Hibernate 中映射视图。这对我有用。

    【讨论】:

      【解决方案2】:

      我认为你需要这样做。

      1. 您的发票表有一个带有对应@ID 属性的主键。

      2. invoice 表中的嵌入 ID 应仅包含 Item 表中的 3 个字段。

      3. 创建一个 ItemID 表,映射这三个字段,并在 Invoice 表中引用它。

      它看起来像这样......

      @Embeddable
      public class ItemId implements java.io.Serializable {
      
      private Long serialNo;
      private Long itemNumber;
          private Date itemDate
      

      ........ 具有相应的 setter 和 getter,并覆盖了 ItemId 类的 hashCode() 和 equals(Object o)。

      1. 在您的发票中引用此 ItemId(就像您使用 EmbeddedId 和属性覆盖所做的那样。

      目前,您在外键定义中嵌入了 5 个字段,但在 itemID 类中只强调了 3 个。您不需要连接表,您只需创建一个复合键类(包含 3 个字段)并将其嵌入到发票表中。

      【讨论】:

      • 您好,感谢您的回答!遗憾的是我不能这样做,因为这些表将用于其他关系。我想我不能以正确的方式做到这一点。