当前位置:   article > 正文

Spring Data JPA实现多表的关联查询_springdatajpa多表关联查询

springdatajpa多表关联查询

1、Spring Data JPA关系映射

对象关系映射(Object relational mapping)是指通过将对象状态映射到数据库列,来开发和维护对象和关系数据库之间的关系。它能够轻松处理(执行)各种数据库操作,如插入、更新、删除等。

关系映射的注解:

注解说明
@JoinColumn指定一个实体组织或实体集合。用在“多对一”和“一对多”的关联中。
@OneToOne定义表之间“一对一”的关系。
@OneToMany定义表之间“一对多”的关系。
@ManyToOne定义表之间“多对一”的关系。
@ManyToMany定义表之间“多对多”的关系。

下面将介绍如何使用Spring Data JPA处理多张数据库表之间的关联关系。

 

2、创建项目与配置信息

(1)创建SpringBoot项目

创建SpringBoot项目,并创建项目结构:dao(数据访问层)、entity(实体层)、test(测试层)。

(2)添加JPA和MySQL数据库的依赖

在pom.xml配置文件中,添加如下依赖:

  1. <!-- Spring Data JPA -->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-data-jpa</artifactId>
  5. </dependency>
  6. <!-- MySQL的JDBC数据库驱动 -->
  7. <dependency>
  8. <groupId>mysql</groupId>
  9. <artifactId>mysql-connector-java</artifactId>
  10. <version>8.0.19</version>
  11. </dependency>

(3)配置数据库连接信息

SpringBoot项目使用MySQL等关系型数据库,需要配置连接信息。

将默认的application.properties文件的后缀修改为“.yml”,即配置文件名称为:application.yml,并配置以下MySQL数据库的连接信息:

  1. spring:
  2. datasource:
  3. url: jdbc:mysql://localhost:3306/db_admin?useSSL=false&amp
  4. username: root
  5. password: 123456
  6. driver-class-name: com.mysql.cj.jdbc.Driver
  7. jpa:
  8. database: MYSQL
  9. show-sql: true
  10. open-in-view: true
  11. properties:
  12. hibernate:
  13. enable_lazy_load_no_trans: true #使用延时加载时控制Session的生命周期
  14. dialect: org.hibernate.dialect.MySQL5Dialect
  15. ddl-auto: update

 

3、延时加载时控制Session的生命周期

