本文简单总结一下hibernate的具有映射关系CRUD操作,主要使用到了cascade和fetch,其中cascade针对的是CUD操作,表示级联关系,fetch针对的是R操作,表示级联加载方式。
1.先看下javaEE的API中关于cascade的描述
java.lang.Object java.lang.Enumjavax.persistence.CascadeType
public enum CascadeTypeextends Enum< CascadeType>
Defines the set of cascadable operations that are propagated to the associated entity. The value cascade=ALL
is equivalent to
cascade={PERSIST, MERGE, REMOVE, REFRESH}
.
Since: Java Persistence 1.0
Enum Constant Summary | |
---|---|
ALL Cascade all operations |
|
MERGE Cascade merge operation |
|
PERSIST Cascade persist operation |
|
REFRESH Cascade refresh operation |
|
REMOVE Cascade remove operation |
Method Summary | |
---|---|
static CascadeType |
valueOf(String name) Returns the enum constant of this type with the specified name. |
static CascadeType[] |
values() Returns an array containing the constants of this enum type, in the order they're declared. |
Methods inherited from class java.lang.Enum |
---|
clone, compareTo, equals, getDeclaringClass, hashCode, name, ordinal, toString, valueOf |
Methods inherited from class java.lang.Object |
---|
finalize, getClass, notify, notifyAll, wait, wait, wait |
具体的使用方式,以annotation为例,例如@ManyToOne(cascade=CascadeType.ALL),其中cascade的值可以是一个数组。
2.再看下javaEE的API中关于fetch的描述
java.lang.Object java.lang.Enumjavax.persistence.FetchType
public enum FetchTypeextends Enum< FetchType>
Defines strategies for fetching data from the database. The EAGER
strategy is a requirement on the persistence provider runtime that data must be eagerly fetched. The LAZY
strategy is a hint to the persistence provider runtime that data should be fetched lazily when it is first accessed. The implementation is permitted to eagerly fetch data for which the LAZY
strategy hint has been specified. In particular, lazy fetching might only be available for Basic
mappings for which property-based access is used.
Example: @Basic(fetch=LAZY) protected String getName() { return name; }
Enum Constant Summary | |
---|---|
EAGER Defines that data must be eagerly fetched |
|
LAZY Defines that data can be lazily fetched |
Method Summary | |
---|---|
static FetchType |
valueOf(String name) Returns the enum constant of this type with the specified name. |
static FetchType[] |
values() Returns an array containing the constants of this enum type, in the order they're declared. |
Methods inherited from class java.lang.Enum |
---|
clone, compareTo, equals, getDeclaringClass, hashCode, name, ordinal, toString, valueOf |
Methods inherited from class java.lang.Object |
---|
finalize, getClass, notify, notifyAll, wait, wait, wait |
具体的使用方式,以annotation为例,例如@ManyToOne(fetch=FetchType.EAGER)。
3.进行CRCD的C,即插入(insert)操作
仍然使用之前的Group和User,二者为一对多关系
Group
package com.baosight.model;
import java.util.*;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name="t_group")
public class Group {
private String id;
private String name;
private Set users = new HashSet();
@Id
@GeneratedValue//auto
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@OneToMany(mappedBy="group",cascade=CascadeType.ALL)
public Set getUsers() {
return users;
}
public void setUsers(Set users) {
this.users = users;
}
}
User
package com.baosight.model;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@Entity
@Table(name="t_user")
public class User {
private String id;
private String name;
private Group group;
@Id
@GeneratedValue//auto
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToOne(cascade=CascadeType.ALL)
public Group getGroup() {
return group;
}
public void setGroup(Group group) {
this.group = group;
}
}
使用JUnit进行测试
package com.baosight.model;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class OrMappingTest {
private static SessionFactory sf = null;
@BeforeClass
public static void beforeClass(){
new SchemaExport(new AnnotationConfiguration().configure()).create(false, true);
// 读取配置文件
Configuration cfg = new AnnotationConfiguration();
// 得到session工厂
sf = cfg.configure().buildSessionFactory();
}
@Test
public void testSaveUser() {
Group g = new Group();
User u = new User();
u.setName("u1");
g.setName("g1");
u.setGroup(g);
Session s = sf.getCurrentSession();
s.beginTransaction();
// s.save(g);
s.save(u);
s.getTransaction().commit();
}
@Test
public void testSaveGroup() {
User u1 = new User();
u1.setName("u1");
User u2 = new User();
u2.setName("u2");
Group g = new Group();
g.setName("g1");
g.getUsers().add(u1);
g.getUsers().add(u2);
u1.setGroup(g);
u2.setGroup(g);
Session s = sf.getCurrentSession();
s.beginTransaction();
s.save(g);
s.getTransaction().commit();
}
@Test
public void testGetUser() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
User u = (User) s.get(User.class, "2");
s.getTransaction().commit();
System.out.println(u.getGroup().getName());
}
@Test
public void testUpdateUser() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
User u = (User) s.get(User.class, "2");
s.getTransaction().commit();
u.setName("user");
u.getGroup().setName("group");
Session s2 = sf.getCurrentSession();
s2.beginTransaction();
s2.update(u);
s2.getTransaction().commit();
}
@Test
public void testLoadUser() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
User u = (User) s.load(User.class, "2");
System.out.println(u.getGroup().getName());
s.getTransaction().commit();
}
@Test
public void testDeleteUser() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
// User u = (User) s.load(User.class, "2");
// u.setGroup(null);
// s.delete(u);
s.createQuery("delete from User u where u.id=2").executeUpdate();
s.getTransaction().commit();
}
@Test
public void testGetGroup() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
Group g = (Group) s.get(Group.class, "1");
s.getTransaction().commit();
for(User u:g.getUsers()){
System.out.println(u.getName());
}
}
@Test
public void testLoadGroup() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
Group g = (Group) s.load(Group.class, "1");
for(User u:g.getUsers()){
System.out.println(u.getName());
}
s.getTransaction().commit();
}
@Test
public void testDeleteGroup() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
Group g = (Group) s.load(Group.class, "1");
for(User u:g.getUsers()){
u.setGroup(null);
}
g.setUsers(null);
s.delete(g);
s.getTransaction().commit();
}
/*@Test
public void testSchemaExport() {
}*/
@AfterClass
public static void afterClass(){
// 关闭session工厂
sf.close();
}
}
其中测试C使用的方法为testSaveUser和testSaveGroup
3.1先看testSaveUser,当不设置Group和User二者之间的关联时,方法为:
@Test
public void testSaveUser() {
Group g = new Group();
User u = new User();
u.setName("u1");
g.setName("g1");
// u.setGroup(g);
Session s = sf.getCurrentSession();
s.beginTransaction();
s.save(g);
s.save(u);
s.getTransaction().commit();
}
运行结果为
可以看到2张表插入的数据没有关联,group_id是null
3.2当testSaveUser设置Group和User二者之间的关联时,方法为:
@Test
public void testSaveUser() {
Group g = new Group();
User u = new User();
u.setName("u1");
g.setName("g1");
u.setGroup(g);
Session s = sf.getCurrentSession();
s.beginTransaction();
// s.save(g);
s.save(u);
s.getTransaction().commit();
}
需要在User中的getGroup上使用@ManyToOne(cascade=CascadeType.ALL),表示级联保存
@ManyToOne(cascade=CascadeType.ALL)
public Group getGroup() {
return group;
}
运行结果
可以看到,这时group_id就有值了。
3.3再看看testSaveGroup,当仅在Group设置二者之间的关联时,方法为:
@Test
public void testSaveGroup() {
User u1 = new User();
u1.setName("u1");
User u2 = new User();
u2.setName("u2");
Group g = new Group();
g.setName("g1");
g.getUsers().add(u1);
g.getUsers().add(u2);
// u1.setGroup(g);
// u2.setGroup(g);
Session s = sf.getCurrentSession();
s.beginTransaction();
s.save(g);
s.getTransaction().commit();
}
需要在Group中的getUsers上使用@OneToMany(mappedBy="group",cascade=CascadeType.ALL),表示级联保存
@OneToMany(mappedBy="group",cascade=CascadeType.ALL)
public Set getUsers() {
return users;
}
测试结果为
可以看到,是有问题的,此时group_id的值为null
3.4还需要在上面的User设置关联,testSaveGroup为
@Test
public void testSaveGroup() {
User u1 = new User();
u1.setName("u1");
User u2 = new User();
u2.setName("u2");
Group g = new Group();
g.setName("g1");
g.getUsers().add(u1);
g.getUsers().add(u2);
u1.setGroup(g);
u2.setGroup(g);
Session s = sf.getCurrentSession();
s.beginTransaction();
s.save(g);
s.getTransaction().commit();
}
这时,group_id就有值了
4.关于CRUD的R,即读取操作,主要使用的是get和load方法,使用到的是fetch
值得一提的是,对于本例中的Many2One/One2Many双向关联,在不设置fetch的时候,hibernate对于fetch是有默认值的。
具体来说,@ManyToOne的默认值为fetch=FetchType.EAGER。例如本例中User不设置fetch相当于设置了fetch=FetchType.EAGER,当R的时候会自动级联查询1的一方
@OneToMany的默认值为fetch=FetchType.LAZY。例如本例中Group不设置fetch相当于设置了fetch=FetchType.LAZY,当R的时候不会自动级联查询多的一方
首先看下get方法
4.1看下testGetUser方法
@Test
public void testGetUser() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
User u = (User) s.get(User.class, "2");
s.getTransaction().commit();
System.out.println(u.getGroup().getName());
}
结果为
从结果可以看到,会自动进行级联查询
4.2看下testGetGroup方法
@Test
public void testGetGroup() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
Group g = (Group) s.get(Group.class, "1");
s.getTransaction().commit();
for(User u:g.getUsers()){
System.out.println(u.getName());
}
}
运行结果为
上面讲过@OneToMany的默认值为fetch=FetchType.LAZY,所以未执行对User的查询
4.3修改Group的getUsers方法
@OneToMany(mappedBy="group",cascade=CascadeType.ALL,fetch=FetchType.EAGER)
public Set getUsers() {
return users;
}
测试结果为
再来看看load方法
4.4看下testLoadUser
@Test
public void testLoadUser() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
User u = (User) s.load(User.class, "2");
System.out.println(u.getGroup().getName());
s.getTransaction().commit();
}
执行结果为
@Test
public void testLoadGroup() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
Group g = (Group) s.load(Group.class, "1");
for(User u:g.getUsers()){
System.out.println(u.getName());
}
s.getTransaction().commit();
}
执行结果为
注意,此时,并为设置Group和Use的fetch属性
5.CRUD的U,即修改操作,主要使用的是update方法
前面提到了,可以设置cascade指明级联方式,再执行与之相对应的操作
看下testUpdateUser
@Test
public void testUpdateUser() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
User u = (User) s.get(User.class, "2");
s.getTransaction().commit();
u.setName("user");
u.getGroup().setName("group");
Session s2 = sf.getCurrentSession();
s2.beginTransaction();
s2.update(u);
s2.getTransaction().commit();
}
执行结果为
6.1先看下testDeleteUser
@Test
public void testDeleteUser() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
User u = (User) s.load(User.class, "2");
// u.setGroup(null);
s.delete(u);
// s.createQuery("delete from User u where u.id=2").executeUpdate();
s.getTransaction().commit();
}
测试结果
由于Group和User中都设置了cascade,所以删除User数据会级联删除Group的数据6.2要想只删除User的数据,需要在删除之前先切断映射关系
@Test
public void testDeleteUser() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
User u = (User) s.load(User.class, "2");
u.setGroup(null);
s.delete(u);
// s.createQuery("delete from User u where u.id=2").executeUpdate();
s.getTransaction().commit();
}
执行结果
6.3当然,在明确知道要删除的多的一方的id的话,还可以直接执行HQL进行删除操作
@Test
public void testDeleteUser() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
// User u = (User) s.load(User.class, "2");
// u.setGroup(null);
// s.delete(u);
s.createQuery("delete from User u where u.id=2").executeUpdate();
s.getTransaction().commit();
}
@Test
public void testDeleteGroup() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
Group g = (Group) s.load(Group.class, "1");
// for(User u:g.getUsers()){
// u.setGroup(null);
// }
// g.setUsers(null);
s.delete(g);
s.getTransaction().commit();
}
运行结果
由于Group和User都设置了cascade,所以在删除Group的同时会级联删除相关的User数据
为了解决这一问题,需要在删除之前,先将二者之间的级联设置为null
注意,只删除一方无效果,需要将双方的关联都去除
6.5只删除1,不删除多
@Test
public void testDeleteGroup() {
testSaveGroup();
Session s = sf.getCurrentSession();
s.beginTransaction();
Group g = (Group) s.load(Group.class, "1");
for(User u:g.getUsers()){
u.setGroup(null);
}
g.setUsers(null);
s.delete(g);
s.getTransaction().commit();
}
运行结果为
以上即为hibernate的CRUD常见的操作,需要注意cascade和fetch的配置,需要在具体的使用中仔细体会。