我的编程空间,编程开发者的网络收藏夹
学习永远不晚

JPA如何将查询结果转换为DTO对象

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

JPA如何将查询结果转换为DTO对象

前言

JPA支持使用@Query自定义查询,查询的结果需要字节用DTO对象接收,如果使用HQL的查询语句,可以将直接将DTO对象的构造方法传入hql中,直接转为DTO对象;而如果使用native sql查询的方式,只能将返回结果用Object[]对象接收,然后DTO设置对象的构造来接收Object[]里面的参数完成DTO对象的转换。

例子

mysql数据库表

用户表

CREATE TABLE `pos_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
  `user_pwd` varchar(255) DEFAULT NULL,
  `user_type` int(11) DEFAULT NULL,
  `parent_id` bigint(20) DEFAULT NULL,
  `user_status` int(11) DEFAULT NULL,
  `distributor_id` bigint(20) DEFAULT NULL,
  `creator_identity_type` int(2) DEFAULT NULL,
  `creator_id` bigint(20) DEFAULT NULL,
  `create_date` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8;

设备表

CREATE TABLE `pos_device` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `imei` varchar(120) NOT NULL,
  `mac` varchar(120) NOT NULL,
  `unique_code` varchar(120) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
  `type` varchar(100) DEFAULT NULL,
  `system_version` varchar(100) DEFAULT NULL,
  `distributor_id` bigint(20) DEFAULT NULL,
  `creator_identity_type` int(2) DEFAULT NULL,
  `creator_id` bigint(20) DEFAULT NULL,
  `create_date` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8;

用户和设备关联表

CREATE TABLE `pos_user_device_relation` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `device_id` bigint(20) DEFAULT NULL,
  `user_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

可以看到用户和设备关联表中有用户id和设备id

联合查询的需求

想列出pos_user_device_relation表中所有pos_user的distributor_id=1的所有用户和设备,要求返回的信息包括用户的username、type信息和设备的imei、mac等信息。

sql语句

SELECT
pdr.id,
pdr.device_id,
pd.imei,
pd.mac,
pd.unique_code,
pd.type,
pd.system_version,
pdr.user_id,
pu.user_name,
pu.user_type
FROM
pos_user_device_relation pdr, pos_user pu, pos_device pd
WHERE pdr.device_id = pd.id AND pdr.user_id = pu.id AND pdr.user_id in (select id from pos_user where distributor_id=1) limit 0,10

查询可以正常得到结果,结果行是这样的:

+----+-----------+---------------------+-------------------+--------------------------+----------+----------------+---------+---------------+-----------+
| id | device_id | imei                | mac               | unique_code              | type     | system_version | user_id | user_name     | user_type |
+----+-----------+---------------------+-------------------+--------------------------+----------+----------------+---------+---------------+-----------+

如何在JPA中映射为DTO对象

DTO对象字段定义如下:

private Long posUserDeviceId;
private Long deviceId;
private String deviceImei;
private String deviceMac;
private String deviceUniqueCode;
private String deviceType;
private String deviceSystemVersion;
private Long userId;
private String username;
private PosUserEntityConstants.UserType userType;

对象中的PosUserEntityConstants.UserType是一个自定义转换类型,通过继承AttributeConverter将Integer转换为UserType的枚举。

方法一:使用HQL的方法

Repository的查询代码如下:

