【问题标题】:Hibernate/SQL: Selecting records only if exist in one-to-many relationship, returns too manyHibernate/SQL:仅当存在一对多关系时才选择记录,返回太多
【发布时间】:2012-12-01 04:03:08
【问题描述】:

在我的应用程序中,我有 2 个表格,结算结果和结算状态 结算结果包含一些基本数据,如名称、类型等。 SettlementState 是与 Settlement Result 关系的“多”方,由 Settlement Result PK 和 Status as Many-To-One ID as PK 和日期组成。示例数据:

Settlement Result
------------------------------
ID| Name      | Sth
------------------------------
1 | Some Name | Something more
2 | Name2     | more2

Settlement State
----------
Result's ID | StatusId     | Date 
------------------------------
1           | 1            | some date
1           | 2            | date
1           | 3            | date
2           | 1            | date

现在我希望使用 HQL/Plain SQL 从结算结果中选择具有例如状态 ID == 3 的行,但不是任何具有更高 ID 的行。

可能的状态很少:

Status ID | Desc
-----------------
1 | Created
2 | Confirmed
3 | Accepted
4 | Rejected

当我们创建 SettlementResult 时,总会有一些“工作流程”。首先,Result 的状态为 Created (ID 1)。然后它可以被拒绝(ID 4)或确认(ID 2)。然后我们可以再次接受或拒绝。

因此,一个 SettlementResult 可以具有状态 ID 为 1、2、4 的 SettlementStates(已创建、已确认,但未接受 = 已拒绝)。

问题是,我只想选择那些具有特定状态(例如 Created)的 SettlementResults。”

使用这样的 HQL 查询:

Query query = session.createSQLQuery( "select distinct s from SettlementResult s join s.settlementStates states where states.settlementStatePK.status.statusId == 1" );

它返回每个结算结果,即使是状态为 1、2、3 的结果(因为集合包含 ID 等于已创建的那个)。

是否可以仅选择仅具有特定状态的那些结算结果,例如,如果我们想要 Created[ID 1],我们将获得所有结算状态状态为 1,没有 2,3 或 4 状态的结果。当我们选择状态 id 为 3 的那些时,如果它具有状态 id = 1,2,3 但不是 4 的结算状态,我们可以接受。某种max[status.id]

@Entity
@Table(name = "SETTLEMENT_STATE")
public class SettlementState
{
    @EmbeddedId
    private SettlementStatePK settlementStatePK;

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

    @Column(name = "STATUS_DTTM")
    private Date statusDate;
}

@Embeddable
public class SettlementStatePK implements Serializable
{
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "STATUS_ID")
    private Status status;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "SETTLEMENT_RESULT_ID")
    private SettlementResult settlementResult;
}

@Entity
@Table(name = "SETTLEMENT_RESULT")
public class SettlementResult implements Serializable
{
    @Id
    @Column(name = "SETTLEMENT_RESULT_ID")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "STATUS_ID")
    private Status status;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "MODEL_GROUP_ID")
    private SettlementModelGroup settlementModelGroup;

    @Column(name = "\"MODE\"")
    private Integer mode;

    @Column(name = "CREATED_DTTM")
    private Date createdDate;

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

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "settlementStatePK.settlementResult")
    private List<SettlementState> settlementStates;
}

【问题讨论】:

  • 你能发布映射吗?
  • 编辑完成,不是完整的映射,但有相关字段
  • 同时发布您用来获取数据的查询。
  • select distinct a -> a 没有定义。错字? hibernate 不抱怨这个吗?
  • 扩展了我的描述。一点点,这样你就可以清楚地了解我想要实现的目标

标签: sql hibernate join


【解决方案1】:

你有两个选择:

如果您看到您的查询,您正在获取 SettlementResult 对象

Query query = session.createSQLQuery( "select distinct s from SettlementResult s join s.settlementStates states where states.settlementStatePK.status.statusId == 1" );

您的查询是完美的,但是一旦您拥有父对象 SettlementResult 并访问 OneToMany 集合结算状态,hibernate 将根据 SettlementResult P.K. 加载所有这些对象。 (这也是你观察到的)。

所以选项-1从孩子到父母 这意味着从您的查询中返回 SettlementState 对象:

Query query = session.createSQLQuery( "select distinct states from SettlementResult s join s.settlementStates states where states.settlementStatePK.status.statusId == 1" );

然后您可以从状态对象访问 SettlementResult,因为您已经在 SettlementState 类中为 SettlementResult 定义了 ManyToOne。在这种情况下,您肯定会拥有您正在寻找的那些 SettlementResult 对象。

选项 2:划分您的 SettlementState 对象 选项 1 对您有用,但此解决方案对您来说可能看起来很奇怪。所以解决这个问题的最好方法是你可以划分 Settlement State 对象(如你的问题中所述)

1. RejectedSettlementState
2. NonRejectedSettlementState

这两个类将扩展一个基本抽象类 (SettlementState)。您可以在状态 ID 上定义鉴别器公式。一旦有了这些类,就可以将这些子类关联到 SettlementResult。这是您需要的课程 (sudo)

@DiscriminatorForumla("case when status_id == 4 then "REJECTEDSTATE" else "NONREJECTEDSTATE" end")
public abstract class SettlementState{
...
// Define all of your common fields in this parent class
}

@DiscriminatorValue("REJECTEDSTATE")
public class RejectedSettlementState extends SettlementState{
...
// define only fields specific to this rejected state
}


@DiscriminatorValue("NOTREJECTEDSTATE")
public class NonRejectedSettlementState extends SettlementState{
...
// define only fields specific to this non-rejected state
}

现在是 SettlementResult 类

public class SettlementResult{

@OneToMany(fetch = FetchType.LAZY, mappedBy = "settlementStatePK.settlementResult")
private List<RejectedSettlementState> rejectedSettlementStates;

@OneToMany(fetch = FetchType.LAZY, mappedBy = "settlementStatePK.settlementResult")
private List<NonRejectedSettlementState> nonRejectedSettlementStates;

}

所以一旦你拥有了所有这些对象。然后你不需要查询状态。您只需加载父对象 SettlementResult,然后访问您的拒绝或非拒绝结算状态。 Hibernate 将使用公式条件使用在 SettlementResult 类中定义的延迟加载来初始化这些集合。

注意 两种解决方案对我来说都是可以接受的,这取决于您希望系统中的哪一个存在。第二个选项为您提供更多未来优势。

更多信息:请询问!我已尽力让您了解这个想法:)。祝你好运!

【讨论】:

  • 谢谢!在找到您的解决方案之前,我已经设法使用 1 个大 sql 查询(只有很少的子选择)来做到这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 2017-06-02
  • 2011-01-07
  • 2018-09-27
  • 2012-12-15
相关资源
最近更新 更多