Java 如何联接辅助表中非主键列上的表?

Java 如何联接辅助表中非主键列上的表?,java,hibernate,orm,Java,Hibernate,Orm,在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, PRIMA

在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 )
)
这里是为了简洁而消除的映射(getter和setter)

@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;
 }    
如果其联接列不是
FOO\u ID
,而是
BAR\u ID
,如何添加
BAR\u名称的映射

我尝试了以下方法:

@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>();
这不会产生SQL错误,但也不会检索任何数据,因为对BAR_名称的查询使用FOO_ID作为连接值,而不是BAR_ID

出于测试目的,我用以下命令填充了DB

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');

在获取ID为10的Foo对象时,许多似乎有效的解决方案将返回一个空集合(与包含一个名称的集合相反)

不确定如何使用JPA/注释执行此操作,但对于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>

您将无法做您想做的事情。@CollectionFements(以及@OneToMany)始终通过所有者的实体主键映射

映射Foo/Bar继承的方式也很奇怪-它们显然不在同一个表中;似乎使用将是更好的方法。请记住,这仍然无法帮助您映射
Bar\u名称
Bar\u id
,因为主键值在层次结构中共享(即使子类的列名可能不同)


一种可能的替代方法是使用Foo和Bar之间的映射,而不是使用继承。这是您能够将
Bar\u名称
映射到
Bar\u id
的唯一方法,也是最适合您的表结构的映射(尽管可能不适用于您的域模型).

我找到了一个解决方案。如果你像这样映射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
类中进一步使其可用。

我相信这相当于@JoinTable(name=“Bar\u name”,joinColumns=@JoinColumn(name=“Bar\u ID”)),它不起作用,因为它使用FOO_ID的值来联接BAR_NAMES表。抱歉,我误读了你的话。我无法将BAR_ID映射为BAR类的主键,因为它是FOO对象的子对象,使用带有辅助表的单表继承或使用联接子类继承。在实时环境中,FOO是具有不同类型ID的许多子类的基类。我们使用了连接子类模型和具有辅助表的单类模型,发现两者都有各自的优点和缺点。更改层次结构不是一个选项。老实说,一旦您增加了“每个层次结构表”使用辅助表的方法与“每类表”方法相比,您失去了它的唯一优势(性能稍快),但这与您的问题无关。正如我所说的,如果不更改层次结构映射或数据库结构,您将无法以您想要的方式映射集合(例如,放弃
bar\u id
并在整个层次结构中使用
foo\u id
作为主键)。referencedColumnName专门用于解决此类问题。如果我创建了一个与FOO表的TYPE_ID连接的表TYPE_NAMES,它将按预期工作。如果不允许在辅助表中的列上进行映射,则可能是一个错误或设计限制。我正在尝试确定是哪个。
referencedColumnName
addr这是一个完全不同的问题-它允许您指定“to-one”关联另一端的列的名称(可以是实际的@OneToOne映射、@SecondaryTable映射或通过复合键的ManyToOne).嗯,除了我说的,如果我在主表中使用一列,它就可以工作。
barId
仍然用
@Id
标记,有什么区别?
@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;
}