在Spring Data JPA中在使用延时加载时,要控制Session的生命周期,否则会出现“could not initialize proxy [xxxxxx#1] - no Session”错误。可以在配置文件中配置以下代码来控制Session的生命周期:

application.properties配置文件:

  1. spring.jpa.open-in-view=true
  2. spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true

application.yml配置文件:

 

4、一对一查询

一对一外键关联的配置比较简单,以公民实体对象和身份证实体对象为例,在表tab_people(公民表)中添加一个字段“card_id”,作为该表的外键,同时需要保证该字段的唯一性,否则就不是一对一映射关系了,而是一对多映射关系。表tab_people和tab_idcard(身份证表)之间的关联关系如下图所示。

【示例】一对一关联,获取公民信息与身份证号码。

(1)在MySQL数据库创建tab_people表和tab_idcard表,并添加相关数据。

  1. -- 判断数据表是否存在,存在则删除
  2. DROP TABLE IF EXISTS tab_people;
  3. DROP TABLE IF EXISTS tab_idcard;
  4. -- 创建“身份证信息”数据表
  5. CREATE TABLE IF NOT EXISTS tab_idcard
  6. (
  7. id INT AUTO_INCREMENT PRIMARY KEY COMMENT '身份证ID',
  8. idCard_code VARCHAR(45) COMMENT '身份证号码'
  9. ) COMMENT = '身份证信息表';
  10. -- 创建“公民信息”数据表
  11. CREATE TABLE IF NOT EXISTS tab_people
  12. (
  13. id INT AUTO_INCREMENT PRIMARY KEY COMMENT '公民ID',
  14. NAME VARCHAR(45) NOT NULL COMMENT '公民名称',
  15. sex VARCHAR(2) COMMENT '公民性别',
  16. age INT COMMENT '公民年龄',
  17. card_id INT UNIQUE COMMENT '身份证ID',
  18. -- 创建外键约束
  19. FOREIGN KEY fk_card_id (card_id)
  20. REFERENCES tab_idcard(id)
  21. ) COMMENT = '公民信息表';
  22. -- 添加数据
  23. INSERT INTO tab_idcard(idCard_code) VALUE('123456789');
  24. INSERT INTO tab_people(NAME,sex,age,card_id) VALUES('pan_junbiao的博客','男',32,1);

(2)创建名称为People.java公民信息的持久化类。

  1. package com.pjb.jpauserdemo.entity;
  2. import javax.persistence.*;
  3. /**
  4. * 公民信息的持久化类
  5. * @author pan_junbiao
  6. **/
  7. @Entity
  8. @Table(name = "tab_people")
  9. public class People
  10. {
  11. //公民ID
  12. @Id
  13. @GeneratedValue(strategy = GenerationType.IDENTITY)
  14. @Column(name = "id")
  15. private int id;
  16. //公民名称
  17. @Column(name = "name")
  18. private String name;
  19. //公民性别
  20. @Column(name = "sex")
  21. private String sex;
  22. //公民年龄
  23. @Column(name = "age")
  24. private int age;
  25. //关联的身份证对象
  26. @OneToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
  27. @JoinColumn(name="card_id")
  28. private IDcard idcard;
  29. public int getId()
  30. {
  31. return id;
  32. }
  33. public void setId(int id)
  34. {
  35. this.id = id;
  36. }
  37. public String getName()
  38. {
  39. return name;
  40. }
  41. public void setName(String name)
  42. {
  43. this.name = name;
  44. }
  45. public String getSex()
  46. {
  47. return sex;
  48. }
  49. public void setSex(String sex)
  50. {
  51. this.sex = sex;
  52. }
  53. public int getAge()
  54. {
  55. return age;
  56. }
  57. public void setAge(int age)
  58. {
  59. this.age = age;
  60. }
  61. public IDcard getIdcard()
  62. {
  63. return idcard;
  64. }
  65. public void setIdcard(IDcard idcard)
  66. {
  67. this.idcard = idcard;
  68. }
  69. }

(3)创建名称为IDcard.java身份证信息的持久化类。

  1. package com.pjb.jpauserdemo.entity;
  2. import javax.persistence.*;
  3. /**
  4. * 身份证信息的持久化类
  5. * @author pan_junbiao
  6. **/
  7. @Entity
  8. @Table(name = "tab_idcard")
  9. public class IDcard
  10. {
  11. //身份证ID
  12. @Id
  13. @GeneratedValue(strategy = GenerationType.IDENTITY)
  14. @Column(name = "id")
  15. private int id;
  16. //身份证号码
  17. @Column(name = "idcard_code")
  18. private String idCardCode;
  19. public int getId()
  20. {
  21. return id;
  22. }
  23. public void setId(int id)
  24. {
  25. this.id = id;
  26. }
  27. public String getIdCardCode()
  28. {
  29. return idCardCode;
  30. }
  31. public void setIdCardCode(String idCardCode)
  32. {
  33. this.idCardCode = idCardCode;
  34. }
  35. }

(4)创建名称为PeopleDao.java公民信息数据库访问接口,并继承JpaRepository接口。

  1. package com.pjb.jpauserdemo.dao;
  2. import com.pjb.jpauserdemo.entity.People;
  3. import org.springframework.data.jpa.repository.JpaRepository;
  4. import org.springframework.stereotype.Repository;
  5. /**
  6. * 公民信息数据库访问接口
  7. * @author pan_junbiao
  8. **/
  9. @Repository
  10. public interface PeopleDao extends JpaRepository<People,Integer>
  11. {
  12. }

(5) 测试:一对一新增(新增公民信息与关联的身份证信息)。

  1. @Autowired
  2. private PeopleDao peopleDao;
  3. /**
  4. * 一对一新增:新增公民信息与关联的身份证信息
  5. * @author pan_junbiao
  6. */
  7. @Test
  8. public void addPeopleAndIdcard()
  9. {
  10. //创建身份证信息
  11. IDcard idcard = new IDcard();
  12. idcard.setIdCardCode("123456789");
  13. //创建公民信息
  14. People people = new People();
  15. people.setName("pan_junbiao的博客");
  16. people.setSex("男");
  17. people.setAge(32);
  18. //将公民与身份证信息关联
  19. people.setIdcard(idcard);
  20. //执行新增操作
  21. peopleDao.save(people);
  22. //如果新增成功,则可以获取自增主键
  23. //否则新增失败,则抛出异常
  24. if(people.getId()>0)
  25. {
  26. System.out.println("新增公民信息成功!");
  27. System.out.println("公民ID:" + people.getId());
  28. System.out.println("公民名称:" + people.getName());
  29. System.out.println("身份证号码:" + idcard.getIdCardCode());
  30. }
  31. }

执行结果:

(6)测试:一对一查询(获取公民信息与关联的身份证信息)。

  1. @Autowired
  2. private PeopleDao peopleDao;
  3. /**
  4. * 一对一查询:获取公民信息与关联的身份证信息
  5. * @author pan_junbiao
  6. */
  7. @Test
  8. public void getPeopleAndIdcard()
  9. {
  10. People people = peopleDao.findById(1).get();
  11. if (people != null)
  12. {
  13. System.out.println("---------------1、公民信息--------------------");
  14. System.out.println("公民编号:" + people.getId());
  15. System.out.println("公民名称:" + people.getName());
  16. System.out.println("公民性别:" + people.getSex());
  17. System.out.println("公民年龄:" + people.getAge());
  18. //获取关联的身份证信息信息
  19. System.out.println("---------------2、身份证信息信息---------------");
  20. IDcard idCard = people.getIdcard();
  21. if(idCard!=null)
  22. {
  23. System.out.println("身份证ID:" + idCard.getId());
  24. System.out.println("身份证号码:" + idCard.getIdCardCode());
  25. }
  26. }
  27. }

执行结果:

 

5、一对多查询

在日常开发中一对多查询是常见的,也是业务中十分重要的部分。下面将以生产商对象(类Factory)与产品对象(类Product)为例,讲解JPA的一对多关联。类Factory与类Product的关联关系如下图。

【示例】建立生产商与产品对象对象的一对多关联,并利用映射关系查询完整的产品信息。

(1)在MySQL数据库创建tab_factory表和tab_product表,并添加相关数据。

  1. -- 判断数据表是否存在,存在则删除
  2. DROP TABLE IF EXISTS tab_factory;
  3. DROP TABLE IF EXISTS tab_product;
  4. -- 创建“生产商信息”数据表
  5. CREATE TABLE IF NOT EXISTS tab_factory
  6. (
  7. factory_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '生产商ID',
  8. NAME VARCHAR(20) NOT NULL COMMENT '生产商名称'
  9. ) COMMENT = '生产商信息表';
  10. -- 创建“产品信息”数据表
  11. CREATE TABLE IF NOT EXISTS tab_product
  12. (
  13. id INT AUTO_INCREMENT PRIMARY KEY COMMENT '产品ID',
  14. NAME VARCHAR(20) NOT NULL COMMENT '产品名称',
  15. price DECIMAL(9,2) NOT NULL COMMENT '产品价格',
  16. factory_id INT COMMENT '生产商ID'
  17. ) COMMENT = '产品信息表';
  18. -- 添加数据
  19. INSERT INTO tab_factory(NAME) VALUES('华为公司');
  20. INSERT INTO tab_product(NAME,price,factory_id) VALUES('华为手机',1299,1);
  21. INSERT INTO tab_product(NAME,price,factory_id) VALUES('华为路由器',699,1);

(2)创建名称为Product.java产品信息的持久化类。

  1. package com.pjb.jpauserdemo.entity;
  2. import javax.persistence.*;
  3. import java.math.BigDecimal;
  4. /**
  5. * 产品信息的持久化类
  6. * @author pan_junbiao
  7. **/
  8. @Entity
  9. @Table(name = "tab_product")
  10. public class Product
  11. {
  12. //产品ID
  13. @Id
  14. @GeneratedValue(strategy = GenerationType.IDENTITY)
  15. @Column(name = "id")
  16. private int id;
  17. //产品名称
  18. @Column(name = "name")
  19. private String name;
  20. //产品价格
  21. @Column(name = "price")
  22. private BigDecimal price;
  23. //生产商信息
  24. @ManyToOne
  25. @JoinColumn(name = "factory_id")
  26. private Factory factory;
  27. public int getId()
  28. {
  29. return id;
  30. }
  31. public void setId(int id)
  32. {
  33. this.id = id;
  34. }
  35. public String getName()
  36. {
  37. return name;
  38. }
  39. public void setName(String name)
  40. {
  41. this.name = name;
  42. }
  43. public BigDecimal getPrice()
  44. {
  45. return price;
  46. }
  47. public void setPrice(BigDecimal price)
  48. {
  49. this.price = price;
  50. }
  51. public Factory getFactory()
  52. {
  53. return factory;
  54. }
  55. public void setFactory(Factory factory)
  56. {
  57. this.factory = factory;
  58. }
  59. }

(3)创建名称为Factory.java生产商信息的持久化类。

  1. package com.pjb.jpauserdemo.entity;
  2. import javax.persistence.*;
  3. import java.util.List;
  4. /**
  5. * 生产商信息的持久化类
  6. * @author pan_junbiao
  7. **/
  8. @Entity
  9. @Table(name = "tab_factory")
  10. public class Factory
  11. {
  12. //生产商ID
  13. @Id
  14. @GeneratedValue(strategy = GenerationType.IDENTITY)
  15. @Column(name = "factory_id")
  16. private int factoryId;
  17. //生产商名称
  18. @Column(name = "name")
  19. private String name;
  20. //产品列表
  21. @OneToMany
  22. @JoinColumn(name = "factory_id")
  23. private List<Product> productList;
  24. public int getFactoryId()
  25. {
  26. return factoryId;
  27. }
  28. public void setFactoryId(int factoryId)
  29. {
  30. this.factoryId = factoryId;
  31. }
  32. public String getName()
  33. {
  34. return name;
  35. }
  36. public void setName(String name)
  37. {
  38. this.name = name;
  39. }
  40. public List<Product> getProductList()
  41. {
  42. return productList;
  43. }
  44. public void setProductList(List<Product> productList)
  45. {
  46. this.productList = productList;
  47. }
  48. }

(4)创建名称为FactoryDao.java生产商信息数据库访问接口,并继承JpaRepository接口。

  1. package com.pjb.jpauserdemo.dao;
  2. import com.pjb.jpauserdemo.entity.Factory;
  3. import org.springframework.data.jpa.repository.JpaRepository;
  4. import org.springframework.stereotype.Repository;
  5. /**
  6. * 生产商信息数据库访问接口
  7. * @author pan_junbiao
  8. **/
  9. @Repository
  10. public interface FactoryDao extends JpaRepository<Factory,Integer>
  11. {
  12. }

(5)创建名称为ProductDao.java产品信息数据库访问接口,并继承JpaRepository接口。

  1. package com.pjb.jpauserdemo.dao;
  2. import com.pjb.jpauserdemo.entity.Product;
  3. import org.springframework.data.jpa.repository.JpaRepository;
  4. import org.springframework.stereotype.Repository;
  5. /**
  6. * 产品信息数据库访问接口
  7. * @author pan_junbiao
  8. **/
  9. @Repository
  10. public interface ProductDao extends JpaRepository<Product,Integer>
  11. {
  12. }

(6)测试:一对多查询(获取生产商信息与关联的产品列表)。

  1. @Autowired
  2. private FactoryDao factoryDao;
  3. /**
  4. * 一对多查询:获取生产商信息与关联的产品列表
  5. * @author pan_junbiao
  6. */
  7. @Test
  8. public void getFactoryAndProductList()
  9. {
  10. Factory factory = factoryDao.findById(1).get();
  11. if (factory != null)
  12. {
  13. System.out.println("---------------1、生产商信息信息--------------");
  14. System.out.println("生产商编号:" + factory.getFactoryId());
  15. System.out.println("生产商名称:" + factory.getName());
  16. //获取关联的产品信息信息
  17. System.out.println("---------------2、产品信息信息---------------");
  18. List<Product> productList = factory.getProductList();
  19. if(productList!=null && productList.size()>0)
  20. {
  21. for(Product product : productList)
  22. {
  23. System.out.println("产品编号:" + product.getId());
  24. System.out.println("产品名称:" + product.getName());
  25. System.out.println("产品价格:" + product.getPrice());
  26. System.out.println("-------------------");
  27. }
  28. }
  29. }
  30. }

执行结果:

(7)测试:多对一查询(获取产品信息与关联的生产商信息)。

  1. @Autowired
  2. private ProductDao productDao;
  3. /**
  4. * 多对一查询:获取产品信息与关联的生产商信息
  5. * @author pan_junbiao
  6. */
  7. @Test
  8. public void getProductAndFactory()
  9. {
  10. List<Product> productList = productDao.findAll();
  11. if(productList!=null && productList.size()>0)
  12. {
  13. for (Product product : productList)
  14. {
  15. //获取产品信息
  16. System.out.println("产品编号:" + product.getId());
  17. System.out.println("产品名称:" + product.getName());
  18. System.out.println("产品价格:" + product.getPrice());
  19. //获取关联的生产商信息信息
  20. Factory factory = product.getFactory();
  21. System.out.println("生产商编号:" + factory.getFactoryId());
  22. System.out.println("生产商名称:" + factory.getName());
  23. System.out.println("-------------------");
  24. }
  25. }
  26. }

执行结果:

 

6、多对多查询

多对多关联关系是比较特殊的一种关联关系,它与一对一和一对多关联关系不同,需要通过另外的一张表保存多对多的映射关系。下面将以应用系统中的权限分配为例讲解多对多的关联关系,例如用户可以拥有多个系统的操作权限,而一个权限又可以被赋予多个用户,这就是典型的多对多关联映射关系。其中用户表(tab_user)和权限表(tab_user)的表关系如下图所示。

说明:由于多对多关联关系的查询对第3个表进行反复查询,在一定程度上会影响系统的性能效率,所以在应用中尽量少使用多对多关联关系的表结果。

【示例】建立用户对象与权限对象的多对多关联关系,查询用户admin所拥有的权限,以及权限“新闻管理”被赋予了哪些用户。

(1)在MySQL数据库创建用户表(tab_user)、权限表(tab_role)和映射表(tab_mapping),并添加相关数据。

  1. -- 判断数据表是否存在,存在则删除
  2. DROP TABLE IF EXISTS tab_user;
  3. DROP TABLE IF EXISTS tab_role;
  4. DROP TABLE IF EXISTS tab_mapping;
  5. -- 创建“用户信息”数据表
  6. CREATE TABLE IF NOT EXISTS tab_user
  7. (
  8. id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
  9. NAME VARCHAR(45) NOT NULL COMMENT '用户名称'
  10. ) COMMENT = '用户信息表';
  11. -- 创建“权限信息”数据表
  12. CREATE TABLE IF NOT EXISTS tab_role
  13. (
  14. id INT AUTO_INCREMENT PRIMARY KEY COMMENT '权限ID',
  15. role_name VARCHAR(45) NOT NULL COMMENT '权限名称'
  16. ) COMMENT = '权限信息表';
  17. -- 创建“映射信息”数据表
  18. CREATE TABLE IF NOT EXISTS tab_mapping
  19. (
  20. id INT AUTO_INCREMENT PRIMARY KEY COMMENT '映射ID',
  21. user_id INT COMMENT '用户Id',
  22. role_id INT COMMENT '权限Id'
  23. ) COMMENT = '映射信息表';
  24. -- 添加数据
  25. INSERT INTO tab_user(NAME) VALUES('admin'),('pan_junbiao的博客');
  26. INSERT INTO tab_role(role_name) VALUES('系统管理员'),('新闻管理员'),('广告管理员');
  27. INSERT INTO tab_mapping(user_id,role_id) VALUES(1,1),(1,2),(1,3),(2,2),(2,3);

(2)创建名称为User.java用户信息的持久化类。

  1. package com.pjb.jpauserdemo.entity;
  2. import javax.persistence.*;
  3. import java.util.List;
  4. /**
  5. * 用户信息的持久化类
  6. * @author pan_junbiao
  7. **/
  8. @Entity
  9. @Table(name = "tab_user")
  10. public class User
  11. {
  12. //用户ID
  13. @Id
  14. @GeneratedValue(strategy = GenerationType.IDENTITY)
  15. @Column(name = "id")
  16. private int id;
  17. //用户名称
  18. @Column(name = "name")
  19. private String name;
  20. //引用的权限实体对象集合
  21. @ManyToMany(fetch = FetchType.LAZY)
  22. @JoinTable(name = "tab_mapping",joinColumns = {@JoinColumn(name = "user_id")},inverseJoinColumns = {@JoinColumn(name="role_id")})
  23. private List<Role> roleList;
  24. public int getId()
  25. {
  26. return id;
  27. }
  28. public void setId(int id)
  29. {
  30. this.id = id;
  31. }
  32. public String getName()
  33. {
  34. return name;
  35. }
  36. public void setName(String name)
  37. {
  38. this.name = name;
  39. }
  40. public List<Role> getRoleList()
  41. {
  42. return roleList;
  43. }
  44. public void setRoleList(List<Role> roleList)
  45. {
  46. this.roleList = roleList;
  47. }
  48. }

(3)创建名称为Role.java权限信息的持久化类。

  1. package com.pjb.jpauserdemo.entity;
  2. import javax.persistence.*;
  3. /**
  4. * 权限信息的持久化类
  5. * @author pan_junbiao
  6. **/
  7. @Entity
  8. @Table(name = "tab_role")
  9. public class Role
  10. {
  11. //权限ID
  12. @Id
  13. @GeneratedValue(strategy = GenerationType.IDENTITY)
  14. @Column(name = "id")
  15. private int id;
  16. //权限名称
  17. @Column(name = "role_name")
  18. private String roleName;
  19. public int getId()
  20. {
  21. return id;
  22. }
  23. public void setId(int id)
  24. {
  25. this.id = id;
  26. }
  27. public String getRoleName()
  28. {
  29. return roleName;
  30. }
  31. public void setRoleName(String roleName)
  32. {
  33. this.roleName = roleName;
  34. }
  35. }

(4)创建名称为UserDao.java用户信息数据库访问接口,并继承JpaRepository接口。

  1. package com.pjb.jpauserdemo.dao;
  2. import com.pjb.jpauserdemo.entity.User;
  3. import org.springframework.data.jpa.repository.JpaRepository;
  4. import org.springframework.stereotype.Repository;
  5. /**
  6. * 用户信息数据库访问接口
  7. * @author pan_junbiao
  8. **/
  9. @Repository
  10. public interface UserDao extends JpaRepository<User,Integer>
  11. {
  12. }

(5)测试:多对多查询(获取用户信息与关联的权限列表)。

  1. @Autowired
  2. private UserDao userDao;
  3. /**
  4. * 多对多查询:获取用户信息与关联的权限列表
  5. * @author pan_junbiao
  6. */
  7. @Test
  8. public void getUserAndRole()
  9. {
  10. List<User> userList = userDao.findAll();
  11. if(userList!=null && userList.size()>0)
  12. {
  13. //遍历用户列表
  14. for(User user : userList)
  15. {
  16. System.out.println("用户编号:" + user.getId());
  17. System.out.println("用户名称:" + user.getName());
  18. //获取权限列表
  19. List<Role> roleList = user.getRoleList();
  20. if(roleList!=null && roleList.size()>0)
  21. {
  22. System.out.print("用户拥有的权限:");
  23. for (Role role : roleList)
  24. {
  25. System.out.print(role.getRoleName()+";");
  26. }
  27. }
  28. System.out.println("\n-----------------------------------------------");
  29. }
  30. }
  31. }

执行结果:

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/530973
推荐阅读
相关标签
  

闽ICP备14008679号