zoukankan      html  css  js  c++  java
  • 【java框架】JPA(3) -- JPA映射关系

    1.   单向一对多配置

    单向一对多使用@OneToMany标签进行配置,在一方有一个集合属性与多方进行关联,集合可以是List或者Set,区别是List是有序、Set是无序不重复。

    对应在一方配置@OneToMany:

    /**
     *  单向一对多:使用JPA配置
     */
    @Entity
    @Table(name = "t_productdir")
    public class ProductDir {
        @Id
        @GeneratedValue
        private Integer id;
    
        @Column(name = "dName")
        private String dirName; //产品分类名称
    
        /**
         *  单向一对多:在一方使用集合Set或者List进行多方关系的维护
         *  对于集合,必须要先new出来
         *
         *  对于数据库:不管是多对一,还是一对多,不管是单向还是双向,
         *  数据库的设置都是不变的,哪边是多方,外键就在哪边
         *  单向一对多,默认就是使用的懒加载(以后都把懒加载配置上)
         */
        @OneToMany(fetch = FetchType.LAZY)
        @JoinColumn(name = "dir_id")
        @OrderBy("id desc") //需要排序的时候使用@OrderBy来拿值,多个排序属性之间使用","分割,并且一定要用List集合
        private Set<Product> productSet = new HashSet<>(); //集合默认懒加载
    
        public ProductDir() {
        }

    性能:单向一对多无论配置懒加载还是迫切加载都发送相同数量的SQL语句,性能极差。

    2.   双向一对多、多对一配置

    2.1.基础配置

    双向一对多、多对一需要同时配置两边的属性,一方与多方都要有关联属性存在(同时配置@ManyToOne、@OneToMany),同时在一方放弃关系维护配置mappedBy。具体配置如下:

    多方Product:

    /**
     *  双向一对多、多对一:使用JPA配置
     */
    @Entity
    @Table(name = "t_product")
    public class Product {
        @Id
        @GeneratedValue
        private Integer id;
    
        @Column(name = "t_name")
        private String name; //产品名称
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "dir_id")
        private ProductDir productDir;
    
        public ProductDir getProductDir() {
            return productDir;
        }
    
        public void setProductDir(ProductDir productDir) {
            this.productDir = productDir;
        }
    
        public Product() {
        }
    
    }

    一方ProductDir:

    /**
     *  双向一对多、多对一:使用JPA配置
     */
    @Entity
    @Table(name = "t_productdir")
    public class ProductDir {
        @Id
        @GeneratedValue
        private Integer id;
    
        @Column(name = "dName")
        private String dirName; //产品分类名称
    
        //mappedBy:一方放弃关系维护,把关系维护交给多方,注意mappedBy中的值必须和Product中ProductDir的属性一样
        //注意:使用mappedBy就不要使用JoinColumn了,这边已经不需要维护关系了
        @OneToMany(fetch = FetchType.LAZY, mappedBy = "productDir")
        //@JoinColumn(name = "dir_id")
       // @OrderBy("id desc") //需要排序的时候使用@OrderBy来拿值,多个排序属性之间使用","分割,并且一定要用List集合
        private Set<Product> productSet = new HashSet<>(); //集合默认懒加载
    
        public ProductDir() {
        }
    
    }

    2.2.级联配置

    在一方的多方属性@OneToMany上使用cascade表示级联,主要有以下几种配置方式:

    ①    Cascade=CascadeType.PERSIST:级联保存;

    ②    Cascade=CascadeType.REMOVE:级联删除(很危险);

    ③    Cascade=CascadeType.ALL:级联增删改

    3.   单向多对多

    多对多关系我们以保存2个用户(user)、3个角色(role)来进行多对多的测试;

    用户与角色是多对多的关系,一个用户对应多个角色,一个角色可以由多个用户充当。

    多堆多关系涉及到一张中间表user_role,

    单向关系:通过用户可以找到多个角色,而角色不能找到对应的用户。

    3.1.单向多对多配置

    主表:User用户类

    /**
     * 基于单向多对多,在用户方可以找到对应的角色(角色不能找到对应用户),只需要在用户方配置@ManyToMany注解映射
     */
    @Entity
    @Table(name="t_user")
    public class User {
        @GeneratedValue
        @Id
        private Long id;
    
        private String name;
    
        // @ManyToMany注释表示User是多对多关系的一端。
        // @JoinTable描述了多对多关系的中间表关系。name属性指定中间表名称,
        // joinColumns定义中间表与当前类User的外键关系。inverseJoinColumns定义中间表与关联类Role的外键关系。
        @ManyToMany
        @JoinTable(name = "t_user_role", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = {
                @JoinColumn(name = "role_id") })
        private Set<Role> roles = new HashSet<Role>();
    
        public User() {
        }
    
        public User(String name) {
            this.name = name;
        }
    }

    从表:Role角色类

    @Entity
    @Table(name="t_role")
    public class Role {
        @GeneratedValue
        @Id
        private Long id;
    
        private String name;
    
        public Role() {
        }
    
        public Role(String name) {
            this.name = name;
        }
    }

    对应创建测试类:

    /**
         * 保存2个用户,保存3个角色(5条)
         */
        @Test
    public void persistUserTest() {
        User user1 = new User("user1");
        User user2 = new User("user2");
    
        Role role1 = new Role("role1");
        Role role2 = new Role("role2");
        Role role3 = new Role("role3");
    
        // 保存中间表:建立用户到角色关系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 = JPAUtil.getEntityManager();
        entityManager.getTransaction().begin();
    
        //保存用户
        entityManager.persist(user1);
        entityManager.persist(user2);
        // 保存角色
        entityManager.persist(role1);
        entityManager.persist(role2);
        entityManager.persist(role3);
    
        entityManager.getTransaction().commit();
        JPAUtil.close(entityManager);
    }

    4.   双向多对多

    4.1.双向多堆多配置

    双向多对多即是在两方同时配置@ManyToMany注解,具体配置如下:

    一方Role角色配置:

    @Entity
    @Table(name="t_role")
    public class Role {
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Id
        private Long id;
    
        private String name;
    
        // @ManyToMany注释表示Role是多对多关系的一端。
        @ManyToMany
        //joinColumns表示中间表与当前Role表的连接外键,inverseJoinColumns表示多方User与中间表的外键连接关系
        @JoinTable(name = "t_user_role", joinColumns = { @JoinColumn(name = "role_id") }, inverseJoinColumns = {
                @JoinColumn(name = "user_id") })
        private Set<User> userSet = new HashSet<>();
    
        public Role() {
        }
    
        public Set<User> getUserSet() {
            return userSet;
        }
    
        public void setUserSet(Set<User> userSet) {
            this.userSet = userSet;
        }
    
        public Role(String name) {
            this.name = name;
        }
    
    }

    多方User用户配置:

    /**
     * 基于单向多对多,在用户方可以找到对应的角色(角色不能找到对应用户),只需要在用户方配置@ManyToMany注解映射
     */
    @Entity
    @Table(name="t_user")
    public class User {
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Id
        private Long id;
    
        private String name;
    
        // @ManyToMany注释表示User是多对多关系的一端。
        // @JoinTable描述了多对多关系的中间表关系。name属性指定中间表名称,
        // joinColumns定义中间表与当前类User的外键关系。inverseJoinColumns定义中间表与关联类Role的外键关系。
        @ManyToMany
        @JoinTable(name = "t_user_role", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = {
                @JoinColumn(name = "role_id") })
        private Set<Role> roles = new HashSet<Role>();
    
        public User() {
        }
    
        public User(String name) {
            this.name = name;
        }
    
    }

    5.   一对一配置

    一对一场景在实际项目中用得比较少,这里主要以QQ与QQ空间为例,有QQ才会有QQ空间。

    5.1.唯一外键一对一

    QQ类(主一)

    @Entity
    public class QQ {
      @Id
      @GeneratedValue
      private Long id;
      private String number;
      // 一对一,一个qq号码对应一个qq空间
      @OneToOne(mappedBy="qq")
      private QQZone zone;
    }

    QQ空间类(从一)

    @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;
    }

    5.1.1保存数据测试

    public void persist() throws Exception {
        QQ qq = new QQ();
        qq.setNumber("123456");
    
        QQZone zone = new QQZone();
        zone.setName("枫夜");
    
        // 建立关系
        qq.setZone(zone);
        zone.setQq(qq);
    
        EntityManager entityManager = JPAUtils.getEntityManager();
        entityManager.getTransaction().begin();
    
        // 先保存主一
        entityManager.persist(qq);
        entityManager.persist(zone);
    
        entityManager.getTransaction().commit();
        entityManager.close();
    }

    5.2.共享主键一对一

    QQ空间类(从一)

    @Entity
    public class QQZone {
      @Id
      @GeneratedValue(generator = "fkGenerator")
      @GenericGenerator(name = "fkGenerator", strategy = "foreign", parameters = @Parameter(name = "property", value = "qq"))
      private Long id;
      private String name;
      // 一对一,一个qq空间输入一个qq号码
      @OneToOne(optional = false)
      // 如果不加这个注解,添加QQZone信息时,就会自动在QQZone表中增加了一个外键qq_id
      @PrimaryKeyJoinColumn
      private QQ qq;
    }
  • 相关阅读:
    不测的秘密:精准测试之路----读书笔记(第二章)
    如何使用for循环连续的实例化多个对象!
    java如何在一个有序的数组类插入一个数!
    webstrom 常用快捷键
    如何使Label带有链接??此法感觉有点取巧!!!
    System.DateTime的一些格式
    如何解决”无法将类型为“System.DateTime”的对象强制转换为类型“System.String”。“
    如何解决“连接未关闭。 连接的当前状态为打开”问题
    c语言中 %p的含义
    什么情况下用递归?
  • 原文地址:https://www.cnblogs.com/yif0118/p/12914104.html
Copyright © 2011-2022 走看看