欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

级联操作

程序员文章站 2022-05-01 23:05:42
...
需求描述:
系统更新日志功能,需要在一个页面里展示主表信息+对所所有子表信息。

原始结构:

/**
*
*/
package common.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.OneToMany;
import javax.persistence.Table;

/**
* 系统更新说明
*
* @author sunhl
*
*/
@Entity
@Table(name="T_CHANGELOG")
@DomainModel(name="系统更新")
public class ChangeLog extends AbstractModel implements Serializable {

private static final long serialVersionUID = 1L;

private String title; // 标题
private List<ChangeLogItem> items = new ArrayList<ChangeLogItem>(); //详细内容

//gets and sets

public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}

@OneToMany
public List<ChangeLogItem> getItems() {
return items;
}

public void setItems(List<ChangeLogItem> items) {
this.items = items;
}

public void addItem(ChangeLogItem item){
this.getItems().add(item);
item.setChangeLog(this);
}
}
------------
/**
*
*/
package common.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;


/**
* 系统更新详细内容
*
* @author sunhl
*
*/
@Entity
@Table(name="AI_CHANGELOG_ITEM")
@DomainModel(name="系统更新详情")
public class ChangeLogItem extends AbstractModel implements Serializable {

private static final long serialVersionUID = 1L;

private ChangeLog changeLog; //所属更新说明
private String content; //内容

//--
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="changeLog_id")
public ChangeLog getChangeLog() {
return changeLog;
}
public void setChangeLog(ChangeLog changeLog) {
this.changeLog = changeLog;
}

@Column(length=2000)
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}

}




问题:

1- 级联提交需要的标记。
ChangeLog:

@OneToMany(cascade = CascadeType.ALL,mappedBy="changeLog")
public List<ChangeLogItem> getItems() {
return items;
}

cascade:
级联操作的类型,{[CascadeType.PERSIST(级联新建)][,CascadeType.REMOVE(级联删除)][,CascadeType.REFRESH(级 联刷新)][,CascadeType.MERGE(级联更新)]},或者使用CascadeType.ALL,表示选择全部。

@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="changeLog_id") //外键
public ChangeLog getChangeLog() {
return changeLog;
}


2- 级联提交后子表无主表id
完成1中设置后发现可以级联提交,但提交后changeLogItem的changeLog_id为空。
这是由于使用@OneToMany(mappedBy="changeLog")标记后系统将不再负责两个对象的关联问题,所以需要通过新建方法,给item自加设置changeLog属性的值。

//给新添加的item设置changeLog的值。
public void addItem(ChangeLogItem item){
this.getItems().add(item);
item.setChangeLog(this);
}


此时可以完成带子项的添加操作,但添加完成后需要展示详细内容时出现“net.sf.json.JSONException: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.bnm.system.domain.SysMenu.childs, no session or session was closed ”的错误。

3- 在加载主表信息时把子表信息同时加载出来,所以需要添加(或修改FetchType.LAZY)fetch=FetchType.EAGER强抽即时加载子项。

@OneToMany(fetch=FetchType.EAGER,cascade = CascadeType.ALL,mappedBy="changeLog")
public List<ChangeLogItem> getItems() {
return items;
}

fetch: FetchType.LAZY 延迟加载;FetchType.EAGER 即时加载。

完成后发现可以正常显示,并同时添加多条子项。
但回到changeLog列表发现,出现多条相同数据,changeLog的重复个数跟其子项数量是一致的。

4- 提交后主表列表出现多条相同记录。这是由于使用FetchType.EAGER加载子项时系统默认使用了left join的查询方式。另外,在本例中列表页面不需要显示子项信息,故将FetchType.EAGER改回FetchType.LAZY,在用户需要展开详情时再通过一次查询完成加载所有内容。

@OneToMany(fetch=FetchType.LAZY,cascade = CascadeType.ALL,mappedBy="changeLog")
public List<ChangeLogItem> getItems() {
return items;
}
-------------------------------------------------
//在action中添加,视具体操作而定。
public void onEdit(){
changeLogService.refash(changeLog)。
}


至此已可以正常显示列表和详情。
5- 无法新增的问题。新增时又出现“org.hibernate.AssertionFailure: null identifier”的问题。
故需要添加一个非空判断

public void onEdit(){
if(changeLog.getId()!=null){
changeLogService.refash(changeLog)
}
}

ok,至此主从表的关联操作已基本完成。

6- 无法单独删除子项的问题。
问题可能是特殊的,在详细信息中删除一个子项时我使用了remove(item)方法,完成后看似确实生效了,但当重新加载对应的数据时,刚才被删掉的子项又显示了出来。
修改方式是添加@Cascade (org.hibernate.annotations.CascadeType.DELETE_ORPHAN)标记

//注:新添加的CascadeType.DELETE_ORPHAN是org.hibernate.annotations的标记。
// 而CascadeType.ALL是javax.persistence.CascadeType的

@OneToMany(fetch=FetchType.LAZY,cascade = CascadeType.ALL,mappedBy="changeLog")
@Cascade (org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
public List<ChangeLogItem> getItems() {
return items;
}


终于完成。

------
关于第6个问题参考文档,内有详细说明:
http://blog.csdn.net/zhh521125/archive/2010/10/20/5954137.aspx