原文链接:https://blog.csdn.net/johnf_nash/article/details/80587204

多表查询在spring data jpa中有两种实现方式,第一种是利用hibernate的级联查询来实现,第二种是创建一个结果集的接口来接收连表查询后的结果,这里介绍第二种方式。

一、一对一映射

实体 UserInfo :用户。

实体 Address:家庭住址。

这里通过外键的方式(一个实体通过外键关联到另一个实体的主键)来实现一对一关联。

实体类

1、实体类 UserInfo.java

package com.johnfnash.learn.domain;import java.io.Serializable;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;@Entity
@Table(name="tb_user")
public class UserInfo implements Serializable {private static final long serialVersionUID = 8283950216116626180L;@Id@GeneratedValue(strategy=GenerationType.IDENTITY)private Long userId;private String name;private int age;private String sex;private String email;// 与 Address 的关联  private Long addressId;public UserInfo() {super();}public UserInfo(String name, int age, String sex, String email, Long addressId) {super();this.name = name;this.age = age;this.sex = sex;this.email = email;this.addressId = addressId;}// getter, setter@Overridepublic String toString() {return String.format("UserInfo [userId=%d, name=%s, age=%s, sex=%s, email=%s]", userId, name, age, sex, email);}}

2. 实体类 Address.java

package com.johnfnash.learn.domain;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;@Entity
@Table(name = "tb_address")
public class Address {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long addressId;private String areaCode;private String country;private String province;private String city;private String area;private String detailAddress;public Address() {super();}public Address(String areaCode, String country, String province, String city, String area,String detailAddress) {super();this.areaCode = areaCode;this.country = country;this.province = province;this.city = city;this.area = area;this.detailAddress = detailAddress;}// getter, setter@Overridepublic String toString() {return "Address [addressId=" + addressId + ", areaCode=" + areaCode + ", country=" + country + ", province="+ province + ", city=" + city + ", area=" + area + ", detailAddress=" + detailAddress + "]";}}

Dao 层

1、UserInfoRepository.java

package com.johnfnash.learn.repository;import java.util.List;import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;import com.johnfnash.learn.domain.UserInfo;
import com.johnfnash.learn.domain.ViewInfo;public interface UserInfoRepository extends JpaRepository<UserInfo, Long> {@Query(value = "SELECT new com.johnfnash.learn.domain.ViewInfo(u, a) FROM UserInfo u, Address a WHERE u.addressId = a.addressId")public List<ViewInfo> findViewInfo();}

注:这里的 ViewInfo 类用来一个用来接收多表查询结果集的类(使用 new + 完整类名构造函数)
代码如下:

package com.johnfnash.learn.domain;import java.io.Serializable;public class ViewInfo implements Serializable {private static final long serialVersionUID = -6347911007178390219L;private UserInfo userInfo;private Address address;public ViewInfo() {}public ViewInfo(UserInfo userInfo) {Address address = new Address();this.userInfo = userInfo;this.address = address;}public ViewInfo(Address address) {UserInfo userInfo = new UserInfo();this.userInfo = userInfo;this.address = address;}public ViewInfo(UserInfo userInfo, Address address) {this.userInfo = userInfo;this.address = address;}// getter, setter}

2. AddressRepository.java

package com.johnfnash.learn.repository;import org.springframework.data.jpa.repository.JpaRepository;import com.johnfnash.learn.domain.Address;public interface AddressRepository extends JpaRepository<Address, Long> {}

测试代码

package com.johnfnash.learn;import java.util.List;import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import com.johnfnash.learn.domain.Address;
import com.johnfnash.learn.domain.UserInfo;
import com.johnfnash.learn.domain.ViewInfo;
import com.johnfnash.learn.repository.AddressRepository;
import com.johnfnash.learn.repository.UserInfoRepository;@RunWith(SpringRunner.class)
@SpringBootTest
public class UserInfoRepositoryTests {@Autowiredprivate UserInfoRepository userInfoRepository;@Autowiredprivate AddressRepository addressRepository;@Beforepublic void init() {Address addr1 = new Address("027","CN","HuBei", "WuHan","WuChang", "123 street");Address addr2 = new Address("023","CN","ChongQing", "ChongQing","YuBei", "123 road");addressRepository.save(addr1);addressRepository.save(addr2);UserInfo user1 = new UserInfo("ZS", 21,"Male","123@xx.com", addr1.getAddressId());UserInfo user2 = new UserInfo("Ww", 25,"Male","234@xx.com", addr2.getAddressId());userInfoRepository.save(user1);userInfoRepository.save(user2);}@Afterpublic void deleteAll() {userInfoRepository.deleteAll();addressRepository.deleteAll();}@Testpublic void testQuery() {List<ViewInfo> viewInfos = userInfoRepository.findViewInfo();for (ViewInfo viewInfo : viewInfos) {System.out.println(viewInfo.getUserInfo());System.out.println(viewInfo.getAddress());}}}

查询相关的 sql 如下:

Hibernate: select userinfo0_.user_id as col_0_0_, address1_.address_id as col_1_0_ from tb_user userinfo0_ cross join tb_address address1_ where userinfo0_.address_id=address1_.address_id
Hibernate: select userinfo0_.user_id as user_id1_4_0_, userinfo0_.address_id as address_2_4_0_, userinfo0_.age as age3_4_0_, userinfo0_.email as email4_4_0_, userinfo0_.name as name5_4_0_, userinfo0_.sex as sex6_4_0_ from tb_user userinfo0_ where userinfo0_.user_id=?
Hibernate: select address0_.address_id as address_1_3_0_, address0_.area as area2_3_0_, address0_.area_code as area_cod3_3_0_, address0_.city as city4_3_0_, address0_.country as country5_3_0_, address0_.detail_address as detail_a6_3_0_, address0_.province as province7_3_0_ from tb_address address0_ where address0_.address_id=?
Hibernate: select userinfo0_.user_id as user_id1_4_0_, userinfo0_.address_id as address_2_4_0_, userinfo0_.age as age3_4_0_, userinfo0_.email as email4_4_0_, userinfo0_.name as name5_4_0_, userinfo0_.sex as sex6_4_0_ from tb_user userinfo0_ where userinfo0_.user_id=?
Hibernate: select address0_.address_id as address_1_3_0_, address0_.area as area2_3_0_, address0_.area_code as area_cod3_3_0_, address0_.city as city4_3_0_, address0_.country as country5_3_0_, address0_.detail_address as detail_a6_3_0_, address0_.province as province7_3_0_ from tb_address address0_ where address0_.address_id=?
Hibernate: select userinfo0_.user_id as user_id1_4_, userinfo0_.address_id as address_2_4_, userinfo0_.age as age3_4_, userinfo0_.email as email4_4_, userinfo0_.name as name5_4_, userinfo0_.sex as sex6_4_ from tb_user userinfo0_
Hibernate: select address0_.address_id as address_1_3_, address0_.area as area2_3_, address0_.area_code as area_cod3_3_, address0_.city as city4_3_, address0_.country as country5_3_, address0_.detail_address as detail_a6_3_, address0_.province as province7_3_ from tb_address address0_

查询结果如下:

UserInfo [userId=1, name=ZS, age=21, sex=Male, email=123@xx.com]
Address [addressId=1, areaCode=027, country=CN, province=HuBei, city=WuHan, area=WuChang, detailAddress=123 street]
UserInfo [userId=2, name=Ww, age=25, sex=Male, email=234@xx.com]
Address [addressId=2, areaCode=023, country=CN, province=ChongQing, city=ChongQing, area=YuBei, detailAddress=123 road]

二、多对多映射

实体 Author :作者。

实体 Book :书籍

这里通过关联表的方式来实现多对多关联。

实体类

实体类:Author.java

package com.johnfnash.learn.domain;import java.io.Serializable;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;@Entity
public class Author implements Serializable {private static final long serialVersionUID = 1227555837798655046L;@Id@GeneratedValueprivate Integer id;private String name;public Author() {super();}public Author(String name) {super();this.name = name;}// getter, setter@Overridepublic String toString() {return String.format("Author [id=%s, name=%s]", id, name);}}

Book.java 实体类

package com.johnfnash.learn.domain;import java.io.Serializable;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;@Entity
public class Book implements Serializable {private static final long serialVersionUID = -2470510857424220408L;@Id@GeneratedValueprivate Integer id;private String name;public Book() {super();}public Book(String name) {super();this.name = name;}//getter, setter@Overridepublic String toString() {return String.format("Book [id=%s, name=%s]", id, name);}}

实体类BookAuthor.java

package com.johnfnash.learn.domain;import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;@Entity
@IdClass(BookAuthorPK.class)
@Table(name = "book_author")
public class BookAuthor {@Idprivate Integer bookId;@Idprivate Integer authorId;public BookAuthor() {super();}public BookAuthor(Integer bookId, Integer authorId) {super();this.bookId = bookId;this.authorId = authorId;}// getter, setter}

注:这里使用 @IdClass 注解指定一个联合主键类来映射实体类的多个属性。这个联合主键类的代码如下:

package com.johnfnash.learn.domain;import java.io.Serializable;public class BookAuthorPK implements Serializable {private static final long serialVersionUID = -1158141803682305656L;private Integer bookId;private Integer authorId;public Integer getBookId() {return bookId;}public void setBookId(Integer bookId) {this.bookId = bookId;}public Integer getAuthorId() {return authorId;}public void setAuthorId(Integer authorId) {this.authorId = authorId;}}

Dao 层

BookRepository.java

package com.johnfnash.learn.repository;import java.util.List;import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;import com.johnfnash.learn.domain.Book;public interface BookRepository extends JpaRepository<Book, Integer> {@Query(nativeQuery = true, value = "SELECT b.id, b.name, GROUP_CONCAT(a.name) as authorName from book b, author a, book_author ba"+ " where b.id = ba.book_id and a.id = ba.author_id and b.name like ?1 group by b.id, b.name")List<Object[]> findByNameContaining(String name);}

注:
1)这里使用 nativeQuery = true 指定使用原生 SQL 进行查询(个人觉得复杂的查询使用原生SQL更好
2)这里使用了 mysql 的内置函数 GROUP_CONCAT 进行行转列, HQL 无法直接识别。可能会出现 Caused by: org.hibernate.QueryException: No data type for node: org.hibernate.hql.internal.ast.tree.MethodNode 的错误

JpaRepository.java

package com.johnfnash.learn.repository;import org.springframework.data.jpa.repository.JpaRepository;import com.johnfnash.learn.domain.Author;public interface AuthorRepository extends JpaRepository<Author, Integer> {}

BookAuthorRepository.java

package com.johnfnash.learn.repository;import org.springframework.data.jpa.repository.JpaRepository;import com.johnfnash.learn.domain.BookAuthor;public interface BookAuthorRepository extends JpaRepository<BookAuthor, Integer> {}

测试代码

package com.johnfnash.learn;import static org.junit.Assert.assertEquals;import java.util.List;import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import com.johnfnash.learn.domain.Author;
import com.johnfnash.learn.domain.Book;
import com.johnfnash.learn.domain.BookAuthor;
import com.johnfnash.learn.repository.AuthorRepository;
import com.johnfnash.learn.repository.BookAuthorRepository;
import com.johnfnash.learn.repository.BookRepository;@RunWith(SpringRunner.class)
@SpringBootTest
public class BookRepositoryTests {@Autowiredprivate BookRepository bookRepository;@Autowiredprivate AuthorRepository authorRepository;@Autowiredprivate BookAuthorRepository bookAuthorRepository;@Beforepublic void init() {Author lewis = new Author("Lewis");Author mark = new Author("Mark");Author peter = new Author("Peter");authorRepository.save(lewis);authorRepository.save(mark);authorRepository.save(peter);Book spring = new Book("Spring in Action");Book springboot = new Book("Spring Boot in Action");bookRepository.save(spring);bookRepository.save(springboot);bookAuthorRepository.save(new BookAuthor(spring.getId(), lewis.getId()));bookAuthorRepository.save(new BookAuthor(spring.getId(), mark.getId()));bookAuthorRepository.save(new BookAuthor(springboot.getId(), mark.getId()));bookAuthorRepository.save(new BookAuthor(springboot.getId(), peter.getId()));}@Afterpublic void deleteAll() {bookAuthorRepository.deleteAll();bookRepository.deleteAll();authorRepository.deleteAll();}@Testpublic void findAll() {assertEquals(bookRepository.findAll().size(), 2);assertEquals(authorRepository.findAll().size(), 3);List<Object[]> books = bookRepository.findByNameContaining("Spring%");for (Object[] book : books) {for (Object object : book) {System.out.print(object + ", ");}System.out.println();}}}

执行 findAll 方法后,查询的相关 SQL 如下:

Hibernate: SELECT b.id, b.name, GROUP_CONCAT(a.name) as authorName from book b, author a, book_author ba where b.id = ba.book_id and a.id = ba.author_id and b.name like ? group by b.id, b.name

输出的结果如下:

3652, Spring in Action, Lewis,Mark,
3653, Spring Boot in Action, Mark,Peter,

参考

(1) Spring Data JPA 实现多表关联查询

(2) springboot(五): spring data jpa的使用

(3) Spring Data JPA 实现多表关联查询

Spring Data JPA 实现多表关联查询相关推荐

  1. Spring Data JPA Specification多表关联查询

    需求:有一个流量计的设备,流量计有一个所属罐区id,想要通过所属罐区查到所关联的这个罐区的编码以及名称. 1.流量计实体类: 主要是给流量计实体添加了以下属性,通过tank_area_id关联: pa ...

  2. 【SpringBoot Data JPA】多表关联查询

    SpringBoot Data JPA 多表关联查询 前言 一.数据库架构设计 1.1 数据表结构 2.2 建立数据库表: 二.SpringBoot整合JPA多表查询 2.1 环境配置 2.2 建立数 ...

  3. 【Spring Data JPA自学笔记三】Spring Data JPA的基础和高级查询方法

    文章目录 调用接口的基础方法查询 Repository CrudRepository PagingAndSortingRepository JPARepository JpaSpecification ...

  4. 解决Springboot+JPA中多表关联查询会查询多次的问题(n+1查询问题)

    解决Springboot+JPA中多表关联查询会查询多次的问题(n+1查询问题) 参考文章: (1)解决Springboot+JPA中多表关联查询会查询多次的问题(n+1查询问题) (2)https: ...

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

    1.Spring Data JPA关系映射 对象关系映射(Object relational mapping)是指通过将对象状态映射到数据库列,来开发和维护对象和关系数据库之间的关系.它能够轻松处理( ...

  6. Spring Data JPA 从入门到精通~查询结果的处理

    参数选择(Sort/Pageable)分页和排序 特定类型的参数,Pageable 并动态 Sort 地将分页和排序应用于查询 案例:在查询方法中使用 Pageable.Slice 和 Sort. P ...

  7. Spring Data JPA 多条件判空查询

    Spring Data JPA虽然大大的简化了持久层的开发,但是在实际开发中,很多地方都需要高级动态查询. 使用@Query注解,这种方式可以直接在Repository里面写sql,但是这种方式的问题 ...

  8. jpa的多表关联查询

    两种情况 利用root.join()实现表关联 利用join方法实现表关联查询的是时候是依赖于主表对应的实体中存在需要关联的表的实体. 这两者的关系可以是1:1 1:n n:1 n:m 相应的实体中需 ...

  9. Spring Data JPA 从入门到精通~查询方法的创建

    查询方法的创建 内部基础架构中有个根据方法名的查询生成器机制,对于在存储库的实体上构建约束查询很有用,该机制方法的前缀 find-By.read-By.query-By.count-By 和 get- ...

最新文章

  1. 云机搭jdk1.8和apache-tomcat
  2. gx works怎么写入from指令_FANUC PMC 指令和应用
  3. 第五十八期:AI艺术日渐繁荣,未来何去何从?
  4. python os函数_python os模块主要函数
  5. js ...运算符_「 giao-js 」用js写一个js解释器
  6. 华为机试HJ8:合并表记录
  7. python三本经典书籍-Python入门经典书籍有哪些?有这三本就够了
  8. 多进程编程之进程间通信
  9. 从程序员到项目经理(2)
  10. 计算机考在职研究生难不难,计算机在职硕士考试难吗?
  11. Unity3d:UGUI,UI与特效粒子层级,2018.2以上版本BakeMesh,粒子在两个Image之间且在ScrollView
  12. win10怎么新建计算机用户,Win10添加用户教程(Microsoft微软帐户、本地帐户、儿童帐户)...
  13. PT_基本概率公式(减法/加法/乘法/除法(条件概率)/全概率/贝叶斯)@条件概率链式法则@乘法法则
  14. 微信小程序----全局状态管理 (便于全局埋点等操作)
  15. 绘制一个stm32最小系统的电路原理图;完成STM32+SD卡 的系统原理图设计
  16. Short 类型直接和数值1做对比
  17. 篮球中各个位置的名称及介绍…
  18. 实现nginx的https 功能报错:
  19. mysql加密存储敏感数据
  20. 手把手教你搭建NOD32升级服务器

热门文章

  1. Android SDK下载列表(含国内下载)-上
  2. LAC:location area code 位置区码
  3. 电商项目—商品的spu、sku概念及其之间的关系
  4. 使用BeanCopier复制对象
  5. AttributeError: partially initialized module ‘openai‘ has no attribute ‘Completion‘ 解决方案
  6. 【一分钟解决】SublimeText3多次保存自动弹出窗口
  7. RSA算法原理【超清晰】
  8. SDUT 英文金曲大赛
  9. 在XP下把win7安装到VHD,内存足够大可以RAMOS
  10. Java实现 LeetCode 799 香槟塔 (暴力模拟)