本文简单总结一下hibernate的具有映射关系CRUD操作,主要使用到了cascade和fetch,其中cascade针对的是CUD操作,表示级联关系,fetch针对的是R操作,表示级联加载方式。
1.先看下javaEE的API中关于cascade的描述
javax.persistence
Enum CascadeType
java.lang.Object java.lang.Enum<CascadeType> javax.persistence.CascadeType
- All Implemented Interfaces:
- Serializable,
Comparable<CascadeType>
-
public enum CascadeType
- extends 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 thistype 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, |
Methods inherited from class java.lang.Object |
---|
finalize, |
具体的使用方式,以annotation为例,例如@ManyToOne(cascade=CascadeType.ALL),其中cascade的值可以是一个数组。
2.再看下javaEE的API中关于fetch的描述
javax.persistence Enum FetchType
java.lang.Object java.lang.Enum<FetchType> javax.persistence.FetchType
- All Implemented Interfaces:
- Serializable,
Comparable<FetchType>
-
public enum FetchType
- extends 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; }
- Since:
- Java Persistence 1.0
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, |
Methods inherited from class java.lang.Object |
---|
finalize, |
具体的使用方式,以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<User> users = new HashSet<User>(); @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<User> getUsers() { return users; } public void setUsers(Set<User> 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<User> 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<User> 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(); }
执行结果为
4.5看下testLoadGroup
@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.CRUD的D,即删除操作,主要使用delete方法
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(); }
执行结果
6.4看下testDeleteGroup
@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(); }
运行结果为
可以看到在删除1之前首先解除了多对其的引用
以上即为hibernate的CRUD常见的操作,需要注意cascade和fetch的配置,需要在具体的使用中仔细体会。