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

猜你喜欢

转载自www.cnblogs.com/yif0118/p/12914104.html
今日推荐