Jpa3

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=?
这样删除是非常危险的,容易不小心就把所有数据删除(千万要慎用)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值