@Query(
        value = "SELECT\n" +
                "new com.hengbao.ethiopiatelecomrecharge.dao.dto.PosUserDeviceRelationDto(\n" +
                "pdr.id,\n" +
                "pdr.deviceId,\n" +
                "pd.imei,\n" +
                "pd.mac,\n" +
                "pd.uniqueCode,\n" +
                "pd.type,\n" +
                "pd.systemVersion,\n" +
                "pdr.userId,\n" +
                "pu.userName,\n" +
                "pu.userType\n" +
                ") \n" +
                "FROM \n" +
                "PosUserDeviceRelationEntity pdr, PosUserEntity pu, PosDeviceEntity pd \n" +
                "WHERE pdr.deviceId = pd.id AND pdr.userId = pu.id AND pdr.userId in (select id from PosUserEntity where distributorId=?1)",
        countQuery = "SELECT count(*) FROM \n" +
                "PosUserDeviceRelationEntity pdr, PosUserEntity pu, PosDeviceEntity pd \n" +
                "WHERE pdr.deviceId = pd.id AND pdr.userId = pu.id AND pdr.userId in (select id from PosUserEntity where distributorId=?1)"
)
Page<PosUserDeviceRelationDto> findUserAndDeviceInfoByDistributorId(Long distributorId, Pageable pageable);

可以看到HQL的方法将PosUserDeviceRelationDto的构造器直接传入到HQL语句中,省去了我们自行转换的麻烦。那么PosUserDeviceRelationDto中也要重写一个相应的构造器:

由于项目中使用了lombok,所有最终dto的代码只是在类上面加上了一些注解,@AllArgsConstructor的注解会自动生成一个全参数的构造器,构造器的顺序和字段定义顺序一致,类代码如下:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class PosUserDeviceRelationDto implements Serializable {
    
    private static final long serialVersionUID = 1L; 
    private Long posUserDeviceId; 
    private Long deviceId;
    private String deviceImei;
    private String deviceMac;
    private String deviceUniqueCode;
    private String deviceType;
    private String deviceSystemVersion; 
    private Long userId;
    private String username;
    private PosUserEntityConstants.UserType userType;
}

方法二:使用native query的方式查询并转换为dto

Repository的查询代码如下:

@Query(
        value = "SELECT\n" +
                "pdr.id,\n" +
                "pdr.device_id,\n" +
                "pd.imei,\n" +
                "pd.mac,\n" +
                "pd.unique_code,\n" +
                "pd.type,\n" +
                "pd.system_version,\n" +
                "pdr.user_id,\n" +
                "pu.user_name,\n" +
                "pu.user_type\n" +
                "FROM\n" +
                "pos_user_device_relation pdr, pos_user pu, pos_device pd\n" +
                "WHERE pdr.device_id = pd.id AND pdr.user_id = pu.id AND pdr.user_id in (select id from pos_user where distributor_id=?1)",
        countQuery = "SELECT count(*) FROM\n" +
                "pos_user_device_relation pdr, pos_user pu, pos_device pd\n" +
                "WHERE pdr.device_id = pd.id AND pdr.user_id = pu.id AND pdr.user_id in (select id from pos_user where distributor_id=?1)",
        nativeQuery = true
)
Page<Object[]> findUserAndDeviceInfoByDistributorId2(Long distributorId, Pageable pageable);

可以看到这样只能用Object[]来接收结果集,而不能直接将返回参数定义为PosUserDeviceRelationDto对象,否则会报no converter的异常。

那如何将Object[]的结果集转换为PosUserDeviceRelationDto对象呢?

首先先看一下Object[]每个对象的类型:BigInteger BigInteger String String String String String BigInteger String Integer

这是可以发现虽然mysql数据库定义的是bigint(20)类型,但是结果集是BigInteger,不能直接用Long接收,所以专门定义一个dto的构造器如下:

public PosUserDeviceRelationDto(BigInteger posUserDeviceId,
                                BigInteger deviceId,
                                String deviceImei,
                                String deviceMac,
                                String deviceUniqueCode,
                                String deviceType,
                                String deviceSystemVersion,
                                BigInteger userId,
                                String username,
                                Integer userType) {
    this.posUserDeviceId = posUserDeviceId == null ? null : posUserDeviceId.longValue();
    this.deviceId = deviceId == null ? null : deviceId.longValue();
    this.deviceImei = deviceImei;
    this.deviceMac = deviceMac;
    this.deviceUniqueCode = deviceUniqueCode;
    this.deviceType = deviceType;
    this.deviceSystemVersion = deviceSystemVersion;
    this.userId = userId == null ? null : userId.longValue();
    this.username = username;
    // UserTypeConverter是继承自javax.persistence.AttributeConverter的类型转换器
    this.userType = new PosUserEntityConstants.UserTypeConverter().convertToEntityAttribute(userType);
}

