JPA 映射关系

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.双向多对多

  1. 双向多对多需要在配置中间表的时候,表名要一致
  2. @JoinTable描述了多对多关系的数据表关系。name属性指定中间表名称,joinColumns定义中间表与Teacher表的外键关系,inverseJoinColumns表示另一个外键。
  3. 配置详情
   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<>();
  1. 双向多对多级联保存 只需要任意保存一方,另一方也会被保存进数据库
  2. 双向多对多级联删除【@ManyToMany(cascade = CascadeType.REMOVE)】只配置在一方时
    跟删除对象有连带关系的全部被删除
  3. 【@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;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值