SpringBoot JPA实践之EntityManage查询返回自定义DTO的代理实现

JPA动态代理
placeholder image
admin 发布于:2020-05-01 22:14:39
阅读:loading

根据前文中的代码实现示例,将实现一个接口代理查询返回自定义DTO的实现,与JPA自带的@Query查询实现类似,主要是将SQL查询出的结果集转换为普通Bean对象,依赖EntityManager对象提供的方法,自动将查询的结果集映射为Map<String,Object>、DTO、List<Map<String,Object>>、List<DTO>,具体使用参考如下。

SQL查询结果集返回为Map结构

package cn.chendd.example.jpa.user.repository;

import cn.chendd.example.jpa.nativequery.annotions.Query;
import cn.chendd.example.jpa.nativequery.annotions.QueryResult;
import cn.chendd.example.jpa.nativequery.enums.ResultSetType;
import cn.chendd.example.jpa.user.dao.NaiveQueryForMapDao;
import org.springframework.data.repository.query.Param;

import java.util.List;
import java.util.Map;

/**
 * @author chendd
 * @date 2020/5/6 23:19
 */
public interface MapResultSqlQuery {

    
@Query(sql = "select * from user limit 1" , entityManager = NaiveQueryForMapDao.class ,
            resultSet = 
@QueryResult(resultSetType = ResultSetType.Map))
    Map<String , Object> queryForMap();

    
@Query(sql = "select * from user" , entityManager = NaiveQueryForMapDao.class ,
            resultSet = 
@QueryResult(resultSetType = ResultSetType.Map))
    List<Map<String , Object>> queryForList();

    
@Query(sql = "select * from user where name = :name " , entityManager = NaiveQueryForMapDao.class ,
            resultSet = 
@QueryResult(resultSetType = ResultSetType.Map))
    List<Map<String , Object>> queryForList(
@Param("name") String name);

    
@Query(sql = "select * from user where (name = :name or :name is null) " +
            
"and (:sexFlag is null or sex in (:sexList))" , entityManager = NaiveQueryForMapDao.class ,
            resultSet = 
@QueryResult(resultSetType = ResultSetType.Map))
    List<Map<String, Object>> queryForList(
@Param("name") String name , @Param("sexFlag") String sexFlag, @Param("sexList") List<String> sexList);
}

SQL查询结果集返回为DTO结构

package cn.chendd.example.jpa.user.repository;

import cn.chendd.example.jpa.nativequery.annotions.Query;
import cn.chendd.example.jpa.nativequery.annotions.QueryResult;
import cn.chendd.example.jpa.user.dao.NaiveQueryForBeanDao;
import cn.chendd.example.jpa.user.dto.UserResultDto;
import org.springframework.data.repository.query.Param;

import java.util.List;

/**
 * @author chendd
 * @date 2020/5/11 22:36
 */
public interface BeanResultSqlQuery {

   
@Query(sql = "select 88911 id , 'chendd' userName , now() createTime , 88911.006 amount , " +
           
"            3.1415926 pi , 1 status", entityManager = NaiveQueryForBeanDao.class)
    UserResultDto queryForBean();

   
@Query(sql = "select 88911 id , 'chendd' userName , now() createTime , 88911.006 amount , " +
           
" 3.1415926 pi , 1 status" , entityManager = NaiveQueryForBeanDao.class)
    List<UserResultDto> queryForList();

   
@Query(sql = "select * from (" +
       
"select 1 id , 'chen11' userName , now() createTime " +
        
" union all " +
        
"select 2 id , 'chen22' userName , now() createTime " +
        
" union all " +
        
"select 3 id , 'chen33' userName , now() createTime " +
        
" union all " +
        
"select 4 id , 'chen44' userName , now() createTime " +
        
") newTab" , entityManager = NaiveQueryForBeanDao.class
        
, resultSet = @QueryResult(include = {"id" , "userName" , "createTime"}))
    List<UserResultDto> queryForListForPart();

   
@Query(sql = "select * from (" +
       
"select 1 id , 'chen11' userName , now() createTime " +
        
" union all " +
        
"select 2 id , 'chen22' userName , now() createTime " +
        
" union all " +
        
"select 3 id , 'chen33' userName , now() createTime " +
        
" union all " +
        
"select 4 id , 'chen44' userName , now() createTime " +
        
") newTab where id > :id and (:name is null or userName like concat(:name,'%'))"
        
, entityManager = NaiveQueryForBeanDao.class
        
, resultSet = @QueryResult(include = {"id" , "userName" , "createTime"}))
    List<UserResultDto> queryForListForParam(
@Param("id") Long userId , @Param("name") String userName);

   
@Query(sql = "select a.name userName from ${value(spring.jpa.properties.hibernate.default_catalog) , after(.)}user a " +
           
" where 1=1 limit :limit"
           
, entityManager = NaiveQueryForBeanDao.class
           
, resultSet = @QueryResult(include = "userName")
    )
    List<UserResultDto> queryForListForVariableParam(
@Param("limit") Long limit);

}

