Hibernate / SQL:仅在存在一对多关系时选择记录,返回太多

时间:2012-12-13 09:35:06

标签: sql hibernate join

在我的应用程序中,我有2个表,结算结果和结算状态 结算结果包含一些基本数据,如名称,类型等。 结算国家是具有结算结果的关系的“多个”方面,包括结算结果PK和作为PK的多对一ID的状态和日期。示例数据:

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选择Settlement Result中的行,例如Status 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(已创建,已确认,但未接受=已拒绝)。

问题是,我只想选择那些具有特定状态的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等于创建的结果)。

是否可以仅选择那些仅具有某种状态的结算结果,例如,如果我们想要创建[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;
}

1 个答案:

答案 0 :(得分:1)

这里有两个选项:

如果您看到自己的查询,则会获取SettlementResult对象

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

您的查询是完美的但是一旦您拥有父对象SettlementResult并且您访问OneToMany集合settlementStates,hibernate将在SettlementResult P.K的基础上加载所有这些。 (这也是你所观察到的)。

所以选项-1 通过Child to Parent 这意味着从查询中返回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对您有用,但这个解决方案对您来说可能很奇怪。因此,解决此问题的最佳方法是您可以划分结算状态对象(如问题中所述)

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类中定义的延迟加载来初始化这些集合。

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

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