Spring Data JPA 實(shí)現(xiàn)多表關(guān)聯(lián)查詢

使用 JPA 作為 ORM 框架。很多人對(duì) JPA 抱有偏見,比如: JPA 只能處理簡(jiǎn)單的單表查詢。下面總結(jié)下幾種多表關(guān)聯(lián)查詢方法。

表結(jié)構(gòu)

  1. 主表bz_package_index

    表 bz_package_index

  2. 子表bz_payeelist_bankexecut,該表的package_id字段關(guān)聯(lián)主表的id字段。

    表 bz_payeelist_bankexecut

第一種方法

使用 Spring 提供的 JdbcTemplate。核心代碼:

List<Obj> list = jdbcTemplate.query(sql.toString(), new BeanPropertyRowMapper<>(Obj.class));

BeanPropertyRowMapper可以自動(dòng)完成映射,也可以自定義實(shí)現(xiàn)特殊類型轉(zhuǎn)換。另外還自動(dòng)支持下劃線轉(zhuǎn)駝峰,只需要DTO和數(shù)據(jù)庫模型的字段完全對(duì)應(yīng)(字段名字一樣,或者Java的駝峰式名稱與數(shù)據(jù)庫字段下劃線式名稱對(duì)應(yīng))。如果使用JdbcTemplate則可以使用這個(gè)BeanPropertyRowMapper實(shí)現(xiàn)DTO和數(shù)據(jù)庫的映射。

  1. 定義返回?cái)?shù)據(jù)的Model,前三個(gè)屬性存儲(chǔ)主表數(shù)據(jù),后兩個(gè)屬性存儲(chǔ)子表數(shù)據(jù)。
package com.pay.payee.model;


import lombok.Data;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;

/**
 * 提交數(shù)據(jù)后,一批作為一個(gè)批次號(hào),包號(hào)=批次號(hào);建議由提交機(jī)構(gòu)代碼+時(shí)間構(gòu)成(BzPackageIndex)表數(shù)據(jù)庫model
 *
 * @author 郭秀志 jbcode@126.com
 * @since 2020-06-08 08:22:00
 */
@Data
@Component
public class BzPackageIndexAndExecuteListModel {
    //以下3個(gè)屬性存儲(chǔ)主表數(shù)據(jù)
    private BigDecimal moneySum;
    private BigDecimal totalIty;
    private String payerAccount;

    //以下2個(gè)屬性存儲(chǔ)子表數(shù)據(jù)。
    private String packageId;
    private BigDecimal money;
}
  1. PayeelistBankexecutServiceImpl代碼,Mapper將查詢出來的數(shù)據(jù)轉(zhuǎn)成上面定義的BzPackageIndexAndExecuteListModel。
package com.pay.payee.service.impl;

import com.pay.payee.entity.BzPayeelistBankexecut;
import com.pay.payee.model.BzPackageIndexAndExecuteListModel;
import com.pay.payee.repository.PayeelistBankexecutRepository;
import com.pay.payee.service.IPayeelistBankexecutService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.List;

/**
 * @ClassName: PayeelistBankexecutServiceImpl
 * @Description:服務(wù)實(shí)現(xiàn)類
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020年1月8日 上午10:44:11
 * @Copyright:
 */
