使用带注释的复合主键进行元素映射

时间:2014-10-29 09:21:04

标签: java hibernate annotations mapping composite-key

我正在将一些带有NHibernate XML映射的C#转换为带有Annotations映射的Java,并且有一个我试过转换但没有成功的实体。我们有MAIN_TABLE,JOIN_TABLE和THIRD_TABLE。来自MAIN_TABLE的一条记录可以有很多来自THIRD_TABLE,它们的关系存储在JOIN_TABLE上,它只有三列:一个主键由MAIN_TABLE和THIRD_TABLE的键组成,第三行包含一些不相关的附加数据。这是最初的NHibernate XML映射:

<class name="MainTable" table="MAIN_TABLE" mutable="false">
<id name="Id" column="rvcplc" type="int" length="10">
  <generator class="assigned"/>
</id>
<bag name="JoinedRecords" table="JOIN_TABLE" order-by="main_table_id">
  <key column="main_table_id" not-null="true"/>
  <composite-element class="JoinTable">
    <parent name="parentRecord"/>
    <many-to-one name="ThirdTable" column="third_table_id"/>
  </composite-element>
</bag>

这是我代码的当前状态:

@Entity()
@Table(name="MAIN_TABLE")
@Immutable
public class MainTable extends BusinessEntity implements IMainTable
{
   @Id
   @Type(type = "LongToNull")
   @Column(name = "id")
   private long id; 

   @ElementCollection
   @CollectionTable(name="JOIN_TABLE", joinColumns={@JoinColumn(name="main_table_id")})
   private List<IJoinTable> joinedRecords = new ArrayList<IJoinTable>();
}

@Embeddable()
@Table(name = "JOIN_TABLE")
@Immutable
public class JoinTable implements Serializable, IJoinTable
{
    @SuppressWarnings("unused")
    @Id
    private JoinTablePK pkId;

    @Parent
    @Column(name = "main_table_id")
    private IMainTable parentRecord;

    @ManyToOne(targetEntity = ThirdTable.class)
    @JoinColumn(name = "third_table_id")
    private IThirdTable thirdTable;

    public static class JoinTablePK implements Serializable
    {
          // foo
    }
}

当我尝试对此关联进行单元测试时,只需加载一个已知的MainTable实体并断言它有三个子集,其集合中的JoinTable对象失败,并显示“无法初始化集合”错误。这是我最好的代码,因为其他尝试使我的所有测试失败,因为当你的映射错误时会发生。

2 个答案:

答案 0 :(得分:2)

我会尝试这样做(我将在代码中使用简单的类,希望超类和接口与问题无关)。

有两种选择。第一个,更容易的是,JOIN_TABLE中的“无关的附加数据”与模型真的无关。

@Entity
@Table(name="MAIN_TABLE")
public class MainTable {
    @Id
    @Type(type = "LongToNull")
    private long id; 

    @ManyToMany
    @JoinTable(name = "JOIN_TABLE", joinColumns = @JoinColumn(name = "JOIN_TABLE_ID"),
      inverseJoinColumns = @JoinColumn(name = "THIRD_TABLE_ID")
    private List<ThirdTable> thirdTableRecords = new ArrayList<ThirdTable>();
}

@Entity
@Table(name="THIRD_TABLE")
public class ThirdTable {
    @Id
    @Type(type = "LongToNull")
    private long id; 

    @ManyToMany(mappedBy = "thirdTableRecords")
    private List<MainTable> mainTableRecords = new ArrayList<MainTable>();

    ...
}

如果仍然需要对“不相关的附加数据”进行建模,那么实体将看起来像这样

@Entity
@Table(name="MAIN_TABLE")
public class MainTable {
    @Id
    @Type(type = "LongToNull")
    private long id; 

    @OneToMany(mappedBy = "mainTable")
    private List<JoinTable> joinTableRecords = new ArrayList<JoinTable>();
}

@Entity
@Table(name="THIRD_TABLE")
public class ThirdTable {
    @Id
    @Type(type = "LongToNull")
    private long id; 

