JPA关系

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()打印关连对象,互相调用容易内存溢出
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值