zoukankan      html  css  js  c++  java
  • Spring Data JPA 之 一对一,一对多,多对多 关系映射

    一、@OneToOne关系映射

    JPA使用@OneToOne来标注一对一的关系。

    实体 People :用户。

    实体 Address:家庭住址。

    People 和 Address 是一对一的关系。

    这里用两种方式描述JPA的一对一关系。

    一种是通过外键的方式(一个实体通过外键关联到另一个实体的主键);

    另外一种是通过一张关联表来保存两个实体一对一的关系。

    1、通过外键的方式

    people 表(id,name,sex,birthday,address_id)

    address 表(id,phone,zipcode,address)

    People.java

     
    1. @Entity
    2. public class People {
    3.     @Id
    4.     @GeneratedValue(strategy = GenerationType.IDENTITY)
    5.     @Column(name = "id", nullable = false)
    6.     private Long id;//id
    7.     @Column(name = "name", nullable = true, length = 20)
    8.     private String name;//姓名
    9.     @Column(name = "sex", nullable = true, length = 1)
    10.     private String sex;//性别
    11.     @Column(name = "birthday", nullable = true)
    12.     private Timestamp birthday;//出生日期
    13.     @OneToOne(cascade=CascadeType.ALL)//People是关系的维护端,当删除 people,会级联删除 address
    14.     @JoinColumn(name = "address_id", referencedColumnName = "id")//people中的address_id字段参考address表中的id字段
    15.     private Address address;//地址
    16. }

    关联的实体的主键一般是用来做外键的。但如果此时不想主键作为外键,则需要设置referencedColumnName属性。当然这里关联实体(Address)的主键 id 是用来做主键,所以这里第20行的 referencedColumnName = "id" 实际可以省略。

    Address.java

     
    1. @Entity
    2. public class Address {
    3.     @Id
    4.     @GeneratedValue(strategy = GenerationType.IDENTITY)
    5.     @Column(name = "id", nullable = false)
    6.     private Long id;//id
    7.     @Column(name = "phone", nullable = true, length = 11)
    8.     private String phone;//手机
    9.     @Column(name = "zipcode", nullable = true, length = 6)
    10.     private String zipcode;//邮政编码
    11.     @Column(name = "address", nullable = true, length = 100)
    12.     private String address;//地址
    13.     //如果不需要根据Address级联查询People,可以注释掉
    14. //    @OneToOne(mappedBy = "address", cascade = {CascadeType.MERGE, CascadeType.REFRESH}, optional = false)
    15. //    private People people;
    16. }

    2、通过关联表的方式来保存一对一的关系。

    people 表(id,name,sex,birthday)

    address 表 (id,phone,zipcode,address)

    people_address (people_id,address_id)

    只需要创建 People 和 Address 两个实体

    People.java

     
    1. @Entity
    2. public class People {
    3.     @Id
    4.     @GeneratedValue(strategy = GenerationType.IDENTITY)
    5.     @Column(name = "id", nullable = false)
    6.     private Long id;//id
    7.     @Column(name = "name", nullable = true, length = 20)
    8.     private String name;//姓名
    9.     @Column(name = "sex", nullable = true, length = 1)
    10.     private String sex;//性别
    11.     @Column(name = "birthday", nullable = true)
    12.     private Timestamp birthday;//出生日期
    13.     @OneToOne(cascade=CascadeType.ALL)//People是关系的维护端
    14.     @JoinTable(name = "people_address",
    15.             joinColumns = @JoinColumn(name="people_id"),
    16.             inverseJoinColumns = @JoinColumn(name = "address_id"))//通过关联表保存一对一的关系
    17.     private Address address;//地址
    18. }

    Address.java

    不变

    二、@OneToMany 和 @ManyToOne

    实体 Author:作者。

    实体 Article:文章。

    Author 和 Article 是一对多关系(双向)。那么在JPA中,如何表示一对多的双向关联呢?

    JPA使用@OneToMany和@ManyToOne来标识一对多的双向关联。一端(Author)使用@OneToMany,多端(Article)使用@ManyToOne。

    JPA规范中,一对多的双向关系由多端(Article)来维护。就是说多端(Article)为关系维护端,负责关系的增删改查。一端(Author)则为关系被维护端,不能维护关系。

    一端(Author)使用@OneToMany注释的mappedBy="author"属性表明Author是关系被维护端。

    多端(Article)使用@ManyToOne和@JoinColumn来注释属性 author,@ManyToOne表明Article是多端,@JoinColumn设置在article表中的关联字段(外键)。

    Author.java

     
    1. @Entity
    2. public class Author {
    3.     @Id // 主键
    4.     @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略
    5.     private Long id; //id
    6.     @NotEmpty(message = "姓名不能为空")
    7.     @Size(min=2, max=20)
    8.     @Column(nullable = false, length = 20)
    9.     private String name;//姓名
    10.     @OneToMany(mappedBy = "author",cascade=CascadeType.ALL,fetch=FetchType.LAZY)
    11.     //级联保存、更新、删除、刷新;延迟加载。当删除用户,会级联删除该用户的所有文章
    12.     //拥有mappedBy注解的实体类为关系被维护端
    13.      //mappedBy="author"中的author是Article中的author属性
    14.     private List<Article> articleList;//文章列表
    15. }

    Article.java

     
    1. @Entity
    2. public class Article {
    3.     @Id
    4.     @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略
    5.     @Column(name = "id", nullable = false)
    6.     private Long id;
    7.     @NotEmpty(message = "标题不能为空")
    8.     @Size(min = 2, max = 50)
    9.     @Column(nullable = false, length = 50) // 映射为字段,值不能为空
    10.     private String title;
    11.     @Lob  // 大对象,映射 MySQL 的 Long Text 类型
    12.     @Basic(fetch = FetchType.LAZY) // 懒加载
    13.     @NotEmpty(message = "内容不能为空")
    14.     @Size(min = 2)
    15.     @Column(nullable = false) // 映射为字段,值不能为空
    16.     private String content;//文章全文内容
    17.     @ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false)//可选属性optional=false,表示author不能为空。删除文章,不影响用户
    18.     @JoinColumn(name="author_id")//设置在article表中的关联字段(外键)
    19.     private Author author;//所属作者
    20. }

    最终生成的表结构

    article 表(id,title,conten,author_id)

    author 表(id,name)

    三、多对多 @ManyToMany

    实体 User:用户。

    实体 Authority:权限。

    用户和权限是多对多的关系。一个用户可以有多个权限,一个权限也可以被很多用户拥有。

    JPA中使用@ManyToMany来注解多对多的关系,由一个关联表来维护。这个关联表的表名默认是:主表名+下划线+从表名。(主表是指关系维护端对应的表,从表指关系被维护端对应的表)。这个关联表只有两个外键字段,分别指向主表ID和从表ID。字段的名称默认为:主表名+下划线+主表中的主键列名,从表名+下划线+从表中的主键列名。

    需要注意的:

    1、多对多关系中一般不设置级联保存、级联删除、级联更新等操作。

    2、可以随意指定一方为关系维护端,在这个例子中,我指定 User 为关系维护端,所以生成的关联表名称为: user_authority,关联表的字段为:user_id 和 authority_id。

    3、多对多关系的绑定由关系维护端来完成,即由 User.setAuthorities(authorities) 来绑定多对多的关系。关系被维护端不能绑定关系,即Game不能绑定关系。

    4、多对多关系的解除由关系维护端来完成,即由Player.getGames().remove(game)来解除多对多的关系。关系被维护端不能解除关系,即Game不能解除关系。

    5、如果 User 和 Authority 已经绑定了多对多的关系,那么不能直接删除 Authority,需要由 User 解除关系后,才能删除 Authority。但是可以直接删除 User,因为 User 是关系维护端,删除 User 时,会先解除 User 和 Authority 的关系,再删除 Authority。

    User.java

     
    1. @Entity
    2. public class User {
    3.     @Id
    4.     @GeneratedValue(strategy = GenerationType.IDENTITY)
    5.     private Long id;
    6.     @NotEmpty(message = "账号不能为空")
    7.     @Size(min=3, max=20)
    8.     @Column(nullable = false, length = 20, unique = true)
    9.     private String username; // 用户账号,用户登录时的唯一标识
    10.     @NotEmpty(message = "密码不能为空")
    11.     @Size(max=100)
    12.     @Column(length = 100)
    13.     private String password; // 登录时密码
    14.     @ManyToMany
    15.     @JoinTable(name = "user_authority",joinColumns = @JoinColumn(name = "user_id"),
    16.     inverseJoinColumns = @JoinColumn(name = "authority_id"))
    17.     //1、关系维护端,负责多对多关系的绑定和解除
    18.     //2、@JoinTable注解的name属性指定关联表的名字,joinColumns指定外键的名字,关联到关系维护端(User)
    19.     //3、inverseJoinColumns指定外键的名字,要关联的关系被维护端(Authority)
    20.     //4、其实可以不使用@JoinTable注解,默认生成的关联表名称为主表表名+下划线+从表表名,
    21.     //即表名为user_authority
    22.     //关联到主表的外键名:主表名+下划线+主表中的主键列名,即user_id
    23.     //关联到从表的外键名:主表中用于关联的属性名+下划线+从表的主键列名,即authority_id
    24.     //主表就是关系维护端对应的表,从表就是关系被维护端对应的表
    25.     private List<Authority> authorityList;
    26. }

    注意:如注释中所言,上面的第20-21行的@JoinTable可以省略,默认可以生成

    Authority.java

     
    1. @Entity
    2. public class Authority {
    3.     @Id
    4.     @GeneratedValue(strategy = GenerationType.IDENTITY)
    5.     private Integer id;
    6.     @Column(nullable = false)
    7.     private String name; //权限名
    8.     @ManyToMany(mappedBy = "authorityList")
    9.     private List<User> userList;
    10. }

    测试 添加

     
    1. @SpringBootTest
    2. @RunWith(SpringRunner.class)
    3. public class UserRepositoryTest {
    4.     @Autowired
    5.     private UserRepository userRepository;
    6.     @Autowired
    7.     private AuthorityRepository authorityRepository;
    8.     @Test
    9.     public void saveAuthority() {
    10.         Authority authority = new Authority();
    11.         authority.setId(1);
    12.         authority.setName("ROLE_ADMIN");
    13.         authorityRepository.save(authority);
    14.     }
    15.     @Test
    16.     public void saveUser() {
    17.         User user = new User();
    18.         user.setUsername("admin");
    19.         user.setPassword("123456");
    20.         Authority authority = authorityRepository.findById(1).get();
    21.         List<Authority> authorityList = new ArrayList<>();
    22.         authorityList.add(authority);
    23.         user.setAuthorList(authorityList);
    24.         userRepository.save(user);
    25.     }
    26. }

    先运行 saveAuthority 添加一条权限记录,

    然后运行 saveUser 添加一条用户记录,与此同时,user_authority 表中也自动插入了一条记录

    测试 删除

    删除用户

     
    1. @SpringBootTest
    2. @RunWith(SpringRunner.class)
    3. public class UserRepositoryTest {
    4.     @Autowired
    5.     private UserRepository userRepository;
    6.     @Test
    7.     public void deleteUser() {
    8.         userRepository.deleteById(1L);
    9.     }
    10. }

    user 表中删除一条记录,同时 user_authority 能够级联删除一条记录

    参考:http://www.cnblogs.com/luxh/archive/2012/05/30/2527123.html

    再次更新

    其中 @OneToMany  和 @ManyToOne 用得最多,这里再补充一下

    关于级联,一定要注意,要在关系的维护端,即 One 端。

    比如 作者和文章,作者是One,文章是Many;文章和评论,文章是One,评论是Many。

    cascade = CascadeType.ALL 只能写在 One 端,只有One端改变Many端,不准Many端改变One端。

    特别是删除,因为 ALL 里包括更新,删除。

    如果删除一条评论,就把文章删了,那算谁的。所以,在使用的时候要小心。一定要在 One 端使用。

    举例

    One 端

    Spring Data JPA 之 一对一,一对多,多对多 关系映射

    Many 端

    本文转自:Spring Data JPA 之 一对一,一对多,多对多 关系映射 | 言曌博客

  • 相关阅读:
    无限维
    黎曼流形
    why we need virtual key word
    TOJ 4119 Split Equally
    TOJ 4003 Next Permutation
    TOJ 4002 Palindrome Generator
    TOJ 2749 Absent Substrings
    TOJ 2641 Gene
    TOJ 2861 Octal Fractions
    TOJ 4394 Rebuild Road
  • 原文地址:https://www.cnblogs.com/JAYIT/p/12889666.html
Copyright © 2011-2022 走看看