weixin_42297864 2008-12-07 22:44
浏览 207
已采纳

openSessionInView引出的奇怪问题

appfuse那套东西,Spring+Hibernate,在Controller层写下如下语句,[color=red]目的是根据视图层的修改内容更新
[code="java"]
sellerSellItem[/color]:
SellerSellItem sellerSellItem = ....(根据视图层生成)
sellerSellItem.setSellerSellReport(sellerSellReport);
sellerSellReport.addSellerSellItem(sellerSellItem);
//sellerSellItemManager.save(sellerSellItem); [color=red]//1.[/color]
sellerSellReportManager.save(sellerSellReport); [color=red]//2[/color]
[/code]
注:sellerSellReport是主表,sellerSellItem是明细.使用了OpenSessionInView.
具体dao实现(service层从略):
[code="java"]
public T save(T object) {
super.getHibernateTemplate().saveOrUpdate(object); [color=red]//3[/color]
super.getHibernateTemplate().flush();
return object;
}
[/code]

问题来了:
1)如果屏蔽语句1,则sellerSellItem根本无法更新(不报错),但是可以新增;
[code="java"]
sellerSellItem[/color]:
SellerSellItem sellerSellItem = ....(根据视图层生成)
sellerSellItem.setSellerSellReport(sellerSellReport);
sellerSellReport.addSellerSellItem(sellerSellItem);
sellerSellReportManager.save(sellerSellReport); [color=red]//2[/color]

service略
dao层:
public T save(T object) {
super.getHibernateTemplate().saveOrUpdate(object); [color=red]//3[/color]
super.getHibernateTemplate().flush();
return object;
}
[/code]
2)如果屏蔽语句2,则sellerSellItem更新时报错:org.hibernate.NonUniqueObjectException,新增时成功.
[code="java"]
sellerSellItem[/color]:
SellerSellItem sellerSellItem = ....(根据视图层生成)
sellerSellItem.setSellerSellReport(sellerSellReport);
sellerSellReport.addSellerSellItem(sellerSellItem);
//sellerSellItemManager.save(sellerSellItem); [color=red]//1.[/color]

service略
dao层:
public T save(T object) {
super.getHibernateTemplate().saveOrUpdate(object); [color=red]//3[/color]
super.getHibernateTemplate().flush();
return object;
}
[/code]
3)将语句3改成merge,屏蔽语句2,靠,更新倒是无问题,新增时一下就直接新增两条同样的记录.(采用merge必须立即flush,否则在service层不能得到id,从而产生各种问题);
[code="java"]
sellerSellItem[/color]:
SellerSellItem sellerSellItem = ....(根据视图层生成)
sellerSellItem.setSellerSellReport(sellerSellReport);
sellerSellReport.addSellerSellItem(sellerSellItem);
sellerSellItemManager.save(sellerSellItem); [color=red]//1.[/color]

service略
dao层:
public T save(T object) {
super.getHibernateTemplate().merge(object); [color=red]//3[/color]
super.getHibernateTemplate().flush();
return object;
}
[/code]
4)将语句3改成merge,屏蔽语句1,更晕,这时实际上新增已正确了,sellerSellReport中已保存有正确的sellerSellItem,但真正的sellerSellItem却并不正确(没有id,处于游离状态),也就是sellerSellItem与sellerSellReport.sellerSellItem此时居然不同步!
[code="java"]
sellerSellItem[/color]:
SellerSellItem sellerSellItem = ....(根据视图层生成)
sellerSellItem.setSellerSellReport(sellerSellReport);
sellerSellReport.addSellerSellItem(sellerSellItem);
sellerSellReportManager.save(sellerSellReport); [color=red]//2[/color]

service略
dao层:
public T save(T object) {
super.getHibernateTemplate().merge(object); [color=red]//3[/color]
super.getHibernateTemplate().flush();
return object;
}
[/code]
5)不得已,先将sellerSellItem游离掉吧.结果发现用尽手段都不能游离掉,什么getHibernateTemplate().contains(obj)和getHibernateTemplate().evict(obj)根本不起作用似的.
[code="java"]
sellerSellItem[/color]:
SellerSellItem sellerSellItem = ....(根据视图层生成)
sellerSellItem.setSellerSellReport(sellerSellReport);
sellerSellReport.addSellerSellItem(sellerSellItem);
sellerSellReportManager.save(sellerSellReport); [color=red]//2[/color]

service略
dao层:
public T save(T object) {
supre.getHibernateTemplate().evict(object);
super.getHibernateTemplate().merge(object); [color=red]//3[/color]
super.getHibernateTemplate().flush();
return object;
}
[/code]

请高人给出解决措施啊,呵呵.
看了源码,分析如下:
1)第一种方式,即利用主表更新子表的方式,由于采用了openSessionInView,在sellerSellReport中已存在id相同的sellerSellItem,所以sellerSellReport.addSellerSellItem(sellerSellItem)这句语句根本就相当于未执行.如果修改SellerSellItem的equals和hasCode方法,则sellerSellReport中会同时存在两个id相等的sellerSellItem,同样会报NonUniqueObjectException;
2)第二种方式,即直接更新子表的方式.由于session中已存在一个id相同的sellerSellItem,当然会报NonUniqueObjectException;
3)第三种方式,相当于更新子表,使用merge语句,经跟踪发现:在执行merge时和执行flush时会分别插入一条记录.进一步跟踪的结果:在执行merge时,是更新子表sellerSellItem,但主表sellerSellReport.sellerSellItem并未更新(不同步!);于是继续进行flush时,会更新主表,导致sellerSellItem重新被增加进去.
4)第四种方式,相当于更新主表,使用merge语句,发现更新后的主表的sellerSellReport.sellerSellItem有id,而sellerSellItem却没有id,居然有这种情况!也就是说merge语句慎用于一对多和多对多的情形.
5)evict不是不起作用,而是因为evict必须是对于get或load或find出来的真正的持久对象才能使其游离(开始以为Session的contains方法是利用持久化对象的equals和hashCode方法来的,结果发现不是这样,而是有个什么什么包装了一次,忘了).

唉,实在是想不到办法了,请各位帮忙,谢了先^_^

  • 写回答

1条回答 默认 最新

  • iteye_10573 2008-12-13 05:50
    关注

    try

    [code="java"]
    sellerSellItem[/color]:

    SellerSellItem sellerSellItem = ....(根据视图层生成)

    sellerSellItem.setSellerSellReport(sellerSellReport);

    SellerSellItem item = sellerSellItemManager.save(sellerSellItem);

    sellerSellReport.addSellerSellItem(item);

    service略

    dao层:

    public T save(T object) {

    T persist = super.getHibernateTemplate().merge(object);
    super.getHibernateTemplate().flush();

    return persist;

    }
    [/code]

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 #MATLAB仿真#车辆换道路径规划
  • ¥15 java 操作 elasticsearch 8.1 实现 索引的重建
  • ¥15 数据可视化Python
  • ¥15 要给毕业设计添加扫码登录的功能!!有偿
  • ¥15 kafka 分区副本增加会导致消息丢失或者不可用吗?
  • ¥15 微信公众号自制会员卡没有收款渠道啊
  • ¥100 Jenkins自动化部署—悬赏100元
  • ¥15 关于#python#的问题:求帮写python代码
  • ¥20 MATLAB画图图形出现上下震荡的线条
  • ¥15 关于#windows#的问题:怎么用WIN 11系统的电脑 克隆WIN NT3.51-4.0系统的硬盘