JPA关系
一、单向一对多
- 性能非常差,不建议使用
- 如果要使用,建议使用双向的多对一、一对多
- 默认是懒加载
(一)代码配置
- 必需使用接口声明
- 可以使用List(有序允许重复)或者Set,必需把它new出来
- 一定要指定外键(否则会出现中间表)
@OneToMany
@JoinColumn(name = "dir_id")
private List<Product> products = new ArrayList<>();
(二)保存代码
public void testSave() throws Exception{
//准备多方
Product p1 = new Product();
p1.setName("西游记");
Product p2 = new Product();
p2.setName("东游记");
//准备一方
ProductDir dir = new ProductDir();
dir.setName("游记");
//建立关系
dir.getProducts().add(p1);
dir.getProducts().add(p2);
EntityManager entityManager = JPAUtil.createEntityManager();
entityManager.getTransaction().begin();
/**
* 3条SQL是最合理的
* 先保存一方,再保存多方 -> 5条
* 先保存多方,再保存一方 -> 5条
* 原因:外键是由一方来处理,一方处理必须发出额外的update语句
*/
entityManager.persist(p1);
entityManager.persist(p2);
entityManager.persist(dir);
entityManager.getTransaction().commit();
entityManager.close();
}
(三)判断集合是否有值
public void testFind2() throws Exception{
EntityManager entityManager = JPAUtil.createEntityManager();
//获取一方
ProductDir dir = entityManager.find(ProductDir.class, 1L);
//判断:一方有没有对应的多方,集合需要判断长度
if(dir.getProducts().size() > 0){
System.out.println("有货!!!!");
}
entityManager.close();
}
二、集合映射
- Hibernate提供的类:实现延迟加载的关键类
- org.hibernate.collection.internal.PersistentSet 实现了java.util.Set接口
- org.hibernate.collection.internal.PersistentBag 实现了java.util.List接口
public void test() throws Exception {
EntityManager entityManager = JPAUtils.getEntityManager();
// 拿到一方的数据
ProductDirSet dir = entityManager.find(ProductDirSet.class, 1L);
// 打印产品类别
System.out.println(dir);//class org.hibernate.collection.internal.PersistentList
System.out.println(dir.getProducts().getClass());
entityManager.close();
}
选择:set一般用在多对多,也有一对多
list一般只是用在组合关系(使用List接口的时候可以配置@Order进行排序)
三、双向多对一(双向一对多)
- 双向配置的表结构和单向是完全一样的
- 尽量使用多方来维护关系(性能好)
- 外键在哪边,哪边就是多方(也方便维护)
@Entity
@Table(name = "employee")
public class Employee {
@Id
@GeneratedValue
private Long id;
/*多个员工属于一个部门*/
@ManyToOne
@JoinColumn(name = "department_id")
private Department department;
@Entity
@Table(name = "department")
public class Department {
@Id
@GeneratedValue
private Long id;
/*一个部门有多个员工
*确定外键关连的名称叫:department_id
* 保证维护关系的列是同一列
* mappedBy = "department" :映射的关系交给Employee类中的department的属性去维护
* Department类放弃了关系维护
*/
@OneToMany(mappedBy = "department")
private List<Employee> employees = new ArrayList<>();
四、级联
- 级联很危险,不要随便使用
- 组合关系(单据)必需使用级联
- 要使用就是使用最强级联:
cascade = CascadeType.ALL,orphanRemoval = true
cascade : 级联
* CascadeType.PERSIST -> 保存
* CascadeType.MERGE -> 修改
* CascadeType.REMOVE -> 删除
* CascadeType.ALL -> 包含级联增删改
* orphanRemoval : 孤儿删除(可以从一方删除解除关系)
1.级联保存
@OneToMany(mappedBy = "department",cascade = CascadeType.PERSIST)
private List<Employee> employees = new ArrayList<>();
2.级联删除:删除一方,然后级联删除多方
@OneToMany(mappedBy = "department",cascade = CascadeType.REMOVE)
private List<Employee> employees = new ArrayList<>();
3.先获取一方,在删除一个多方
@OneToMany(mappedBy = "department",cascade = CascadeType.REMOVE,orphanRemoval = true)
// @JoinColumn(name = "department_id")
private List<Employee> employees = new ArrayList<>();
4.先获取一方,在删除所有多方
@OneToMany(mappedBy = "department",cascade = CascadeType.ALL,orphanRemoval = true)
// @JoinColumn(name = "department_id")
private List<Employee> employees = new ArrayList<>();
五、单向多对多
(一)代码配置
/*
* 这里默认是延时加载(集合都是懒加载)
* @JoinTable:指的是关连(中间)表
* name:中间表的名称
* joinColumns:当前类(表)对应的外键名称
* inverseJoinColumns:关连的类(表)对应的外键名称
*/
@ManyToMany
@JoinTable(name = "t_user_role",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private List<Role> roles = new ArrayList<>();
(二)保存代码
public void testSave() throws Exception{
EntityManager entityManager = JPAUtil.createEntityManager();
//准备2个用户
User u1 = new User();
u1.setName("无忌孩儿");
User u2 = new User();
u2.setName("三丰");
//准备三个角色
Role r1 = new Role();
r1.setName("少林");
Role r2 = new Role();
r2.setName("武当");
Role r3 = new Role();
r3.setName("明教");
//建立关系
// 无忌 -> 武当,明教
u1.getRoles().add(r2);
u1.getRoles().add(r3);
// 三丰 -> 少林,武当,明教
u2.getRoles().add(r1);
u2.getRoles().add(r2);
u2.getRoles().add(r3);
/*执行10条SQL*/
EntityManager entityManager = JPAUtil.createEntityManager();
entityManager.getTransaction().begin();
//再保存用户
entityManager.persist(u1);
entityManager.persist(u2);
//先保存角色
entityManager.persist(r1);
entityManager.persist(r2);
entityManager.persist(r3);
entityManager.getTransaction().commit();
entityManager.close();
}
六、双向多对多
- 一定要自己去控制中间表的表名与列名
- 正常操作都没有问题(尽量不要使用级联)
- 双向保证表名列名是一致的
(一)代码配置
@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 List<Role> roles = new ArrayList<>();
@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 List<User> users = new ArrayList<>();
(二)级联
1.删除user1的所有包含角色(role1,role2),不能删除user1
- 由JPA自动处理,先删除中间表,在删除user1
public void testDelete01() throws Exception{
EntityManager entityManager = JPAUtil.createEntityManager();
entityManager.getTransaction().begin();
User user = entityManager.find(User.class, 1L);
entityManager.remove(user);
entityManager.getTransaction().commit();
entityManager.close();
}
2.级联删除(一边配置,危险)
- 只有一边配置了@ManyToMany(cascade = CascadeType.REMOVE)
- 比如删除武当,只会删除跟武当相关的
3.级联删除(二边配置,非常危险)
- 两边都配置了@ManyToMany(cascade = CascadeType.REMOVE)
- 容易不小心就把所有数据删除
七、一对一
- 共享主键(扩展性不强,不建议使用)
- 唯一外键(扩展性强,建议使用)
- 外键在哪边,哪边就是从表
@Entity
public class QQ {
@Id
@GeneratedValue
private Long id;
private String number;
// 一对一,一个qq号码对应一个qq空间
@OneToOne(mappedBy="qq")
private QQZone zone;
}
@Entity
public class QQZone {
@Id
@GeneratedValue
private Long id;
private String name;
// 一对一,一个qq空间输入一个qq号码
// 默认值optional = true表示qq_id可以为空;反之。。。
@OneToOne(optional = false)
// unique=true确保了一对一关系
@JoinColumn(name = "qq_id", unique = true)
private QQ qq;
}
八、关系总结
- 单向多对一、一对多、双向多对一(一对多)表结构完成一样
- 单向一对多性能差(不用)
- 声明的时候都用接口
- 集合先new出来(以免以后代码麻烦)
- JPA集合默认懒加载
- 想要性能好先一后多
- 先配置单向(业务需要再配置双向)
- 不要在toString()打印关连对象,互相调用容易内存溢出