1. 程式人生 > >Spring Data JPA 實現多表關聯查詢

Spring Data JPA 實現多表關聯查詢

多表查詢在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 @Override public 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

  @Override
  public 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 {

  @Autowired
    private UserInfoRepository userInfoRepository;

  @Autowired
  private AddressRepository addressRepository;

  @Before
    public 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","[email protected]", addr1.getAddressId());
        UserInfo user2 = new UserInfo("Ww", 25,"Male","[email protected]", addr2.getAddressId());
        userInfoRepository.save(user1);
        userInfoRepository.save(user2);
    }

  @After
  public void deleteAll() {
    userInfoRepository.deleteAll();

    addressRepository.deleteAll();
  }

  @Test
  public 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 protected]]
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 protected]]
Address [addressId=2, areaCode=023, country=CN, province=ChongQing, city=ChongQing, area=YuBei, detailAddress=123 road]

二、多對多對映

實體 Author :作者。

實體 Book :書籍

這裡通過關聯表的方式來實現多對多關聯。

實體類

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
    @GeneratedValue
    private Integer id;

    private String name;

  public Author() {
    super();
  }

  public Author(String name) {
    super();
    this.name = name;
  }

  // getter, setter

  @Override
    public 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
    @GeneratedValue
    private Integer id;

    private String name;

    public Book() {
        super();
    }

    public Book(String name) {
        super();
        this.name = name;
    }

  //getter, setter

  @Override
  public String toString() {
    return String.format("Book [id=%s, name=%s]", id, name);
  }

}
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 {

  @Id
  private Integer bookId;

  @Id
  private 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> {

}
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 {

  @Autowired
  private BookRepository bookRepository;

  @Autowired
  private AuthorRepository authorRepository;

  @Autowired
  private BookAuthorRepository bookAuthorRepository;

  @Before
  public 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()));
  }

  @After
  public void deleteAll() {
    bookAuthorRepository.deleteAll();
    bookRepository.deleteAll();
    authorRepository.deleteAll();
  }

  @Test
  public 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, 

參考

相關推薦

Spring Data JPA 實現關聯查詢

多表查詢在spring data jpa中有兩種實現方式,第一種是利用hibernate的級聯查詢來實現,第二種是建立一個結果集的介面來接收連表查詢後的結果,這裡介紹第二種方式。 一、一對一對映 實體 UserInfo :使用者。 實體 Address

spring data jpa 實現條件複雜查詢聯查

最近發現JPA在處理單表時,很方便,但多表就比較複雜了。今天寫一下端午這兩天琢磨的多條件查詢,還有多表聯查。文章比較長,大部分都是程式碼,不願意看的程式碼copy下去,直接可以實現;想交流的可以看完,然後留言交流。maven依賴啊,配置,繼承寫法等知識點不展開說了,之前寫過一

