1.单向一对多配置
// 一次性保存一方,同时保存2个多方
@Before
public void persist() throws Exception {
// 一方
ProductDir dir = new ProductDir();
dir.setName("类型1");
// 多方
Product product = new Product();
product.setName("产品1");
Product product2 = new Product();
product2.setName("产品2");
// 连接关系(只能由一方建立到多方的关系)
dir.getProducts().add(product);
dir.getProducts().add(product2);
EntityManager entityManager = JPAUtils.getEntityManager();
entityManager.getTransaction().begin();
// 保存一方(产品)
entityManager.persist(dir);
// 保存多方(产品类型)
entityManager.persist(product2);
entityManager.persist(product);
entityManager.getTransaction().commit();
entityManager.close();
2.保存性能问题分析
/*测试按不同顺序保存数据,生成的sql条数*/
EntityManager entityManager = JpaUtil.get();
Product product = new Product();
product.setName("小时代");
Product product1 = new Product();
product1.setName("盗墓笔记");
ProductDir dir = new ProductDir();
dir.setName("小说");
dir.getProducts().add(product);
dir.getProducts().add(product1);
//先存一方,再存多方 5条sql
entityManager.getTransaction().begin();
/* entityManager.persist(dir);
entityManager.persist(product);
entityManager.persist(product1);*/
//先存多方,再存一方 5条sql
entityManager.persist(product);
entityManager.persist(product1);
entityManager.persist(dir);
entityManager.getTransaction().commit();
entityManager.close();
总结:单向一对多,上面无论先保存哪一方都会生成5条sql,性能比较低,不常用。
3.获取代码分析
/*测试默认是LAZY加载还是EAGER 加载*/
EntityManager entityManager = JpaUtil.get();
ProductDir product = entityManager.find(ProductDir.class, 1L);
System.out.println(product);
//查询一方(产品类型)
/* ProductDir dir = entityManager.find(ProductDir.class, 1L);
//System.out.println(dir.getProducts());
//判断产品分类中是否有产品 dir.getProducts().size >0
if (dir.getProducts().size()>0){
System.out.println("两本小说已经存入数据库");
}else {
System.out.println("没有一本小说");
}*/
总结:单向一对多默认是LAZY加载方式,具体需要如何加载就自己配置
@ManyToOne(fetch=FetchType.LAZY)
@ManyToOne(fetch=FetchType. EAGER)
4.集合映射
/测试集合映射的类型/
EntityManager entityManager = JpaUtil.get();
ProductDir dir = entityManager.find(ProductDir.class, 1L);
System.out.println(dir.getProducts().getClass());
//org.hibernate.collection.internal.PersistentSet 继承自Set
//org.hibernate.collection.internal.PersistentBag 继承自List
总结:在配置集合时,需要使用父接口来接收
这里必须使用List 或Set来接收 因为products最后需要转化为PersistentBag/Persistentset
Set:不能重复,集合里面的元素没有顺序
List:可以重复,集合里面的元素有顺序
private List<Product> products = new ArrayList<>();
private Set<Product> products = new HashSet<>();
小结: 1. 配置集合时必须使用接口
2. 一般使用Set接口,只有组合关系时使用List
3. 使用List接口的时候可以配置@Order进行排序
@OneToMany
@JoinColumn(name = "dir_id")
@OrderBy("price DESC")
private List<ProductSet> products = new ArrayList<ProductSet>(); = new HashSet<ProductSet>();
5.双向多对一或双向一对多
1.添加数据的性能优化
选择一:在java代码中让多方来维护关系(性能高),类似于单向多对一
选择二:在java 代码中让一方来维护关系(性能低-如下面的代码),类似于单向一对多。
‘ 总结:使用一方来维护关系,生成sql语句比使用多方维护时多,效率较低
2.使用双向映射的正确配置
@Entity
public class ProductDir {
@Id
@GeneratedValue
private Long id;
private String name;
// mappedBy = "dir"表示一方的关系参照多方Prodcut属性dir来管理
@OneToMany( mappedBy = "dir")
// 建议实例化,用的时候不需要在实例化,这里和单向多对一要求不同
private Set<Product2> products = new HashSet<Product2>();
@Entity
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
// 多Product对一ProductDir
// 多个产品属于一个产品类型
// 外键在那个表,这个表就是多
@ManyToOne(fetch = FetchType.LAZY) // 实现延迟加载
// JoinColum设置了外键的名字,不配置默认一方属性名_id
@JoinColumn(name = "dir_id")
// 这里进行了实例(这种写法是错误的,我们一定不能在这里进行实例化)
private ProductDir dir;
6.级联保存/删除
@OneToMany(orphanRemoval=true,mappedBy = "dir")
/*配置外键ID*/
// 1.mappedBy = "dir"表示一方的关系参照多方Prodcut属性dir来管理
// 2.cascade = CascadeType.PERSIST
// 3.必须2边都建立关系
// 4.entityManager.persist(dir);只需要保存一方,多方自动保存
代码示例
EntityManager entityManager = JpaUtil.get();
Product product = new Product();
product.setName("西红柿");
Product product1 = new Product();
product1.setName("南瓜");
ProductDir dir = new ProductDir();
dir.setName("蔬菜");
//建立多方与一方的关系
product.setDir(dir);
product1.setDir(dir);
//再建立一方与多方的关系
dir.getProducts().add(product);
dir.getProducts().add(product1);
// 一次性保存一方,查看是否会同时保存2个多方
entityManager.getTransaction().begin();
entityManager.persist(dir);
entityManager.getTransaction().commit();
entityManager.close();
映射配置cascade = CascadeType.REMOVE
/*测试级联删除
*
* 删除一方,然后会级联删除多方【比较危险】
* 会删除跟这一方相关的多方
* */
EntityManager entityManager = JpaUtil.get();
ProductDir dir = entityManager.find(ProductDir.class, 1L);
entityManager.getTransaction().begin();
entityManager.remove(dir);
entityManager.getTransaction().commit();
entityManager.close();
孤儿删除
/* orphanRemoval=true 从一方删除接触关系的多方
测试孤儿删除,删除一方解除了关系的多方
*/
EntityManager entityManager = JpaUtil.get();
//查找出一方
ProductDir dir = entityManager.find(ProductDir.class, 2L);
//查找出需要解除关系的多方
Product product = entityManager.find(Product.class, 4L);
entityManager.getTransaction().begin();
dir.getProducts().remove(product);
entityManager.getTransaction().commit();
entityManager.close();
}
从一方去删除所有解除关系的多方:先获取一方,在删除所有多方
/* orphanRemoval=true 从一方删除接触关系的多方
先获取一方,在删除所有多方
*/
EntityManager entityManager = JpaUtil.get();
//查找出一方
ProductDir dir = entityManager.find(ProductDir.class, 2L);
//查找出需要解除关系的多方
entityManager.getTransaction().begin();
dir.getProducts().clear();
entityManager.getTransaction().commit();
entityManager.close();
总结:cascade = CascadeType.ALL 支持所有级联操作
7.单向多对多
A. 会产生一个中间表
B. 默认是LAZY加载
配置与测试代码
/*@Entity*/
@Table(name = "t_student")
public class Student {
@Id
@GeneratedValue
private Long id;
private String name;
@JoinTable(name = "t_teacher_stu",joinColumns = @JoinColumn(name = "sid"),inverseJoinColumns =@JoinColumn(name = "tid") )
@ManyToMany(cascade = CascadeType.REMOVE)
private Set<Teacher> teachers = new HashSet<>();
/*@Entity*/
@Table(name = "t_teacher")
public class Teacher {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToMany(cascade = CascadeType.REMOVE)
@JoinTable(name = "t_teacher_stu",joinColumns = @JoinColumn(name = "tid"),inverseJoinColumns =@JoinColumn(name = "sid") )
private Set<Student> students = new HashSet<>();
测试代码
@Test
public void testManytoMany(){
EntityManager entityManager = JpaUtil.get();
//保存2个用户,保存3个角色 观察生成表的情况
User user = new User();
user.setName("黑色玫瑰");
User user1 = new User();
user1.setName("艾欧尼亚");
Role role = new Role();
role.setName("放逐之刃");
Role role1 = new Role();
role1.setName("疾风剑豪");
Role role2 = new Role();
role2.setName("寒冰射手");
user.getRoles().add(role);
user.getRoles().add(role1);
//给用户2添加3 角色
user1.getRoles().add(role);
user1.getRoles().add(role1);
user1.getRoles().add(role2);
//将2个用户和 3个角色各自存入数据库
entityManager.getTransaction().begin();
entityManager.persist(user);
entityManager.persist(user1);
//存角色
entityManager.persist(role);
entityManager.persist(role1);
entityManager.persist(role2);
entityManager.getTransaction().commit();
entityManager.close();
};
@Test
public void testFind(){
/*
* 测试查询
* */
EntityManager entityManager = JpaUtil.get();
User user = entityManager.find(User.class, 1L);
//System.out.println(user); 1条sql 得到结论 默认是LAZY加载
//System.out.println(user.getRoles().size()); 2条sql
entityManager.close();
8.双向多对多
- 双向多对多需要在配置中间表的时候,表名要一致
- @JoinTable描述了多对多关系的数据表关系。name属性指定中间表名称,joinColumns定义中间表与Teacher表的外键关系,inverseJoinColumns表示另一个外键。
- 配置详情
public class Student {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToMany(cascade = CascadeType.REMOVE)
@JoinTable(name = "t_teacher_stu",joinColumns = @JoinColumn(name = "tid"),inverseJoinColumns =@JoinColumn(name = "sid") )
private Set<Student> students = new HashSet<>();
public class Student {
@Id
@GeneratedValue
private Long id;
private String name;
@JoinTable(name = "t_teacher_stu",joinColumns = @JoinColumn(name = "sid"),inverseJoinColumns =@JoinColumn(name = "tid") )
@ManyToMany(cascade = CascadeType.REMOVE)
private Set<Teacher> teachers = new HashSet<>();
- 双向多对多级联保存 只需要任意保存一方,另一方也会被保存进数据库
- 双向多对多级联删除【@ManyToMany(cascade = CascadeType.REMOVE)】只配置在一方时
跟删除对象有连带关系的全部被删除 - 【@ManyToMany(cascade = CascadeType.REMOVE)】只配置在双方时,跟双方有联系全部删除,基本会清空表, 慎用!
9.一对一
1. 唯一外键一对一
@Table(name = "t_qq")
public class QQ {
@Id
@GeneratedValue
private Long id;
private String qqnum;
@OneToOne(mappedBy = "qq")
private QQZone zone;
public class QQZone {
@Id
//配置共享主键
@GeneratedValue(generator = "pkGenerator")
/* @GenericGenerator(name = "pkGenerator", strategy = "foreign",parameters = @Parameter(name = "property", value = "qq"))*/
@GenericGenerator(name ="pkGenerator",strategy ="foreign",parameters =@org.hibernate.annotations.Parameter(name = "property",value = "qq"))
private Long id;
private String name;
// 一对一,一个qq空间输入一个qq号码
// 默认值optional = true表示qq_id可以为空;反之。。。
@OneToOne(optional = false)
// unique=true确保了一对一关系
@PrimaryKeyJoinColumn//有这个配置就不会再次生成主键
private QQ qq;