然后直接调用构造即可:

Page<Object[]> userAndDeviceInfoByDistributorId2 = posUserDeviceRelationRepository.findUserAndDeviceInfoByDistributorId2(1L, PageRequest.of(0, 10));
for (Object[] objects : userAndDeviceInfoByDistributorId2.getContent()) {
    // 转换成dto的方法一:将objects中的所有参数强转为对应类型,传递到dto的构造器中;dto对象定义好对应的构造器
    PosUserDeviceRelationDto dto1 = new PosUserDeviceRelationDto(
            (BigInteger) objects[0],
            (BigInteger) objects[1],
            (String    ) objects[2],
            (String    ) objects[3],
            (String    ) objects[4],
            (String    ) objects[5],
            (String    ) objects[6],
            (BigInteger) objects[7],
            (String    ) objects[8],
            (Integer   ) objects[9]);
    System.out.println(dto1);

网上还能搜到另外一种解决方法,就是通过反射的方法简化dto的转化步骤(https://www.jb51.net/article/238470.htm),但是这个存在bug,如果返回的objects数组中有一个值为null,那么getClass()方法获取类的类型就会报错,所以改为将每个参数的类型直接传入进去,可以这样使用反射其实省不了多少工夫了:

Page<Object[]> userAndDeviceInfoByDistributorId2 = posUserDeviceRelationRepository.findUserAndDeviceInfoByDistributorId2(1L, PageRequest.of(0, 10));
for (Object[] objects : userAndDeviceInfoByDistributorId2.getContent()) {
    // 转换成dto的方法二:反射的方法直接调用构造
    PosUserDeviceRelationDto dto2 = caseDto(objects, new Class[]{BigInteger.class,
                    BigInteger.class,
                    String.class,
                    String.class,
                    String.class,
                    String.class,
                    String.class,
                    BigInteger.class,
                    String.class,
                    Integer.class},
            PosUserDeviceRelationDto.class);
    System.out.println(dto2);
}

private <T> T caseDto(Object[] objectArray, Class[] objectClassArray, Class<T> dtoClass) throws Exception {
    Constructor<T> constructor = dtoClass.getConstructor(objectClassArray);
    return constructor.newInstance(objectArray);
}

例子涉及的部分源代码

Repository

@Query(
        value = "SELECT\n" +
                "new com.hengbao.ethiopiatelecomrecharge.dao.dto.PosUserDeviceRelationDto(\n" +
                "pdr.id,\n" +
                "pdr.deviceId,\n" +
                "pd.imei,\n" +
                "pd.mac,\n" +
                "pd.uniqueCode,\n" +
                "pd.type,\n" +
                "pd.systemVersion,\n" +
                "pdr.userId,\n" +
                "pu.userName,\n" +
                "pu.userType\n" +
                ") \n" +
                "FROM \n" +
                "PosUserDeviceRelationEntity pdr, PosUserEntity pu, PosDeviceEntity pd \n" +
                "WHERE pdr.deviceId = pd.id AND pdr.userId = pu.id AND pdr.userId in (select id from PosUserEntity where distributorId=?1)",
        countQuery = "SELECT count(*) FROM \n" +
                "PosUserDeviceRelationEntity pdr, PosUserEntity pu, PosDeviceEntity pd \n" +
                "WHERE pdr.deviceId = pd.id AND pdr.userId = pu.id AND pdr.userId in (select id from PosUserEntity where distributorId=?1)"
)
Page<PosUserDeviceRelationDto> findUserAndDeviceInfoByDistributorId(Long distributorId, Pageable pageable);
@Query(
        value = "SELECT\n" +
                "pdr.id,\n" +
                "pdr.device_id,\n" +
                "pd.imei,\n" +
                "pd.mac,\n" +
                "pd.unique_code,\n" +
                "pd.type,\n" +
                "pd.system_version,\n" +
                "pdr.user_id,\n" +
                "pu.user_name,\n" +
                "pu.user_type\n" +
                "FROM\n" +
                "pos_user_device_relation pdr, pos_user pu, pos_device pd\n" +
                "WHERE pdr.device_id = pd.id AND pdr.user_id = pu.id AND pdr.user_id in (select id from pos_user where distributor_id=?1)",
        countQuery = "SELECT count(*) FROM\n" +
                "pos_user_device_relation pdr, pos_user pu, pos_device pd\n" +
                "WHERE pdr.device_id = pd.id AND pdr.user_id = pu.id AND pdr.user_id in (select id from pos_user where distributor_id=?1)",
        nativeQuery = true
)
Page<Object[]> findUserAndDeviceInfoByDistributorId2(Long distributorId, Pageable pageable);

DTO类

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class PosUserDeviceRelationDto implements Serializable {
    
    private static final long serialVersionUID = 1L; 
    private Long posUserDeviceId; 
    private Long deviceId;
    private String deviceImei;
    private String deviceMac;
    private String deviceUniqueCode;
    private String deviceType;
    private String deviceSystemVersion; 
    private Long userId;
    private String username;
    private PosUserEntityConstants.UserType userType; 
    public PosUserDeviceRelationDto(BigInteger posUserDeviceId,
                                    BigInteger deviceId,
                                    String deviceImei,
                                    String deviceMac,
                                    String deviceUniqueCode,
                                    String deviceType,
                                    String deviceSystemVersion,
                                    BigInteger userId,
                                    String username,
                                    Integer userType) {
        this.posUserDeviceId = posUserDeviceId == null ? null : posUserDeviceId.longValue();
        this.deviceId = deviceId == null ? null : deviceId.longValue();
        this.deviceImei = deviceImei;
        this.deviceMac = deviceMac;
        this.deviceUniqueCode = deviceUniqueCode;
        this.deviceType = deviceType;
        this.deviceSystemVersion = deviceSystemVersion;
        this.userId = userId == null ? null : userId.longValue();
        this.username = username;
        // UserTypeConverter是继承自javax.persistence.AttributeConverter的类型转换器
        this.userType = new PosUserEntityConstants.UserTypeConverter().convertToEntityAttribute(userType);
    }
}

test测试类:

@Test
public void testFindUserAndDeviceInfoByDistributorId() throws Exception {
    System.out.println("-----------------hql query-----------------");
    Page<PosUserDeviceRelationDto> userAndDeviceInfoByDistributorId = posUserDeviceRelationRepository.findUserAndDeviceInfoByDistributorId(1L, PageRequest.of(0, 10)); 
    System.out.println("count=" + userAndDeviceInfoByDistributorId.getTotalElements());
    if(userAndDeviceInfoByDistributorId.getContent() != null) {
        for (PosUserDeviceRelationDto dto : userAndDeviceInfoByDistributorId.getContent()) {
            System.out.println(dto);
        }
    }
 
    System.out.println("-----------------native sql query-----------------");
    Page<Object[]> userAndDeviceInfoByDistributorId2 = posUserDeviceRelationRepository.findUserAndDeviceInfoByDistributorId2(1L, PageRequest.of(0, 10));
    System.out.println("count=" + userAndDeviceInfoByDistributorId2.getTotalElements());
    if(userAndDeviceInfoByDistributorId2.getContent() != null) {
        for (Object[] objects : userAndDeviceInfoByDistributorId2.getContent()) {
            for (Object obj : objects) {
                System.out.print(obj + "(" + (obj == null ? null : obj.getClass().getSimpleName()) + ") ");
            }
            System.out.println();  
        }
 
        // 转换为dto 方法一
        System.out.println("-----转换dto的第一种方法-----");
        for (Object[] objects : userAndDeviceInfoByDistributorId2.getContent()) {
            // 转换成dto的方法一:将objects中的所有参数强转为对应类型,传递到dto的构造器中;dto对象定义好对应的构造器
            PosUserDeviceRelationDto dto1 = new PosUserDeviceRelationDto(
                    (BigInteger) objects[0],
                    (BigInteger) objects[1],
                    (String    ) objects[2],
                    (String    ) objects[3],
                    (String    ) objects[4],
                    (String    ) objects[5],
                    (String    ) objects[6],
                    (BigInteger) objects[7],
                    (String    ) objects[8],
                    (Integer   ) objects[9]);
            System.out.println(dto1);
        }
 
        // 转换为dto 方法二
        System.out.println("-----转换dto的第二种方法-----");
        for (Object[] objects : userAndDeviceInfoByDistributorId2.getContent()) {
            // 转换成dto的方法二:反射的方法直接调用构造
            PosUserDeviceRelationDto dto2 = caseDto(objects, new Class[]{BigInteger.class,
                            BigInteger.class,
                            String.class,
                            String.class,
                            String.class,
                            String.class,
                            String.class,
                            BigInteger.class,
                            String.class,
                            Integer.class},
                    PosUserDeviceRelationDto.class);
            System.out.println(dto2);
        }
    }
}
 

private <T> T caseDto(Object[] objectArray, Class[] objectClassArray, Class<T> dtoClass) throws Exception {
    Constructor<T> constructor = dtoClass.getConstructor(objectClassArray);
    return constructor.newInstance(objectArray);
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

JPA如何将查询结果转换为DTO对象

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何将 MySQL 查询结果数组转换为对象?

将 mysql 查询结果数组转换为对象的方法如下:创建一个空对象数组。循环结果数组并为每一行创建一个新的对象。使用 foreach 循环将每一行的键值对赋给新对象的相应属性。将新对象添加到对象数组中。关闭数据库连接。如何将 MySQL 查询
如何将 MySQL 查询结果数组转换为对象?
2024-04-29

JPA如何查询原生SQL转换VO对象方式

小编给大家分享一下JPA如何查询原生SQL转换VO对象方式,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!JPA查询原生SQL转换VO对象 List
2023-06-25

java如何将map转换为对象

要将Map转换为对象,可以使用反射来实现。首先,创建一个空对象,然后遍历Map的键值对,通过反射设置对象的属性值。假设有一个Person类,有name和age两个属性,可以按照以下步骤转换Map为Person对象:1. 创建Person对象
2023-08-24

Javascript如何将数组转换为对象

这篇文章主要为大家展示了“Javascript如何将数组转换为对象”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Javascript如何将数组转换为对象”这篇文章吧。将数组转换为对象如果要将数组
2023-06-17

JavaScript如何将arguments对象转换为数组

小编给大家分享一下JavaScript如何将arguments对象转换为数组,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!将 arguments 对象转换为数组a
2023-06-27

如何将json字符串转换为java对象

如何将json字符串转换为java对象?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Java的特点有哪些Java的特点有哪些1.Java语言作为静态面向对象编程语言的代表,
2023-06-14

php如何将数组转换为对象数组

这篇文章主要介绍了php如何将数组转换为对象数组的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇php如何将数组转换为对象数组文章都会有所收获,下面我们一起来看看吧。方法一:强制类型转换$arr = array(
2023-07-06

如何将javascript对象转换为json字符串

这篇文章将为大家详细讲解有关如何将javascript对象转换为json字符串,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。在javascript中,可以使用内置方法“JSON.stringify()”来
2023-06-14

在Java项目中如何将对象转换为String

在Java项目中如何将对象转换为String?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。Java中对象转换为String的常用方法:  方法一:String
2023-05-31

OleDbDataReader查询出的结果如何转换成DataTable啊

要将OleDbDataReader查询结果转换为DataTable,可以按照以下步骤进行操作:1. 创建一个空的DataTable对象。```DataTable dataTable = new DataTable();```2. 使用Ole
2023-08-08

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录