实体关系
一.七种实体关系
(1)一对一(单向/双向) @OneToOne
(2)一对多(单向/双向) @OneToMany
(3)多对一(单向/双向)@ManyToOne
(4)多对多(双向)@ManyToMany
二.实体一对多双向自身关联关系
例子:Windows文件夹
作用:可以表示一种层次关系
实体:
package com.lyh.model;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
@Entity
public class Folder implements Serializable {
private int id;
private String name;
private Folder parentFolder;
private Collection childFolders =new ArrayList();
public Folder() {
}
public Folder(String name, Folder parentFolder, Collection childFolders) {
this.name = name;
this.parentFolder = parentFolder;
this.childFolders = childFolders;
}
@Id
@GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToOne
public Folder getParentFolder() {
return parentFolder;
}
public void setParentFolder(Folder parentFolder) {
this.parentFolder = parentFolder;
}
@OneToMany(cascade={CascadeType.ALL},mappedBy = "parentFolder" /*,fetch=FetchType.EAGER*/)
public Collection getChildFolders() {
return childFolders;
}
public void setChildFolders(Collection childFolders) {
this.childFolders = childFolders;
}
}
实体关系图:
用到的注释说明:
@OneToMany
target Entity
targetEntity 属性是 Class 类型的属性。定义实体一对多关系中处于从属地位的实体类的类型。
mappedBy
mappedBy 属性是 String 类型的属性。mappedBy 属性的值是当前实体在关联实体中的属性名称,使用 mappedBy 可以定义实体类之间的双向关系。如果类之间是单向关系,不需要提供定义,如果类和类之间形成双向关系,我们就需要使用这个属性进行定义,否则可能引起数据 一致性的问题。
Cascade
cascade 属性的类型是 CascadeType[] 类型。cascade 属性定义实体和实体之间的级联关系。使用 cascade 属性定义的级联关系将被容器视为对当前类对象及其关联类对象采取相同的操作,而且这种关系是递归调用的。
cascade 的值只能从 CascadeType.PERSIST(级联新建)、CascadeType.REMOVE(级联删除)、CascadeType.REFRESH(级 联刷新)、CascadeType.MERGE(级联更新)中选择一个或多个。还有一个更方便的选择是使用 CascadeType.ALL,表示选择上面全部四项.cascade=ALL等价于cascade={PERSIST, MERGE, REMOVE, REFRESH}.。
fetch
fetch 属性是 FetchType 类型的属性。可选择项包括:FetchType.EAGER 和 FetchType.LAZY。前者表示关联关系的从类在主类加载的时候同时加载,后者表示关联关系的从类在自己被访问时才加载。默认值是 FetchType.EAGER。
@OneToMany默认类型为FetchType.LAZY
Optional
optional 属性是 boolean 类型的属性。optional 属性用于定义关联关系的从类对象是否必须存在。如果设置为 false,那么该属性就不能设置为 null。默认值是 true。
表结构:
会话Bean
package com.lyh.business;
import com.lyh.model.Folder;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
/**
* User: yhliu
* Date: 2007-9-15
* Time: 13:54:10
* Folder实体操作无状态会话Bean
*/
@Stateless
@Remote
public class FolderServiceBean implements FolderService {
@PersistenceContext(unitName = "mysql")
private EntityManager manager;
public int createFolder(Folder folder) {
manager.persist(folder);
return folder.getId();
}
//根据文件夹名查找文件夹
public Folder findFolder(String name) {
Folder folder = null;
try {
Query query = manager.createQuery(
"from Folder f where f.name=?1");
query.setParameter(1, name);
folder = (Folder) query.getSingleResult();
} catch (EntityNotFoundException notFound) {
System.out.println("EntityNotFoundException");
} catch (NonUniqueResultException nonUnique) {
System.out.println("NonUniqueResultException");
}
return folder;
}
//根据文件夹名查找它下面的子文件夹
public List getChildFoldersByName(Folder folder) {
List childFolders = new ArrayList();
try {
Query query = manager.createQuery(
"from Folder f where f.parentFolder=?1");
query.setParameter(1, folder);
childFolders = query.getResultList();
} catch (EntityNotFoundException notFound) {
System.out.println("EntityNotFoundException");
}
return childFolders;
}
//根据id查找文件夹
public Folder findFolder(int id) {
return manager.find(Folder.class, id);
}
//根据移除文件夹
public void removeFolder(int id) {
Folder parentfolder = manager.find(Folder.class, id);
manager.remove(parentfolder);
}
}
Client类
package com.lyh.clients;
import com.lyh.business.FolderService;
import com.lyh.model.Folder;
import javax.naming.Context;
import javax.rmi.PortableRemoteObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Created by IntelliJ IDEA.
* User: yhliu
* Date: 2007-9-15
* Time: 14:14:50
*/
public class Client {
public static void main(String[] args) throws Exception {
Context jndiContext = getInitialContext();
Object ref = jndiContext.lookup("FolderServiceBean/remote");
FolderService service = (FolderService)
PortableRemoteObject.narrow(ref, FolderService.class);
//一级
Folder rootFolder = new Folder("root", null, new ArrayList());
//二级
Folder windowsFolder = new Folder("windows", null, new ArrayList());
Folder programFilesFolder = new Folder("programFiles", null, new ArrayList());
//三级
Folder helpFolder = new Folder("help", null, new ArrayList());
Folder frontsFolder = new Folder("fronts", null, new ArrayList());
Folder winrarFolder = new Folder("winrar", null, new ArrayList());
//建立根文件夹和windows文件夹之间的联系
rootFolder.getChildFolders().add(windowsFolder);
windowsFolder.setParentFolder(rootFolder);
//建立根文件夹和Programs Files文件夹之间的联系
rootFolder.getChildFolders().add(programFilesFolder);
programFilesFolder.setParentFolder(rootFolder);
windowsFolder.getChildFolders().add(helpFolder);
helpFolder.setParentFolder(windowsFolder);
windowsFolder.getChildFolders().add(frontsFolder);
frontsFolder.setParentFolder(windowsFolder);
programFilesFolder.getChildFolders().add(winrarFolder);
winrarFolder.setParentFolder(programFilesFolder);
service.createFolder(rootFolder);
Folder windows=service.findFolder("windows");
if(windows==null){
System.out.println("没有找到这个文件夹");
}else{
Collection windowsFolderChilders= windows.getChildFolders();
System.out.println("Windows目录下的子目录有:");
// for (Folder windowsFolderChilder : windowsFolderChilders) {
// System.out.println(windowsFolderChilder.getName());
// }
List folders=service.getChildFoldersByName(windows);
for (Folder folder : folders) {
System.out.println(folder.getName());
}
}
// service.removeFolder(windows.getId());
}
public static Context getInitialContext() throws javax.naming.NamingException {
return new javax.naming.InitialContext();
}
}
可能出现的异常:
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.lyh.model.Folder.childFolders, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:358)
原因:关联实体的fetch=FetchType.LAZY 把fetch设置为FetchType.EAGER,可这未免也太影响了效率,
http://forum.springsource.org/showthread.php?t=65169
http://jhyimu2005.javaeye.com/blog/516386
http://hi.baidu.com/vsandjava/blog/item/328f52fbb8d063156c22eb74.html