Spring Data JPA 實現聯查的另一種方式

       通過封裝實體類來實現多表聯查的方式,利用Spring Data JPA @Query定義中的SpEL中的 ( rd.orderId=:#{#orderPageReq.orderId} or :#{#orderPageReq.orderId} is null

Spring Data JPA 一:實現關聯查詢

多表查詢在spring data jpa中有兩種實現方式,第一種是利用hibernate的級聯查詢來實現,第二種是建立一個結果集的介面來接收連表查詢後的結果,這裡介紹第二種方式。 一、一對一對映 實體 UserInfo :使用者。 實體 Address:家庭住址。 這裡通過外來鍵的方

Spring Boot入門系列(十七)整合Mybatis,建立自定義mapper 實現關聯查詢

之前講了Springboot整合Mybatis,介紹瞭如何自動生成pojo實體類、mapper類和對應的mapper.xml 檔案,並實現最基本的增刪改查功能。mybatis 外掛自動生成的mapper 實現了大部分基本、通用的方法,如:insert、update、delete、select 等大概20個左右

SpringBoot中使用Spring Data Jpa 實現簡單的動態查詢的兩種方法

ppr eat value table 得到 blog .net ride integer 首先謝謝大佬的簡書文章:http://www.jianshu.com/p/45ad65690e33# 這篇文章中講的是spring中使用spring data jpa,使用了xml配

SpringBoot12 QueryDSL02之利用QueryDSL實現關聯查詢

之間 entity hide cto tar nds 執行 arc -- 1 業務需求   有的系統業務邏輯比較復雜,存在著多表關聯查詢的的情況,查詢的內容不僅僅是單張表的的內容而是多張表的字段組合而成的,直接使用SplringDataJPA實現是比較復雜的,但是如果使用Q

Spring Data JPA 複雜/條件組合查詢

1: 編寫DAO類或介面  dao類/介面 需繼承 publicinterface JpaSpecificationExecutor<T>          介面;        如果需要分頁,還可繼承   publicinterface Pa

Mybatis Plus程式碼方式實現關聯查詢

  Mapper介面如下: 例1 @Select("SELECT t_question.*,t_stude

Spring Data JPA 二:實現關聯分頁查詢

最近在對JPA的使用過程中發現對於單表的操作很是方便,但是當設計到多表聯查的時候就需要有一些特殊的操作了。 專案中有一個場景是後臺需要做一個分頁的列表查詢,所需要的資料分散在兩張表中,如果是用mybatis的話直接定義resultMap,然後手寫SQL就可以了。而在JPA中就需要用到JPQL

Spring boot jpa 關聯查詢

效果圖: 如圖,根據條件查詢主表資訊,並關聯獲取詳細資料,並實現簡單的排序功能,不多說,上程式碼: model: /** * @author xj * @date 2017/5/3 13:31 */ @Getter @Setter @Entity(name = "

Spring Boot中使用Spring-data-jpa實現分頁查詢(轉)

分頁查詢 log def inpu database ext identity odin btn 在我們平時的工作中,查詢列表在我們的系統中基本隨處可見,那麽我們如何使用jpa進行多條件查詢以及查詢列表分頁呢?下面我將介紹兩種多條件查詢方式。 1、引入起步依賴

spring,springmvc,mybatits整合例項--關聯查詢

spring,springmvc,mybatits整合例項–多表關聯查詢 相比於ssh,ssm框架更加簡潔,輕巧,並且sql語句和程式碼耦合更低。本文給出一個ssm整合多表查詢的例項 完整程式碼下載連結: http://download.csdn.net/download/

spring data jpa】帶有條件的查詢後分頁和不帶條件查詢後分頁實現

一.不帶有動態條件的查詢 分頁的實現  例項程式碼: controller:返回的是Page<>物件 @Controller@RequestMapping(value = "/egg")  publicclass EggController {   @

不使用left-join等關聯查詢,只用單查詢和Java程式,簡便實現查詢”效果

上次我們提到,不使用left-loin關聯查詢,可能是為了提高效率或者配置快取,也可以簡化一下sql語句的編寫。只寫單表查詢,sql真得太簡單了。問題是,查詢多個表的資料還是非常需要的。   因此,存在這麼一個強烈的需求:查詢2個集合,怎麼合併2個集合中的資料為1個集合,且資料關聯要正確。

JPA條件組合查詢,關聯查詢,資料條數(CriteriaQuery)

JPA多條件組合查詢(CriteriaQuery) /**查詢Demo結果集 * @return */ public List<Demo> findDemoList(ParamVO param,Pageable pageabl

Spring Boot中使用Spring-data-jpa實現分頁查詢

在我們平時的工作中,查詢列表在我們的系統中基本隨處可見,那麼我們如何使用jpa進行多條件查詢以及查詢列表分頁呢?下面我將介紹兩種多條件查詢方式。 1、引入起步依賴   <dependency> <groupId>org.springframe

SSM實現連線查詢(一對關聯

    SSM 各種配置資訊 就 不再贅述,之前一篇寫過。表1 對應的 pojo      - - - 表1 欄位   id、name          List<Two>是表2 查詢返回來的結果集表2 對應的 pojo    ---- 表2 欄位 id、name

Spring boot 關聯查詢

返回值: 如圖:,返回結果中,根據股票程式碼關聯查詢了股票名稱,這邊spring boot 有自帶jpa的寫法,下一篇,介紹roo.join();這邊介紹下hibernate的寫法, Controll

利用SSM(springmvc+spring+mybatis)實現聯合查詢

一、資料庫                           Author表 (authorid主鍵)                                                                    NewsInfo表(newsno主