本文将介绍java–Hibernatehqlinner加入eagerfetchononetomany,获取冗余的父对象的详细情况,。我们将通过案例分析、数据研究等多种方式,帮助您更全面地了解这个主题,
本文将介绍java – Hibernate hql inner加入eager fetch on one to many,获取冗余的父对象的详细情况,。我们将通过案例分析、数据研究等多种方式,帮助您更全面地了解这个主题,同时也将涉及一些关于@OneToMany(fetch = FetchType.EAGER), FetchType.EAGER 急加载的导致查询重复明细数据的BUG、c# – 使用nhibernate Criteria API进行Eager Fetching、Hibernate / JPA ManyToOne与OneToMany、Hibernate 3.6.10 Review @OneToMany的知识。
本文目录一览:- java – Hibernate hql inner加入eager fetch on one to many,获取冗余的父对象
- @OneToMany(fetch = FetchType.EAGER), FetchType.EAGER 急加载的导致查询重复明细数据的BUG
- c# – 使用nhibernate Criteria API进行Eager Fetching
- Hibernate / JPA ManyToOne与OneToMany
- Hibernate 3.6.10 Review @OneToMany
java – Hibernate hql inner加入eager fetch on one to many,获取冗余的父对象
我有如下实体
类ProgressNote
@Entity
public class ProgressNote implements Serializable{
@Id
private NotesKey notesKey = new NotesKey();
private SetetoMany(fetch=FetchType.LAZY)
@Access(Accesstype.PROPERTY)
@JoinColumns({
@JoinColumn(name="noteNumber",referencedColumnName="noteNumber"),@JoinColumn(name="ddate",referencedColumnName="ddate"),@JoinColumn(name="patient_id",referencedColumnName="patient_id")
})
public Set
类NotesKey
@Embeddable
public class NotesKey implements Serializable{
private Byte noteNumber;
@Temporal(javax.persistence.TemporalType.DATE)
@Column(name="ddate")
private Date noteDate;
private Patient patient;
public Byte getNoteNumber() {
return noteNumber;
}
public void setNoteNumber(Byte noteNumber) {
this.noteNumber = noteNumber;
}
public Date getNoteDate() {
return noteDate;
}
public void setNoteDate(Date noteDate) {
this.noteDate = noteDate;
}
@ManyToOne(fetch=FetchType.LAZY)
@Access(Accesstype.PROPERTY)
public Patient getPatient() {
return patient;
}
public void setPatient(Patient patient) {
this.patient = patient;
}
@Override
public int hashCode() {
........
}
@Override
public boolean equals(Object obj) {
........
}
}
类PatientObjective
@Entity
public class PatientObjective implements Serializable{
@Id
private PatientObjectiveKey patientObjectiveKey;
public PatientObjectiveKey getPatientObjectiveKey() {
return patientObjectiveKey;
}
public void setPatientObjectiveKey(PatientObjectiveKey patientObjectiveKey) {
this.patientObjectiveKey = patientObjectiveKey;
}
}
类PatientObjectiveKey
@Embeddable
public class PatientObjectiveKey implements Serializable{
private Objective objective;
private Byte noteNumber;
@Temporal(javax.persistence.TemporalType.DATE)
@Column(name="ddate")
private Date noteDate;
private Patient patient;
@ManyToOne(fetch=FetchType.LAZY)
@Access(Accesstype.PROPERTY)
public Objective getobjective() {
return objective;
}
public void setobjective(Objective objective) {
this.objective = objective;
}
public Byte getNoteNumber() {
return noteNumber;
}
public void setNoteNumber(Byte noteNumber) {
this.noteNumber = noteNumber;
}
public Date getNoteDate() {
return noteDate;
}
public void setNoteDate(Date noteDate) {
this.noteDate = noteDate;
}
@ManyToOne(fetch=FetchType.LAZY)
@Access(Accesstype.PROPERTY)
public Patient getPatient() {
return patient;
}
public void setPatient(Patient patient) {
this.patient = patient;
}
@Override
public int hashCode() {
.......
}
@Override
public boolean equals(Object obj) {
........
}
}
我使用这个命名查询后
@NamedQuery(name = “findAllProgressNoteWithObjective”,query = “from
ProgressNote p inner join fetch p.patientObjectives as o where
p.notesKey.patient.id = :patientId)”) using spring hibernate template findByNamedQueryAndNamedParam
在表I中有2个ProgressNotes,其中一个具有单个目标,另一个具有17个目标.我在Java中得到如下结果.
查询生成为
select progressno0_.ddate as ddate46_0_,progressno0_.noteNumber as noteNumber46_0_,progressno0_.patient_id as patient17_46_0_,patientobj1_.ddate as ddate36_1_,patientobj1_.noteNumber as noteNumber36_1_,patientobj1_.objective_id as objective5_36_1_,patientobj1_.patient_id as patient6_36_1_,progressno0_.assessment as assessment46_0_,progressno0_.bloodPressure1 as bloodPre4_46_0_,progressno0_.bloodPressure2 as bloodPre5_46_0_,progressno0_.creationDate as creation6_46_0_,progressno0_.height as height46_0_,progressno0_.lastUpdatedDate as lastUpda8_46_0_,progressno0_.plans as plans46_0_,progressno0_.status as status46_0_,progressno0_.subject as subject46_0_,progressno0_.temprature as temprature46_0_,progressno0_.tempratureUnit as temprat13_46_0_,progressno0_.lastid as lastid46_0_,progressno0_.waist as waist46_0_,progressno0_.weight as weight46_0_,progressno0_.weightUnit as weightUnit46_0_,patientobj1_.remark as remark36_1_,patientobj1_.value as value36_1_,patientobj1_.ddate as ddate46_0__,patientobj1_.noteNumber as noteNumber46_0__,patientobj1_.patient_id as patient6_46_0__,patientobj1_.ddate as ddate0__,patientobj1_.noteNumber as noteNumber0__,patientobj1_.objective_id as objective5_0__,patientobj1_.patient_id as patient6_0__ from pnheader progressno0_ inner join pnobjremark patientobj1_ on progressno0_.ddate=patientobj1_.ddate and progressno0_.noteNumber=patientobj1_.noteNumber and progressno0_.patient_id=patientobj1_.patient_id where progressno0_.patient_id=?
题
问题我看到为什么我得到ProgressNote的多个单个实例(id = 152).有没有办法可以避免这种重复?
如果有人解释或指出我正确的方向,我将非常感激.
使用Hibernate 3.6,MysqL 5.6.10
最佳答案
使用不同:
select distinct p from ProgressNote p
inner join fetch p.patientObjectives as o
where p.notesKey.patient.id = :patientId)
@OneToMany(fetch = FetchType.EAGER), FetchType.EAGER 急加载的导致查询重复明细数据的BUG
项目使用spring data jpa ,
在配置了 多对1 和 1对 多的 表 对象关联关系之后,
同事 发现查询出来的 多的一方 ,有两个数据,而且他们的 数据id 都是一样的。
开始 他们以为是 阿里的json 序列化的时候,因为 有重复引用,循环引用问题,导致了 这个 BUG。
但是 他 设置了 fastjson 禁止 重复引用,循环引用,都不能解决。
刚好我 注意到了,就去 了解一下这个BUG。 开始也以为是 阿里fastjson 的BUG,
但是在我 debug 查看数据的时候,发现 明细表 就是有两条一样的数据的。
那就是 他们配置 关系的时候配置错了。
关联对象代码:
一方:
@OneToMany(fetch = FetchType.EAGER, mappedBy = "trade")
private List<TcOrder> orderList = new ArrayList<>();
多方:
/**
* 父订单ID
*/
@Column(name = "trade_id", nullable = true, length = 19)
private Long tradeId;
@ManyToOne()
@JoinColumn(name = "trade_id", unique = true, insertable = false, updatable = false)
@JSONField(serialize = false)
private TcTrade trade;
这配置 查询的时候的SQL
select
tctrade0_.id as id1_158_0_,
tctrade0_.account_status as account_2_158_0_,
tctrade0_.addres_id as addres_i3_158_0_,
tctrade0_.biz_type as biz_type4_158_0_,
tctrade0_.buyer_id as buyer_id5_158_0_,
tctrade0_.buyer_name as buyer_na6_158_0_,
tctrade0_.buyer_org_id as buyer_or7_158_0_,
tctrade0_.buyer_type as buyer_ty8_158_0_,
tctrade0_.cancel_reason as cancel_r9_158_0_,
tctrade0_.cancel_time as cancel_10_158_0_,
tctrade0_.cancel_type as cancel_11_158_0_,
tctrade0_.channel as channel12_158_0_,
tctrade0_.create_time as create_13_158_0_,
tctrade0_.customer_id as custome14_158_0_,
tctrade0_.customer_name as custome15_158_0_,
tctrade0_.customer_nick_name as custome16_158_0_,
tctrade0_.dealer_id as dealer_17_158_0_,
tctrade0_.dealer_name as dealer_18_158_0_,
tctrade0_.delivery_amount as deliver19_158_0_,
tctrade0_.delivery_status as deliver20_158_0_,
tctrade0_.delivery_way as deliver21_158_0_,
tctrade0_.discount_amount as discoun22_158_0_,
tctrade0_.fans_id as fans_id23_158_0_,
tctrade0_.fee_item_amount as fee_ite24_158_0_,
tctrade0_.follower_id as followe25_158_0_,
tctrade0_.follower_org_id as followe26_158_0_,
tctrade0_.goods_amount as goods_a27_158_0_,
tctrade0_.have_invoice as have_in28_158_0_,
tctrade0_.hedge_type as hedge_t29_158_0_,
tctrade0_.income_amount as income_30_158_0_,
tctrade0_.invoice_id as invoice31_158_0_,
tctrade0_.invoice_no as invoice32_158_0_,
tctrade0_.min_send_amount as min_sen33_158_0_,
tctrade0_.modify_time as modify_34_158_0_,
tctrade0_.order_type as order_t35_158_0_,
tctrade0_.org_id as org_id36_158_0_,
tctrade0_.pay_amount as pay_amo37_158_0_,
tctrade0_.pay_status as pay_sta38_158_0_,
tctrade0_.pay_time as pay_tim39_158_0_,
tctrade0_.payment_id as payment40_158_0_,
tctrade0_.payment_no as payment41_158_0_,
tctrade0_.promoter_id as promote42_158_0_,
tctrade0_.promoter_name as promote43_158_0_,
tctrade0_.promoter_org_id as promote44_158_0_,
tctrade0_.promoter_type as promote45_158_0_,
tctrade0_.refunded_amount as refunde46_158_0_,
tctrade0_.remark as remark47_158_0_,
tctrade0_.remind_delivery as remind_48_158_0_,
tctrade0_.removed as removed49_158_0_,
tctrade0_.require_deliver_date as require50_158_0_,
tctrade0_.seller_id as seller_51_158_0_,
tctrade0_.seller_name as seller_52_158_0_,
tctrade0_.seller_org_id as seller_53_158_0_,
tctrade0_.seller_type as seller_54_158_0_,
tctrade0_.source_id as source_55_158_0_,
tctrade0_.source_no as source_56_158_0_,
tctrade0_.staff_id as staff_i57_158_0_,
tctrade0_.staff_name as staff_n58_158_0_,
tctrade0_.store_id as store_i59_158_0_,
tctrade0_.store_name as store_n60_158_0_,
tctrade0_.total_amount as total_a61_158_0_,
tctrade0_.trade_img as trade_i62_158_0_,
tctrade0_.trade_no as trade_n63_158_0_,
tctrade0_.trade_status as trade_s64_158_0_,
tctrade0_.uuid as uuid65_158_0_,
orderlist1_.trade_id as trade_i29_146_1_,
orderlist1_.id as id1_146_1_,
orderlist1_.id as id1_146_2_,
orderlist1_.brand_id as brand_id2_146_2_,
orderlist1_.brand_name as brand_na3_146_2_,
orderlist1_.create_time as create_t4_146_2_,
orderlist1_.customer_id as customer5_146_2_,
orderlist1_.customer_name as customer6_146_2_,
orderlist1_.customer_nick_name as customer7_146_2_,
orderlist1_.dealer_id as dealer_i8_146_2_,
orderlist1_.dealer_name as dealer_n9_146_2_,
orderlist1_.delivery_amount as deliver10_146_2_,
orderlist1_.discount_amount as discoun11_146_2_,
orderlist1_.express_code as express12_146_2_,
orderlist1_.express_name as express13_146_2_,
orderlist1_.express_no as express14_146_2_,
orderlist1_.fans_id as fans_id15_146_2_,
orderlist1_.goods_amount as goods_a16_146_2_,
orderlist1_.income_amount as income_17_146_2_,
orderlist1_.modify_time as modify_18_146_2_,
orderlist1_.order_img as order_i19_146_2_,
orderlist1_.order_no as order_n20_146_2_,
orderlist1_.order_state as order_s21_146_2_,
orderlist1_.org_id as org_id22_146_2_,
orderlist1_.removed as removed23_146_2_,
orderlist1_.staff_id as staff_i24_146_2_,
orderlist1_.staff_name as staff_n25_146_2_,
orderlist1_.store_id as store_i26_146_2_,
orderlist1_.store_name as store_n27_146_2_,
orderlist1_.total_amount as total_a28_146_2_,
orderlist1_.trade_id as trade_i29_146_2_,
itemlist2_.order_id as order_i24_147_3_,
itemlist2_.id as id1_147_3_,
itemlist2_.id as id1_147_4_,
itemlist2_.actual_price as actual_p2_147_4_,
itemlist2_.appraise_id as appraise3_147_4_,
itemlist2_.category_id as category4_147_4_,
itemlist2_.category_name as category5_147_4_,
itemlist2_.create_time as create_t6_147_4_,
itemlist2_.dealer_id as dealer_i7_147_4_,
itemlist2_.dealer_name as dealer_n8_147_4_,
itemlist2_.deliver_num as deliver_9_147_4_,
itemlist2_.deliver_status as deliver10_147_4_,
itemlist2_.deliver_time as deliver11_147_4_,
itemlist2_.discount_price as discoun12_147_4_,
itemlist2_.express_code as express13_147_4_,
itemlist2_.express_name as express14_147_4_,
itemlist2_.express_no as express15_147_4_,
itemlist2_.goods_id as goods_i16_147_4_,
itemlist2_.goods_no as goods_n17_147_4_,
itemlist2_.goods_title as goods_t18_147_4_,
itemlist2_.item_img as item_im19_147_4_,
itemlist2_.item_state as item_st20_147_4_,
itemlist2_.item_type as item_ty21_147_4_,
itemlist2_.modify_time as modify_22_147_4_,
itemlist2_.num as num23_147_4_,
itemlist2_.order_id as order_i24_147_4_,
itemlist2_.original_price as origina25_147_4_,
itemlist2_.remark as remark26_147_4_,
itemlist2_.removed as removed27_147_4_,
itemlist2_.return_num as return_28_147_4_,
itemlist2_.sale_price as sale_pr29_147_4_,
itemlist2_.sale_type as sale_ty30_147_4_,
itemlist2_.share_price as share_p31_147_4_,
itemlist2_.shipper_id as shipper32_147_4_,
itemlist2_.shipper_name as shipper33_147_4_,
itemlist2_.sku_id as sku_id34_147_4_,
itemlist2_.sku_no as sku_no35_147_4_,
itemlist2_.source_id as source_36_147_4_,
itemlist2_.source_item_id as source_37_147_4_,
itemlist2_.source_no as source_38_147_4_,
itemlist2_.source_type as source_39_147_4_,
itemlist2_.specification as specifi40_147_4_,
itemlist2_.store_id as store_i41_147_4_,
itemlist2_.store_name as store_n42_147_4_,
itemlist2_.trade_id as trade_i43_147_4_
from
tc_trade tctrade0_
left outer join
tc_order orderlist1_
on tctrade0_.id=orderlist1_.trade_id
left outer join
tc_order_item itemlist2_
on orderlist1_.id=itemlist2_.order_id
where
tctrade0_.id=175010
有 两个 left join 就是这里导致 出现 重复的明细数据
旧代码, 多方的配置方法与我的方法不一样。 配置了 @ManyToOne 正常来说,就不要配置 @JoinColumn 了。
这里配置了, 如果去掉 @JoinColumn 又会报错,因为 配置有 tradeId ,有重复了。这样的话,就可能需要改代码了。
会可能出BUG的。
解决办法
1. 但是也有其他方式 去改了,比如 查询 之后, 再查一遍 明细 order 数据, 重复赋值即可。
2. 改为 懒加载就可以解决了,不要使用 EAGER ,也比较影响性能
fetch = FetchType.LAZY
c# – 使用nhibernate Criteria API进行Eager Fetching
我的精简实体看起来像这样:
class Limit { Risk {get; set;} } class Risk { List<Company> Companies { get;set;} } class Company { List<Address> OldAdresses {get;set;} } class Address { string Street { get;set;} }
我的Criteria调用如下所示:
var CriterionGruppe = Expression.Eq("Account.Id",someGuid); var temp = _transaktion.Session.CreateCriteria(typeof(Limit)) .SetFetchMode("Risk",FetchMode.Eager) .SetFetchMode("Risk.Companies",FetchMode.Eager) .Add(CriterionGruppe) .SetResultTransformer(new distinctRootEntityResultTransformer()) .List<Limit>();
地址仍然加载了许多选择.如何在我的标准电话中包含公司的旧地址.
我已经在ayende的博客中阅读了博客文章,并在stackoverflow上阅读了其他几个问题.但仍然没有运气.
我希望有人能指出我正确的方向.
在此先感谢彼得
When must we use eager loading in NHibernate? What is it’s usage?
NHibernate Eager Fetching Over Multiple Levels
Ayende Blog
解决方法
var account = _transaktion.Session.Load<Account>(someGuid); var temp = _transaktion.Session.CreateCriteria(typeof(Limit)) .SetFetchMode("Risk",FetchMode.Eager) .SetFetchMode("Risk.Companies",FetchMode.Eager) .SetFetchMode("Company.OldAddresses",FetchMode.Eager) .Add(Expression.Eq("Account",account)) .SetResultTransformer(new distinctRootEntityResultTransformer()) .List<Limit>();
然而,这是非常低效的.您正在加载大量重复数据以进行1 SQL查询.一个更好的方法是
>加载实际需要的投影>使用Futures和Batched延迟加载来避免单个笛卡尔结果集并选择n 1.
Hibernate / JPA ManyToOne与OneToMany
我目前正在阅读有关实体关联的Hibernate文档,但遇到一些困难却难以理解。它在本质上做的区别ManyToOne
和OneToMany
联系。尽管我在实际项目中使用了它们,但是我无法完全理解它们之间的差异。据我了解,如果一个表/一个实体ManyToOne
与另一个实体有关联,则该关联应来自另一侧OneToMany
。那么,我们应该如何根据具体情况决定选择哪个呢?它又如何影响数据库/查询/结果?到处都有很好的例子吗?
PS:我认为这与问题相关,如果有人可以解释关联所有者的观点以及双向关联和单向关联之间的区别,那将是有帮助的。
答案1
小编典典假设您有一个订单和一个订单行。您可以选择在Order和OrderLine之间具有单向OneToMany(Order将具有OrderLines的集合)。或者,您可以选择在OrderLine和Order之间具有ManyToOne关联(OrderLine将引用其Order)。或者,您可以选择同时拥有两者,在这种情况下,该关联将成为双向的OneToMany
/ ManyToOne关联。
您选择的解决方案主要取决于情况以及实体之间的耦合程度。例如,如果用户,公司,提供者都具有许多地址,则在每个人和地址之间都具有单向性,而使地址不知道其所有者是有意义的。
假设您有一个用户和一条消息,一个用户可以有数千条消息,那么将其建模为从消息到用户的仅一个ManyToOne可能是有意义的,因为无论如何您很少会询问用户的所有消息。不过,由于JPQL查询通过导航实体之间的关联而在实体之间进行连接,因此可以使该关联仅用于双向查询。
在双向关联中,您可能处于对象图不一致的情况。例如,订单A会有一组空的OrderLines,但是某些OrderLines将具有对Order
A的引用。JPA强制始终使关联的一侧为所有者一侧,而另一侧为相反一侧。JPA将忽略反面。所有者一方是决定存在什么关系的一方。在OneToMany双向关联中,所有者方必须是多方。因此,在前面的示例中,所有者端将是OrderLine,而JPA将保留行与订单A之间的关联,因为这些行都引用了A。
这样的关联将这样映射:
为了 :
@OneToMany(mappedBy = "parentOrder") // mappedBy indicates that this side is the // inverse side, and that the mapping is defined by the attribute parentOrder // at the other side of the association.private Set<OrderLine> lines;
在OrderLine中:
@ManyToOneprivate Order parentOrder;
Hibernate 3.6.10 Review @OneToMany
关于OnToMany
总共有5个参数.
1. cascade (eg. CascadeType.ALL);
2. fetch (eg. FetchType.LAZY);
3. orphanRemoval (eg. true);
4. targetEntity (eg. Person.class);
5. mappedBy (eg. "teamId");
1. cascade ,级联配置 . 有好几个类型. 要根据实际情况配置, 例如级联删除,新增,更新之类的. 但是级联删除确实要小心,反正我一般不使用级联删除.
2. fetch , 性能配置, 是不是LAZY , 要看情况,但是很多时候大家都会选择LAZY.
3. orphanRemoval (可选 — 默认为 false)标记这个集合作为双向关联关系中的方向一端。
4. targetEntity 对应多个实体类的class.
5. mappedBy 简单来说就是一对多的外键.
PS: 为了测试annotations ,查了很多资料,发现annotations 不太够全面.还是用 hbm.xml 比较好. 因为xml 有更详细的配置.
<bag name="member" cascade="save-update" lazy="true" inverse="false" >
<key column="teamId"></key>
<one-to-many/>
</bag>
关于java – Hibernate hql inner加入eager fetch on one to many,获取冗余的父对象的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于@OneToMany(fetch = FetchType.EAGER), FetchType.EAGER 急加载的导致查询重复明细数据的BUG、c# – 使用nhibernate Criteria API进行Eager Fetching、Hibernate / JPA ManyToOne与OneToMany、Hibernate 3.6.10 Review @OneToMany等相关知识的信息别忘了在本站进行查找喔。
本文标签: