【问题标题】:Hibernate/JPA unidirectional OneToMany with join condition on constant value in source tableHibernate/JPA 单向 OneToMany 与源表中常量值的连接条件
【发布时间】:2017-08-02 21:52:19
【问题描述】:

我想使用 Hibernate 注释来表示使用连接的单向一对多关系。我想在连接上添加一个条件,所以它只发生在 source 表中的一列(“一”)等于一个常数值时。例如。

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 注释

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

@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

提前致谢!

【问题讨论】:

  • 我不明白。既然您使用的是JOIN,而不是LEFT JOIN,为什么不直接使用where 子句呢? SELECT * FROM buildings b JOIN building_floors bf on bf.building_id = b.id WHERE b.type = 'OFFICE'。你期待什么结果?
  • 对不起!请原谅我的错误。我的意思是我的示例查询是LEFT JOIN。我将编辑我的问题。

标签: java hibernate jpa join hibernate-annotations


【解决方案1】:

为什么不使用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;
}

【讨论】:

  • 感谢您的指点。我现在正在追求这个选项,但在单元测试中遇到了错误。堆栈跟踪中的根本原因是:Caused by: org.h2.jdbc.JdbcSQLException: Invalid value "19" for parameter "parameterIndex" [90008-176]
  • 您的查询似乎有错误,可能参数类型无效。我相信你会解决这个问题。
  • 我相信这个错误正在发生,因为type 也是我的Building 实体上的一个字段。我不得不向 Column 注释添加其他参数,并且我停止收到错误。 @Column(name = "type", insertable = false, updatable = false)
  • 感谢您的帮助。我采用了这种方法,并且效果很好。
【解决方案2】:

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

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

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

当然,您将无法修改此类集合,为了避免任何异常,您可以在 OfficeBuildingFloors 上使用 @Immutable

【讨论】:

  • 我没想到!我会查明我是否能够/允许在我的数据库中创建此视图。
【解决方案3】:

在我看来,您应该创建一个特定的查询来实现您的目标,而不是使用常量参数放置特定的注释。我没有看到你提到除了 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 来执行此操作。希望这可以帮助您实现目标。

【讨论】:

  • 感谢您的建议。我遇到的问题是,我必须在所有返回 Building 对象的各种 DAO 方法中使用这个 NamedQuery。在我的情况下,这将是 20-30 个不同的地方。我还有其他实体,其中包含对 Building 的引用(例如,Street 实体包含一组 Buildings。)然后我是否需要创建一个不同的 NamedQuery 以返回 Street 以便它我想要的 JOIN 吗?如果我错了,请纠正我!
【解决方案4】:

如果你想要过滤源表,你可以使用@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。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-06
    • 1970-01-01
    • 1970-01-01
    • 2017-03-07
    • 2018-03-19
    相关资源
    最近更新 更多