1.单向一对多
1、映射配置:一方配置
@OneToMany
// 必须配置外键id,否则会多生成一张表,形成多对多的关系
@JoinColumn(name = "dir_id")
// 建议实例化,用的时候不需要在实例化,这里和单向多对一要求不同
private Set<Product> products = new HashSet<Product>();
2、保存代码:我们一般不使用单向一对多(原因:性能太差)
productDir.getProducts().add(product1);
entityManager.persist(product1);
entityManager.persist(product2);
entityManager.persist(productDir);
结论:先保存多方或者一方都会发送给五条sql语句
3条insert,2条update,无论怎样调整保存的顺序,都会有额外的update语句
原因就是现在外键是由一方来处理,一方处理必须发出额外的update语句
3、获取代码:
//得到产品类型
ProductDir productDir = entityManager.find(ProductDir.class, 1L);
//根据产品类型获得产品集合
Set<Product> products = productDir.getProducts();
要通过一方集合属性的size是否为0判断集合是否有值,不能通过集合是否为null来配置,这个和单向多对一是不同的
if (products.size()!=0){
System.out.println("该产品类型有产品");
}else{
System.out.println("该产品类型没有产品");
}
4、获取代码细节:
单向一对多,默认是抓取方式是懒加载,但是我们一般使用什么抓取方式还是会配@OneToMany(fetch=FetchType.EAGER)
或者 @OneToMany(fetch=FetchType.LAZY)
2.集合映射
(1)、Hibernate提供的类:实现延迟加载的关键类
org.hibernate.collection.internal.PersistentSet 实现了java.util.Set接口
org.hibernate.collection.internal.PersistentBag 实现了java.util.List接口
(2)、声明集合时必须使用接口接收(Set、List)
java.util.Set对应PersistentSet
java.util.List对应PersistentBag
因为Hibernate在创建了集合对象后是使用了它的的集合类PersistentSet来接收数据的(而不是我们写的那个HashSet)。如果我们使用接口声明(如Set),那么它的集合类PersistentSet也是实现了这个接口。所以它可以把这个类赋给Set。 但是我们使用HashSet,它们关系是平级的,不能进行转换,所以就出现错误了。
3.双向多对一/双向一对多
3.1.保存数据分析
Product多方
@Entity
@Table(name = "t_product")
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "dir_id")
private ProductDir dir;
priducrDir一方 // 应该将外键交给多方管理,可以少发sql语句,提高性能
@Entity
@Table(name = "t_productdir")
public class ProductDir {
@Id
@GeneratedValue
private Long id;
private String name;
//@JoinColumn(name = "dir_id") //管理权交给多方后,这里就不需要再配置外键了.
@OneToMany(mappedBy = "dir") //mappedBy = "dir"表示一方的关系参照多方Prodcut属性dir来管理
private Set<Product> products = new HashSet<>();
测试 //还是应该先保存一方,再保存多方,就可只发3条sql语句
@Test
public void testAdd() throws Exception{
Product product1 = new Product();
product1.setName("保时捷");
Product product2 = new Product();
product2.setName("大切洛斯基");
ProductDir productDir = new ProductDir();
productDir.setName("喜欢的汽车");
//双方关联
product1.setDir(productDir);
product2.setDir(productDir);
productDir.getProducts().add(product1);
productDir.getProducts().add(product2);
EntityManager entityManager = JpaUtil.getEntityManager();
entityManager.getTransaction().begin();
//先保存一方,再保存多方,就可只发3条sql语句
entityManager.persist(productDir);
entityManager.persist(product1);
entityManager.persist(product2);
entityManager.getTransaction().commit();
entityManager.close();
}
3.2.级联保存
(1)概述
级联操作:操作一端的同时,另外一端也会受到级联 (级联的报错和级联删除)
效果:保存商品类别(ProductDir,一方),就把它对应的商品(Product,多方)也一起保存起来!
关键是只调用一次persist方法
(2)映射配置
一方配置
mappedBy = "dir"表示一方的关系参照多方Prodcut属性dir来管理
cascade 级联操作 属性:ALL所有支持 PERSIST级联保存 REMOVE级联删除 REFRESH 更新
@OneToMany(cascade = CascadeType.PERSIST, mappedBy = "dir")
private Set<Product> products = new HashSet<Product>();
(3)测试代码
//保存
@Test
public void testAdd() throws Exception{
Product product1 = new Product();
product1.setName("保时捷");
Product product2 = new Product();
product2.setName("大切洛斯基");
ProductDir productDir = new ProductDir();
productDir.setName("喜欢的汽车");
//双方关联
productDir.getProducts().add(product1);
productDir.getProducts().add(product2);
product1.setDir(productDir);
product2.setDir(productDir);
EntityManager entityManager = JpaUtil.getEntityManager();
entityManager.getTransaction().begin();
//这里只需要保存一方,自自动将关联的数据添加进去
entityManager.persist(productDir);
entityManager.getTransaction().commit();
entityManager.close();
}
发3条sql语句
Hibernate: insert into t_productdir (name) values (?)
Hibernate: insert into t_product (dir_id, name) values (?, ?)
Hibernate: insert into t_product (dir_id, name) values (?, ?)
3.3.其他级联操作相关测试方案
(1)级联删除:删除一方,然后级联删除多方(危险操作)
示例:删除商品分类(ProductDir,一方),它下面的所有商品(Product,多方)都要删除
如果我们直接删除的话,就发现报一个错(外键关系不能删除)
一方配置cascade = CascadeType.REMOVE
@OneToMany(cascade = CascadeType.REMOVE,mappedBy = "dir",fetch = FetchType.LAZY)
private Set<Product> products = new HashSet<>();
测试
@Test
public void testdelect() throws Exception{
EntityManager entityManager = JpaUtil.getEntityManager();
entityManager.getTransaction().begin();
//查询产品分类
ProductDir productDir = entityManager.find(ProductDir.class, 1L);
//删除该产品分类的所有关联数据
entityManager.remove(productDir);
entityManager.getTransaction().commit();
entityManager.close();
}
结果
Hibernate: select productdir0_.id as id1_1_0_, productdir0_.name as name2_1_0_ from t_productdir productdir0_ where productdir0_.id=?
Hibernate: select products0_.dir_id as dir_id3_1_0_, products0_.id as id1_0_0_, products0_.id as id1_0_1_, products0_.dir_id as dir_id3_0_1_, products0_.name as name2_0_1_ from t_product products0_ where products0_.dir_id=?
Hibernate: delete from t_product where id=?
Hibernate: delete from t_product where id=?
Hibernate: delete from t_productdir where id=?
(2)从一方去删除一个解除关系(外键的值为null)的多方:先获取一方,在删除一个多方
一方配置
// orphanRemoval=true可以从一方删除解除关系(就是外键dir_id=null)的多方
@OneToMany(cascade = CascadeType.REMOVE,mappedBy = "dir",fetch = FetchType.LAZY,orphanRemoval = true)
private Set<Product> products = new HashSet<>();
测试
@Test
public void testdelect2() throws Exception{
EntityManager entityManager = JpaUtil.getEntityManager();
entityManager.getTransaction().begin();
//查询产品分类
ProductDir productDir = entityManager.find(ProductDir.class, 1L);
Product product = entityManager.find(Product.class, 1L);
// 删除所有和当前对象解除关联关系的对象
productDir.getProducts().remove(product);
entityManager.getTransaction().commit();
entityManager.close();
}
(3)从一方去删除所有解除关系的多方:先获取一方,在删除所有多方
一方配置
// orphanRemoval=true可以从一方删除解除关系(就是外键dir_id=null)的多方
@OneToMany(cascade = CascadeType.REMOVE,mappedBy = "dir",fetch = FetchType.LAZY,orphanRemoval = true)
private Set<Product> products = new HashSet<>();
@Test
public void testdelect3() throws Exception{
EntityManager entityManager = JpaUtil.getEntityManager();
entityManager.getTransaction().begin();
//查询产品分类
ProductDir productDir = entityManager.find(ProductDir.class, 1L);
// 删除所有和当前对象解除关联关系的对象
productDir.getProducts().clear();
entityManager.getTransaction().commit();
entityManager.close();
}
3.4.级联操作总结
cascade = CascadeType.ALL
CascadeType.ALL包含级联删除,级联保存,但是是在删除的时候比较危险操作,谨慎操作.
4.单向多对多
(1)配置单向多对多
①Role 角色
@Entity
@Table(name="t_role")
public class Role {
@Id
@GeneratedValue
private Long id;
private String name;
}
②User 用户
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
// 多对多:一个用户拥有多个角色
//@JoinTable中间表配置 name属性指定中间表名称, joinColumns:列名 inverseJoinColumns:另外一列。
@ManyToMany
@JoinTable(name = "t_user_role", joinColumns = { @JoinColumn(name = "user_id") },
inverseJoinColumns= {@JoinColumn(name = "role_id") })
private Set<Role> roles = new HashSet<Role>();
会生成一个中间表
(2)保存数据
保存2个用户,保存3个角色(5条)
保存中间表:建立用户到角色关系user1(role1,role2),user2(role1,role2,role3)(5条)
@Test
public void testN() throws Exception{
//创建3个角色,2个用户
Role role1 = new Role();
role1.setName("外卖小哥");
Role role2 = new Role();
role2.setName("五级演员");
Role role3 = new Role();
role3.setName("摄影师");
User user1 = new User();
user1.setName("刘星");
User user2 = new User();
user2.setName("聂顺");
//关联
user1.getRoles().add(role1);
user1.getRoles().add(role2);
user1.getRoles().add(role3);
user2.getRoles().add(role1);
user2.getRoles().add(role2);
//添加
EntityManager entityManager = JpaUtil.getEntityManager();
entityManager.getTransaction().begin();
entityManager.persist(user1);
entityManager.persist(user2);
entityManager.persist(role1);
entityManager.persist(role2);
entityManager.persist(role3);
entityManager.getTransaction().commit();
entityManager.close();
}
执行的SQL
Hibernate: insert into t_user (name) values (?)
Hibernate: insert into t_user (name) values (?)
Hibernate: insert into t_role (name) values (?)
Hibernate: insert into t_role (name) values (?)
Hibernate: insert into t_role (name) values (?)
Hibernate: insert into t_user_role (user_id, role_id) values (?, ?)
Hibernate: insert into t_user_role (user_id, role_id) values (?, ?)
Hibernate: insert into t_user_role (user_id, role_id) values (?, ?)
Hibernate: insert into t_user_role (user_id, role_id) values (?, ?)
Hibernate: insert into t_user_role (user_id, role_id) values (?, ?)
(3)获取数据
@Test
public void get() throws Exception {
EntityManager entityManager = JPAUtils.getEntityManager();
User user = entityManager.find(User.class, 1L);
System.out.println(user);
System.out.println(user.getRoles().getClass());
//执行到这一步的时候才发送Sql语句,因此也是使用的延时加载
System.out.println(user.getRoles());
entityManager.close();
}
5.双向多对多
下面通过案例说明
5.1.常规映射配置
(1)Role 角色
@Entity
@Table(name = "t_role")
public class Role {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToMany
@JoinTable(name = "t_user_role", joinColumns = { @JoinColumn(name = "role_id") },
inverseJoinColumns= { @JoinColumn(name = "user_id") })
private Set<User> users = new HashSet<User>();
}
(2)User 用户
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToMany
@JoinTable(name = "t_user_role", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns= {
@JoinColumn(name = "role_id") })
private Set<Role> roles = new HashSet<Role>();
}
5.2.级联保存
(1)User映射配置
@ManyToMany(cascade = CascadeType.PERSIST)
(2)测试代码
// User:@ManyToMany(cascade = CascadeType.PERSIST)
@Test
public void persist2() throws Exception {
// 保存2个用户,保存3个角色(5条)
User user1 = new User();
User user2 = new User();
Role role1 = new Role();
Role role2 = new Role();
Role role3 = new Role();
// 保存中间表:建立用户到角色关系user1(role1,role2),user2(role1,role2,role3)(5条)
user1.getRoles().add(role1);
user1.getRoles().add(role2);
user2.getRoles().add(role1);
user2.getRoles().add(role2);
user2.getRoles().add(role3);
EntityManager entityManager = JPAUtils.getEntityManager();
entityManager.getTransaction().begin();
// 保存用户的同时,级联保存角色
entityManager.persist(user1);
entityManager.persist(user2);
entityManager.getTransaction().commit();
entityManager.close();
}
5.3.删除user1(由Hibernate自动处理,先删除中间表,再删除user1)
// 注意:User对象是有管理中间表的权限的
@Test
public void delete1() throws Exception {
EntityManager entityManager = JPAUtils.getEntityManager();
entityManager.getTransaction().begin();
User user = entityManager.find(User.class, 1L);
entityManager.remove(user);
entityManager.getTransaction().commit();
entityManager.close();
}
5.4.删除user1的所有包含角色(role1,role2),不能删除user1(只删除中间表)
// 注意:User对象是有管理中间表的权限的
@Test
public void delete2() throws Exception {
EntityManager entityManager = JPAUtils.getEntityManager();
entityManager.getTransaction().begin();
User user = entityManager.find(User.class, 1L);
user.getRoles().clear();
entityManager.getTransaction().commit();
entityManager.close();
}
5.5.删除user1的一个角色,不能删除user1(只删除中间表)
// 注意:User对象是有管理中间表的权限的
@Test
public void delete3() throws Exception {
EntityManager entityManager = JPAUtils.getEntityManager();
entityManager.getTransaction().begin();
User user = entityManager.find(User.class, 1L);
Role role = entityManager.find(Role.class, 1L);
user.getRoles().remove(role);
entityManager.getTransaction().commit();
entityManager.close();
}
5.6.修改角色:先删除在添加
// 注意:User对象是有管理中间表的权限的
// 关系user1(role1,role2)->关系user1(role1,role3)
@Test
public void delete4() throws Exception {
EntityManager entityManager = JPAUtils.getEntityManager();
entityManager.getTransaction().begin();
User user = entityManager.find(User.class, 1L);
Role role2 = entityManager.find(Role.class, 2L);
Role role3 = entityManager.find(Role.class, 3L);
user.getRoles().remove(role2);
user.getRoles().add(role3);
entityManager.getTransaction().commit();
entityManager.close();
}
5.7.级联删除
(1)一边配置(危险)
// User:@ManyToMany(cascade = CascadeType.REMOVE)
//注意:User对象是有管理中间表的权限的
@Test
public void delete5() throws Exception {
EntityManager entityManager = JPAUtils.getEntityManager();
entityManager.getTransaction().begin();
User user = entityManager.find(User.class, 1L);
entityManager.remove(user);
entityManager.getTransaction().commit();
entityManager.close();
}
注意执行的SQL
Hibernate: delete from t_user_role where user_id=?
Hibernate: delete from t_user_role where role_id=?
Hibernate: delete from t_user_role where role_id=?
Hibernate: delete from t_role where id=?
Hibernate: delete from t_role where id=?
Hibernate: delete from t_user where id=?
(2)二边配置(非常危险)
//User:@ManyToMany(cascade = CascadeType.REMOVE)
//Role:@ManyToMany(cascade = CascadeType.REMOVE)
//注意:User,Role对象是有管理中间表的权限的
@Test
public void delete6() throws Exception {
EntityManager entityManager = JPAUtils.getEntityManager();
entityManager.getTransaction().begin();
User user = entityManager.find(User.class, 1L);
entityManager.remove(user);
entityManager.getTransaction().commit();
entityManager.close();
}
}
注意执行的SQL
Hibernate: delete from t_user_role where user_id=?
Hibernate: delete from t_user_role where role_id=?
Hibernate: delete from t_user_role where role_id=?
Hibernate: delete from t_user_role where user_id=?
Hibernate: delete from t_user_role where role_id=?
Hibernate: delete from t_role where id=?
Hibernate: delete from t_role where id=?
Hibernate: delete from t_user where id=?
Hibernate: delete from t_role where id=?
Hibernate: delete from t_user where id=?
这样删除是非常危险的,容易不小心就把所有数据删除(千万要慎用)