DTO代码定义

package cn.chendd.example.jpa.user.dto;

import cn.chendd.example.jpa.nativequery.annotions.Column;
import lombok.Data;
import org.hibernate.type.StringType;

import java.math.BigDecimal;
import java.sql.Timestamp;

/**
* UserResultDto
*
 * @author chendd
* @date 2020/5/6 0:29
 */
@Data
public class UserResultDto {

   
@Column
   
private Long id;
   
@Column(name = "userName" , type = StringType.class)
   
private String userName;
   
@Column
   
private Timestamp createTime;//或者Date类型
   
@Column
   
private BigDecimal amount;
   
@Column
   
private Double pi;
   
@Column
   
private Boolean status;

}

自定义注解

@Query

sql:提供SQL语句,支持动态SQL查询与内置变量替换;

entityManager:entityManager是一个线程不安全对象,通常也涉及到具体的数据源,故需要调用者提供,与DAO中提供数据源DataSource一样,代码中有提供基于base的实现可参考;

QueryResult:查询返回的结果集绑定,不指定该参数,默认返回DTO类型;

@QueryResult

查询返回的结果集类型,默认返回DTO类型,且包含DTO所有的属性,查询返回的字段名称为DTO中定义的属性名称,字段值类型为属性定义类型,也可以手动指定返回的结果集类型(Map或DTO);考虑到某个DTO如果想多个复用的场景,提供了一个includes的实现,将只对限定的某些属性生效;

@Param

JPA-hibernate提供的注解;

@Column

name:查询SQL中的字段名称,与DTO中定义的属性名称对应,指定后优先按指定设置;

type:查询列对应的Hibernate类型,默认不指定将根据DTO属性类型自动转换为Hibernate对应类型,指定后优先按指定设置,具体转换的类型可参考本系列文章小技巧篇提供的API地址;

实现功能点

(1)方法返回值按约定定义,常见的查询返回一条数据与多条数据,即Map与List类型结果集,当方法的返回值类型为List结构时,调用entityManage.getResultList方法;否则调用entityManager.getSingleResult;

(2)调用逻辑参考JPA提供的@Query查询实现,学习成本较低;

(3)内置动态参数绑定方式,使用规范与JPA提供@Query的三种参数传递方式一致(个人觉得最佳的一种方式,三种方式本站的JPA系列文章有详细说明),即“@Param注解 + :属性名”的方式;

(4)支持内置变量替换,${} 部分支持三个内置函数: before 、value、after,其中value为 application .yml文件中的key,before与after分别为value值的插入与追加的文本;

实现不足

(1)目前为增加关于分页的查询实现;

(2)调用的类或方法当前必须为事物只读;

另有一种直接使用Spring AOP的代理实现,直接拦截所有Jpa的Repository,实现方法的自定义实现即可。

 点赞


 发表评论

当前回复:作者

 评论列表


留言区