@Service
@Transactional
public class PayeelistBankexecutServiceImpl implements IPayeelistBankexecutService {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private PayeelistBankexecutRepository payeelistBankexecutRepository;

 
    @Override
    public List findBzPackageIndexAndExecutListByState(String state) {
        String sql = "SELECT * FROM 
`bz_payeelist_bankexecut` list, bz_package_index idx 
WHERE idx.id = list.package_id AND idx.state =? 
limit 5";
        /**
         * 帶條件查詢
         */
        return jdbcTemplate.query(sql, new Object[]{state}, new BeanPropertyRowMapper(BzPackageIndexAndExecuteListModel.class));
    }

}
  1. controller類調(diào)用service,由于使用了XML格式所以返回的數(shù)據(jù)變成了XML,通過produces = MediaType.APPLICATION_JSON_VALUE使其返回json
    @ApiVersion(5)
    @RequestMapping(value = "/findBzPackageIndexAndExecutListByState", produces = MediaType.APPLICATION_JSON_`VALUE)
    // http://localhost:8555/v5/packageIndex/findBzPackageIndexAndExecutListByState
    public List<BzPackageIndexAndExecuteListModel> findBzPackageIndexAndExecutListByState() throws InterruptedException {
        return payeelistBankexecutService.findBzPackageIndexAndExecutListByState("80");
    } 
  1. 測(cè)試
    訪問url:http://localhost:8085/v5/packageIndex/findBzPackageIndexAndExecutListByState
    返回json數(shù)據(jù):
[
  {
    "moneySum": 29459.00,
    "totalIty": 26,
    "payerAccount": "622848002565612",
    "packageId": "-202004-1586060312402",
    "money": 802.00
  },
  {
    "moneySum": 29459.00,
    "totalIty": 26,
    "payerAccount": "622848002565612",
    "packageId": "-202004-1586060312402",
    "money": 2801.00
  },
  {
    "moneySum": 29459.00,
    "totalIty": 26,
    "payerAccount": "622848002565612",
    "packageId": "-202004-1586060312402",
    "money": 2801.00
  },
  {
    "moneySum": 29459.00,
    "totalIty": 26,
    "payerAccount": "622848002565612",
    "packageId": "-202004-1586060312402",
    "money": 2802.00
  },
  {
    "moneySum": 29459.00,
    "totalIty": 26,
    "payerAccount": "622848002565612",
    "packageId": "-202004-1586060312402",
    "money": 807.00
  }
]

第二種方法(推薦)

SpringBoot使用Jpa兩張表聯(lián)查返回自定義實(shí)體。

  1. 定義數(shù)據(jù)存儲(chǔ)的Model,來接收兩張表返回的數(shù)據(jù),注意:此時(shí)創(chuàng)建的是一個(gè)interface,并且里面的字段是用get的形式創(chuàng)建的接收參數(shù)。
package com.pay.payee.model;

import java.math.BigDecimal;

/**
 * @ClassName: IPackageIndexAndExecuteList
 * @Description: 2張表級(jí)聯(lián)查詢。定義返回?cái)?shù)據(jù)的接口。
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020/6/8 11:41
 * @Copyright:
 */
public interface IPackageIndexAndExecuteList {

    //以下3個(gè)get方法存儲(chǔ)主表對(duì)應(yīng)數(shù)據(jù)
    BigDecimal getMoneySum();
    BigDecimal getTotalIty();
    String getPayerAccount();

    //以下2個(gè)get方法存儲(chǔ)子表對(duì)應(yīng)數(shù)據(jù)
    String getPackageId();
    BigDecimal getMoney();
}
  1. 在主表或者子表的Repository類定義原生sql,返回上面定義的接口類型數(shù)據(jù)List<IPackageIndexAndExecuteList>。注意sql字段的別名對(duì)應(yīng)接口中的getXXX后面名稱,如:getMoneySumsql中的money_sum as moneySum對(duì)應(yīng);money字段名與接口相同,則可以不用別名轉(zhuǎn)換自動(dòng)映射值。
/**
 * @ClassName: PayeelistBankexecutRepository
 * @Description: PayeelistBankexecutRepository持久層類,定義跟數(shù)據(jù)庫操作的接口
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020年1月8日 上午10:42:50
 * @Copyright:
 */
public interface PayeelistBankexecutRepository extends JpaRepository<BzPayeelistBankexecut, Long> {

    // 通過BzPackageIndex狀態(tài)篩選待執(zhí)行的銀行收款數(shù)據(jù)。
    @Query(nativeQuery = true, value = "SELECT money_sum as moneySum,total_ity as totalIty,idx.payer_account as payerAccount,package_id as packageId,money FROM `bz_payeelist_bankexecut` list, bz_package_index idx WHERE idx.id = list.package_id AND idx.state =?1 limit 5")
    // 原生SQL方法
    List<IPackageIndexAndExecuteList> findPackageIndexAndExecuteList_State(String state);
  1. Service調(diào)用Repository
@Service
@Transactional
public class PayeelistBankexecutServiceImpl implements IPayeelistBankexecutService {
    
    @Autowired
    private PayeelistBankexecutRepository payeelistBankexecutRepository;

    @Override
    public List<IPackageIndexAndExecuteList> findPackageIndexAndExecuteList_State(String state) {
        return payeelistBankexecutRepository.findPackageIndexAndExecuteList_State(state);
    }
  1. Controller調(diào)用Service
    @ApiVersion(5)
    @RequestMapping(value = "/findPackageIndexAndExecuteList_State", produces = MediaType.APPLICATION_JSON_VALUE)
    // http://localhost:8555/v5/packageIndex/findPackageIndexAndExecuteList_State
    public List<IPackageIndexAndExecuteList> findPackageIndexAndExecuteList_State(String state) {
        List<IPackageIndexAndExecuteList> packageIndexAndExecuteList = payeelistBankexecutService.findPackageIndexAndExecuteList_State("80");
        packageIndexAndExecuteList.stream().forEach(dto -> {
            log.info("result: moneySum:{}, totalIty:{}, payerAccount:{}, packageId:{}", dto.getMoneySum(), dto.getTotalIty(), dto.getMoney(), dto.getPayerAccount(), dto.getPackageId());
        });

        return packageIndexAndExecuteList;
    }
  1. 測(cè)試
    訪問url:http://localhost:8085/v5/packageIndex/findPackageIndexAndExecuteList_State
    返回json數(shù)據(jù):
[
  {
    "moneySum": 29459.00,
    "totalIty": 26,
    "money": 802.00,
    "payerAccount": "622848002565612",
    "packageId": "-202004-1586060312402"
  },
  {
    "moneySum": 29459.00,
    "totalIty": 26,
    "money": 2801.00,
    "payerAccount": "622848002565612",
    "packageId": "-202004-1586060312402"
  },
  {
    "moneySum": 29459.00,
    "totalIty": 26,
    "money": 2801.00,
    "payerAccount": "622848002565612",
    "packageId": "-202004-1586060312402"
  },
  {
    "moneySum": 29459.00,
    "totalIty": 26,
    "money": 2802.00,
    "payerAccount": "622848002565612",
    "packageId": "-202004-1586060312402"
  },
  {
    "moneySum": 29459.00,
    "totalIty": 26,
    "money": 807.00,
    "payerAccount": "622848002565612",
    "packageId": "-202004-1586060312402"
  }
]

聯(lián)查出來的數(shù)據(jù)結(jié)果,都存儲(chǔ)在DTO中??刂婆_(tái)打印數(shù)據(jù)信息:

控制臺(tái)循環(huán)打印dto對(duì)象

總結(jié)

接收的DTO一定要是interface,里面的參數(shù)要寫成get形式的方法體,這樣jpa在查詢到數(shù)據(jù)后,會(huì)自動(dòng)映射到interface里,通過調(diào)用get的方法體相當(dāng)于調(diào)用了取參數(shù)值,這樣把數(shù)據(jù)取出來。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容