    @OneToMany(mappedBy = "thirdTable")
    private List<JoinTable> joinTableRecords = new ArrayList<joinTable>();

    ...
}

@Entity
@Table(name="JOIN_TABLE")
public class JoinTable {
    @EmbeddedId
    private JoinTablePK pkId;

    private String irrelevantData;

    @ManyToOne
    @JoinColumn(name = "MAIN_TABLE_ID", insertable = false, updateable = false)
    private MainTable mainTable;

    @ManyToOne
    @JoinColumn(name = "THIRD_TABLE_ID", insertable = false, updateable = false)
    private ThirdTable thirdTable;

    ...
}

@Embeddable
public class JoinTablePK {
    @Column(name = "MAIN_TABLE_ID")
    private Long mainTableId;

    @Column(name = "THIRD_TABLE_ID")
    private Long thirdTableId;

    // getters and setters
}

insertable = falseupdateable = false是因为您将两个字段映射到同一列,并且一列只能有一个可写字段。

我没有对代码进行测试,所以如果它能够正常工作,我会感到惊讶,但希望它能为您提供一些指导方针,并且您至少可以向解决方案迈进几步。

答案 1 :(得分:0)

除了Predrag Maric建议的另一种方法之外,还有另外一种方法。这里详细说明:http://www.mkyong.com/hibernate/hibernate-many-to-many-example-join-table-extra-column-annotation/

我已添加此回复,以防链接对其他人有用。这是我的完整代码:

@Entity()
@Table(name="MAIN_TABLE")
@Immutable
public class MainTable extends BusinessEntity implements IMainTable
{
    @Id
    @Type(type = "LongToNull")
    @Column(name = "MAIN_TABLE_ID")
    private long id; 

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.parentRecord", targetEntity =     JoinTable.class)
    private List<IJoinTable> joinedRecords = new ArrayList<IJoinTable>();
}

@Entity
@Table(name = "JOIN_TABLE")
@AssociationOverrides({
     @AssociationOverride(name="pk.parentMainTable", joinColumns =   @JoinColumn(name="MAIN_TABLE_ID")),
     @AssociationOverride(name="pk.parentThirdTable", joinColumns = @JoinColumn(name="THIRD_TABLE_ID"))
      })
public class JoinTable implements Serializable, IJoinTable
{
    private JoinTablePK pkId;
    private BigDecimal irrelevantData;

    // Ctor
    public JoinTable(MainTable mainTable, ThirdTable thirdTable)
    {
        pk = new JoinTablePK(mainTable, thirdTable);
    }

    @EmbeddedId
    public JoinTablePK getPk()
    {
        return pk;
    }

    // here goes the setter, no annotations

    @Transient
    public IMainTable getMainTable()
    {
        return getPk().getMainTable();
    }

    // here goes the setter, no annotations

    @Transient
    public IThirdTable getThirdTable()
    {
        return getPk().getThirdTable();
    }

    // here goes the setter, no annotations

    @Column(name = "JOIN_TABLE_IRRELEVANT_DATA")
    public BigDecimal getIrrelevantData()
    {
        return irrelevantData;
    }

    // here goes the setter, no annotations

    /**
    * PRIMARY KEY CLASS
    */
    @Embeddable
    public static class JoinTablePK implements Serializable
    {
      private MainTable parentMainTable;

      @ManyToOne
      public MainTable getParentMainTable()
      {
        return parentMainTable;
      }

      // here goes the setter, no annotations

      private ThirdTable parentThirdTable;

      @ManyToOne
      public ThirdTable getParentThirdTable()
      {
        return parentThirdTable();
      }

      // here goes the setter, no annotations

      // empty ctor
      public JoinTablePK() {}

      // ctor with parameters
      public JoinTablePK(MainTable parentMainTable, ThirdTable parentThirdTable)
      {
        this.parentMainTable = parentMainTable;
        this.parentThirdTable = parentThirdTable;
      }

      // rest of the class, implement equals() and hashCode()
    }
}