JPA支持使用@Query自定義查詢,查詢的結果須要字節用DTO對象接收,若是使用HQL的查詢語句,能夠將直接將DTO對象的構造方法傳入hql中,直接轉爲DTO對象;而若是使用native sql查詢的方式,只能將返回結果用Object[]對象接收,而後DTO設置對象的構造來接收Object[]裏面的參數完成DTO對象的轉換。java
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和設備idmysql
想列出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 | +----+-----------+---------------------+-------------------+--------------------------+----------+----------------+---------+---------------+-----------+
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的枚舉。ide
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的註解會自動生成一個全參數的構造器,構造器的順序和字段定義順序一致,類代碼以下:this
@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; }
Repository的查詢代碼以下:.net
@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的構造器以下:code
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://blog.csdn.net/qq_36144258/article/details/80296512),可是csdn上這個存在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); }
/** * https://blog.csdn.net/qq_36144258/article/details/80296512 * 網頁中直接使用objectArray中獲取每個class,可是這樣有一個問題,就是若是獲取的objectArray中有一個空值的話,不能獲取到class, * 致使不能獲取到對象的構造器 * @param objectArray * @param objectClassArray * @param dtoClass * @param <T> * @return */ 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); } } } /** * https://blog.csdn.net/qq_36144258/article/details/80296512 * 網頁中直接使用objectArray中獲取每個class,可是這樣有一個問題,就是若是獲取的objectArray中有一個空值的話,不能獲取到class, * 致使不能獲取到對象的構造器 * @param objectArray * @param objectClassArray * @param dtoClass * @param <T> * @return */ private <T> T caseDto(Object[] objectArray, Class[] objectClassArray, Class<T> dtoClass) throws Exception { Constructor<T> constructor = dtoClass.getConstructor(objectClassArray); return constructor.newInstance(objectArray); }