Hibernate / JPA单向OneToMany,在源表中具有常量值的连接条件

时间:2017-08-02 21:52:19

标签: java hibernate jpa join hibernate-annotations

我想使用Hibernate注释来表示使用连接的单向一对多关系。我想在连接上添加一个条件,因此只有在 source 表中的列(" one")等于常量值时才会发生这种情况。例如。

SELECT *
FROM buildings b
LEFT JOIN building_floors bf on bf.building_id = b.id AND b.type = 'OFFICE'

我想代表该查询的b.type = 'OFFICE'部分。

我的问题与此问题非常相似,只是我在源表上有条件。 JPA/Hibernate Join On Constant Value

Java实体如下所示:

@Entity
@Table(name = "buildings")
public class Building {

    @Id
    @Column(name = "id")
    private int id;

    @Column(name = "type")
    private String type;

    @OneToMany(mappedBy = "buildingId",
            fetch = FetchType.EAGER,
            cascade = {CascadeType.ALL},
            orphanRemoval = true)
    @Fetch(FetchMode.JOIN)
    // buildings.type = 'OFFICE'   ????
    private Set<BuildingFloors> buildingFloors;

    // getters/setters
}

@Entity
@Table(name = "building_floors")
public class BuildingFloor {

    @Id
    @Column(name = "building_id")
    private int buildingId;

    @Id
    @Column(name = "floor_id")
    private int floorId;

    @Column(name = "description")
    private String description;

    // getters/setters
}

我已尝试过一些我有占位符评论的事情:

@Where annotation

这不起作用,因为它适用于目标实体。

@JoinColumns注释

@JoinColumns({
        @JoinColumn(name = "building_id", referencedColumnName = "id"),
        @JoinColumn(name = "'OFFICE'", referencedColumnName = "type")
})

这不起作用,因为我收到以下错误(为简洁起见而简化):Syntax error in SQL statement "SELECT * FROM buildings b JOIN building_floors bf on bf.building_id = b.id AND bf.'OFFICE' = b.type"

不同的@JoinColumns注释

@JoinColumns({
        @JoinColumn(name = "building_id", referencedColumnName = "id"),
        @JoinColumn(name = "buildings.type", referencedColumnName = "'OFFICE'")
})

这不起作用,因为在使用单向OneToMany关系时,referencedColumnName来自源表。所以我收到错误:org.hibernate.MappingException: Unable to find column with logical name: 'OFFICE' in buildings

提前致谢!

4 个答案:

答案 0 :(得分:9)

为什么不使用inheritance ? (我在JPA中使用它,我从不直接使用hibernate)

@Entity
@Inheritance
@Table(name = "buildings")
@DiscriminatorColumn(name="type")
public class Building {

    @Id
    @Column(name = "id")
    private int id;

    @Column(name = "type")
    private String type;
}

并且:

@Entity
@DiscriminatorValue("OFFICE")
public class Office extends Building {
    @OneToMany(mappedBy = "buildingId",
        fetch = FetchType.EAGER,
        cascade = {CascadeType.ALL},
        orphanRemoval = true)
    private Set<BuildingFloors> buildingFloors;
}

答案 1 :(得分:4)

使用以下选择创建数据库视图:

SELECT bf.* FROM building_floors bf JOIN buildings b on bf.building_id = b.id AND b.type = 'OFFICE'

将其作为普通实体映射到类OfficeBuildingFloors,然后在@OneToMany类中使用Building

当然,您无法修改此类集合,并且可以避免在@Immutable上使用OfficeBuildingFloors时出现的任何异常。

答案 2 :(得分:1)

在我看来,您应该创建一个特定的查询来实现您的目标,而不是使用常量参数来设置特定的注释。我没有看到你提到除了Hibernate之外的其他框架所以我会举一些Hibernate的例子。在Building课程中,您的unidirectional映射如下所示:

@OneToMany(fetch = FetchType.Lazy, cascade = {CascadeType.ALL}, orphanRemoval = true)
@JoinTable(name = "building_floors", joinColumns = @JoinColumn(name = "id"), inverseJoinColumns = @JoinColumn(name = "building_id")
private Set<BuildingFloor> buildingFloors;

然后,您可以使用TypedQuery这样的方式获取数据。

TypedQuery<Customer> query = getEntityManager().createNamedQuery("select b from building b inner join fetch b.buildingFloors where b.type = 'OFFICE'", Building.class);
List<Building> result = query.getResultList();

我的解决方案不是特定于Hibernate的,实际上你可以用简单的JPA来执行它。希望这可以帮助您实现目标。

答案 3 :(得分:1)

如果您想要过滤器源表,可以使用@Loader注释

@Entity
@Table(name = "buildings")
@Loader(namedQuery = "building")
@NamedNativeQuery(name="building", 
    query="SELECT * FROM buildings b"
        + " LEFT JOIN building_floors bf on bf.building_id = b.id"
        + " WHERE b.type = 'OFFICE' AND b.id = ?",
    resultClass = Building.class)
class Building

如果可以在DB内部使用,那么使用DB中的视图方法会更好更清晰。否则,将Building重命名为明确表示过滤的内容。

另一种方法:@Filter,@ FilterDef。