我有一种情况需要在ORM类层次结构中的对象上连接表,其中连接列不是基类的主键。以下是表格设计的示例:
CREATE TABLE APP.FOO
(
FOO_ID INTEGER NOT NULL,
TYPE_ID INTEGER NOT NULL,
PRIMARY KEY( FOO_ID )
)
CREATE TABLE APP.BAR
(
FOO_ID INTEGER NOT NULL,
BAR_ID INTEGER NOT NULL,
PRIMARY KEY( BAR_ID ),
CONSTRAINT bar_fk FOREIGN KEY( FOO_ID ) REFERENCES APP.FOO( FOO_ID )
)
CREATE TABLE APP.BAR_NAMES
(
BAR_ID INTEGER NOT NULL,
BAR_NAME VARCHAR(128) NOT NULL,
PRIMARY KEY( BAR_ID, BAR_NAME),
CONSTRAINT bar_names_fk FOREIGN KEY( BAR_ID ) REFERENCES APP.BAR( BAR_ID )
)
以下是映射(为了简洁而消除了吸气剂和灭火剂
@Entity
@Table(name = "FOO")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "TYPE_ID", discriminatorType = javax.persistence.DiscriminatorType.INTEGER)
public abstract class Foo {
@Id
@Column(name = "FOO_ID")
private Long fooId;
}
@Entity
@DiscriminatorValue("1")
@SecondaryTable(name = "BAR", pkJoinColumns = { @PrimaryKeyJoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID") })
public class Bar extends Foo{
@Column(table = "BAR", name = "BAR_ID")
Long barId;
}
如果BAR_NAMES
的联接列不是FOO_ID
,而是BAR_ID
?
@CollectionOfElements(fetch = FetchType.LAZY)
@Column(name = "BAR_NAME")
@JoinTable(name = "BAR_NAMES", joinColumns = @JoinColumn(table = "BAR", name = "BAR_ID", referencedColumnName="BAR_ID"))
List<String> names = new ArrayList<String>();
的映射?
我尝试了以下内容:
@JoinTable(name = "BAR_NAMES", joinColumns = @JoinColumn(name = "BAR_ID"))
这会失败,因为用于检索Bar对象的SQL尝试从FOO表中获取BAR_ID值。我也尝试用
替换JoinTable注释insert into FOO (FOO_ID, TYPE_ID) values (10, 1);
insert into BAR (FOO_ID, BAR_ID) values (10, 20);
insert into BAR_NAMES (BAR_ID, BAR_NAME) values (20, 'HELLO');
这不会产生SQL错误,但也不会检索任何数据,因为针对BAR_NAMES的查询使用FOO_ID作为连接值而不是BAR_ID。
出于测试目的,我使用以下命令填充了数据库
{{1}}
当获取ID 10的Foo对象(而不是包含1个名称的集合)时,许多似乎有效的解决方案将返回空集合
答案 0 :(得分:7)
我能够找到解决方案。如果你像这样映射Bar类
@Entity
@DiscriminatorValue("1")
@SecondaryTable(name = "BAR", pkJoinColumns = { @PrimaryKeyJoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID") })
public class Bar extends Foo {
@OneToOne
@JoinColumn(table = "BAR", name = "BAR_ID")
MiniBar miniBar;
}
并添加以下类
@Entity
@SqlResultSetMapping(name = "compositekey", entities = @EntityResult(entityClass = MiniBar.class, fields = { @FieldResult(name = "miniBar", column = "BAR_ID"), }))
@NamedNativeQuery(name = "compositekey", query = "select BAR_ID from BAR", resultSetMapping = "compositekey")
@Table(name = "BAR")
public class MiniBar {
@Id
@Column(name = "BAR_ID")
Long barId;
}
然后,您可以将任何类型的映射添加到MiniBar
类,就好像barId是主键一样,然后进一步使其在外部Bar
类中可用。
答案 1 :(得分:2)
不确定如何使用JPA / Annotations执行此操作,但使用Hibernate XML映射文件时,它将类似于:
<class name="Bar" table="BAR">
<id name="id" type="int">
<column name="BAR_ID"/>
<generator class="native"/>
</id>
<set name="barNames" table="BAR_NAMES">
<!-- Key in BAR_NAMES table to map to this class's key -->
<key column="BAR_ID"/>
<!-- The value in the BAR_NAMES table we want to populate this set with -->
<element type="string" column="BAR_NAME"/>
</set>
</class>
答案 2 :(得分:2)
你将无法做你想做的事。 @CollectionOfElements(以及@OneToMany,就此而言)始终通过所有者的实体主键进行映射。
映射Foo / Bar继承的方式也很奇怪 - 它们显然不在同一个表中;看起来使用JoinedSubclass似乎是一种更好的方法。请记住,仍然无法帮助您将bar_names
映射到bar_id
,因为主键值在层次结构中共享(即使列名可能与子类不同)。
一种可能的替代方法是在Foo和Bar之间使用@OneToOne映射而不是继承。这是您能够将bar_names
映射到bar_id
以及最适合您的表结构的映射的唯一方法(尽管可能不适用于您的域模型)。