Spring Data JPA 实现多表关联查询

使用 JPA 作为 ORM 框架。很多人对 JPA 抱有偏见,比如: JPA 只能处理简单的单表查询。下面总结下几种多表关联查询方法。

表结构

  1. 主表bz_package_index

    表 bz_package_index

  2. 子表bz_payeelist_bankexecut,该表的package_id字段关联主表的id字段。

    表 bz_payeelist_bankexecut

第一种方法

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

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

BeanPropertyRowMapper可以自动完成映射,也可以自定义实现特殊类型转换。另外还自动支持下划线转驼峰,只需要DTO和数据库模型的字段完全对应(字段名字一样,或者Java的驼峰式名称与数据库字段下划线式名称对应)。如果使用JdbcTemplate则可以使用这个BeanPropertyRowMapper实现DTO和数据库的映射。

  1. 定义返回数据的Model,前三个属性存储主表数据,后两个属性存储子表数据。
package com.pay.payee.model;


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

import java.math.BigDecimal;

/**
 * 提交数据后,一批作为一个批次号,包号=批次号;建议由提交机构代码+时间构成(BzPackageIndex)表数据库model
 *
 * @author 郭秀志 jbcode@126.com
 * @since 2020-06-08 08:22:00
 */
@Data
@Component
public class BzPackageIndexAndExecuteListModel {
    //以下3个属性存储主表数据
    private BigDecimal moneySum;
    private BigDecimal totalIty;
    private String payerAccount;

    //以下2个属性存储子表数据。
    private String packageId;
    private BigDecimal money;
}
  1. PayeelistBankexecutServiceImpl代码,Mapper将查询出来的数据转成上面定义的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:服务实现类
 * @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类调用service,由于使用了XML格式所以返回的数据变成了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. 测试
    访问url:http://localhost:8085/v5/packageIndex/findBzPackageIndexAndExecutListByState
    返回json数据:
[
  {
    "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两张表联查返回自定义实体。

  1. 定义数据存储的Model,来接收两张表返回的数据,注意:此时创建的是一个interface,并且里面的字段是用get的形式创建的接收参数。
package com.pay.payee.model;

import java.math.BigDecimal;

/**
 * @ClassName: IPackageIndexAndExecuteList
 * @Description: 2张表级联查询。定义返回数据的接口。
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020/6/8 11:41
 * @Copyright:
 */
public interface IPackageIndexAndExecuteList {

    //以下3个get方法存储主表对应数据
    BigDecimal getMoneySum();
    BigDecimal getTotalIty();
    String getPayerAccount();

    //以下2个get方法存储子表对应数据
    String getPackageId();
    BigDecimal getMoney();
}
  1. 在主表或者子表的Repository类定义原生sql,返回上面定义的接口类型数据List<IPackageIndexAndExecuteList>。注意sql字段的别名对应接口中的getXXX后面名称,如:getMoneySumsql中的money_sum as moneySum对应;money字段名与接口相同,则可以不用别名转换自动映射值。
/**
 * @ClassName: PayeelistBankexecutRepository
 * @Description: PayeelistBankexecutRepository持久层类,定义跟数据库操作的接口
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020年1月8日 上午10:42:50
 * @Copyright:
 */
public interface PayeelistBankexecutRepository extends JpaRepository<BzPayeelistBankexecut, Long> {

    // 通过BzPackageIndex状态筛选待执行的银行收款数据。
    @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调用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调用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. 测试
    访问url:http://localhost:8085/v5/packageIndex/findPackageIndexAndExecuteList_State
    返回json数据:
[
  {
    "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"
  }
]

联查出来的数据结果,都存储在DTO中。控制台打印数据信息:

控制台循环打印dto对象

总结

接收的DTO一定要是interface,里面的参数要写成get形式的方法体,这样jpa在查询到数据后,会自动映射到interface里,通过调用get的方法体相当于调用了取参数值,这样把数据取出来。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343