使用 JPA 作為 ORM 框架。很多人對(duì) JPA 抱有偏見,比如: JPA 只能處理簡(jiǎn)單的單表查詢。下面總結(jié)下幾種多表關(guān)聯(lián)查詢方法。
表結(jié)構(gòu)
-
主表
bz_package_index。表 bz_package_index -
子表
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ù)庫的映射。
- 定義返回?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;
}
-
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));
}
}
-
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");
}
- 測(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í)體。
- 定義數(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();
}
- 在主表或者子表的
Repository類定義原生sql,返回上面定義的接口類型數(shù)據(jù)List<IPackageIndexAndExecuteList>。注意sql字段的別名對(duì)應(yīng)接口中的getXXX后面名稱,如:getMoneySum跟sql中的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);
-
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);
}
-
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;
}
- 測(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ù)取出來。

