JPA自定义VO接受返回结果集(unwrap)

本文探讨了在JPA与MyBatis中处理复杂SQL查询的不同方式,包括使用@Query注解、Specification对象及CriteriaBuilder等,并提供了一个自定义VO来承载查询结果的具体案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JPA跟mybitis比较,简单的业务搜索是方便的,但是设计到复杂的SQL搜索时,我们需要自定义SQL。

1.@Query直接写SQL,缺点是无法动态的组装条件

2.JPA的Specification对象动态组装where搜索条件

3.entityManager执行CriteriaBuilder

4.entityManger直接使用createNativeQuery,执行原生SQL。这里设计到返回结果集的承载体必须是数据库对应的实体。

   这里说一个自定义的VO承接返回结果集的方法。

ProjectAttendanceEntity
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "f_id")
    private Long fId;

    @Column(name = "user_id")
    private Integer userId;

    @Column(name = "zh_name")
    private String zhName;

    @Column(name = "po_code")
    private String poCode;

    @Column(name = "po_name")
    private String poName;

    @Column(name = "punch_date")
    private String punchDate;

    @Column(name = "is_original")
    private String isOriginal;

    @Column(name = "attendance_hours")
    private String attendanceHours;

    @Column(name = "work_hours")
    private String workHours;

    @Column(name = "punch_area")
    private String punchArea;

结果集承接VO   (AttendancePoSzVO)

    private String poId;

    private String poName;

    private String zhName;

执行接口:

/**
     * 批量修改项目名称
     * @return
     */
    @PostMapping("/po/sz/batch/{project}/{pn}/{ps}")
    public PageResultVO findBatchPoInfoByUserIdAndDate(@RequestBody List<Long> ids,@PathVariable String project,@PathVariable Integer pn,@PathVariable Integer ps){
        log.info("url:/po/sz/batch/"+"|param:"+ids);
        //通过id查询数据
        List<ProjectAttendanceEntity> projects = projectAttendanceEntityRepository.findByIdIn(ids);
        //获取SQL
        String sql = getSQL(projects,pn,ps);
        Query query = entityManager.createNativeQuery(sql);
        List<AttendancePoSzVO> list = query.unwrap(NativeQuery.class).setResultTransformer(Transformers.aliasToBean(AttendancePoSzVO.class)).getResultList();
        //初始化结果集
        List<DropDownVO> result = new ArrayList<>();
        for(AttendancePoSzVO poSz : list){
            result.add(new DropDownVO(poSz.getPoName(),poSz.getPoId()));
        }
        return new PageResultVO(GlobalReturnCode.SUCCESS_CODE,"SUCCESS",ps,pn,result);
    }


    /**
     * 组装查询SQL
     * @return
     */
    public String getSQL(List<ProjectAttendanceEntity> poStatus, Integer pn, Integer ps){
        StringBuilder sql = new StringBuilder("SELECT DISTINCT res.po_id as poId,res.po_name as poName, GROUP_CONCAT(DISTINCT res.user_id) AS zhName ");
            sql.append(" FROM (");
            sql.append(" SELECT tt.po_name,tt.po_id,tt.user_id");
            sql.append(" FROM sie_sz_po_attendance_v tt ");
            sql.append(" WHERE");
            for(ProjectAttendanceEntity po : poStatus){
                sql.append("(tt.user_id = ").append(po.getUserId()).append(" and tt.rt_begin_date <= '").append(po.getPunchDate())
                    .append("' and tt.rt_end_date >= '").append(po.getPunchDate()).append("') OR ");
            }
            //截掉最后一个OR
            sql = new StringBuilder(sql.substring(0,sql.length()-3));
            sql.append(" ) res");
            sql.append(" GROUP BY res.po_name,res.po_id");
            sql.append(" HAVING ");
            for(ProjectAttendanceEntity po : poStatus){
                sql.append(" INSTR(zhName,").append(po.getUserId()).append(") >0").append(" AND ");
            }
            //截取最后一个AND
            sql = new StringBuilder(sql.substring(0,sql.length()-4));
            sql.append(" LIMIT ").append(pn).append(",").append((pn+1)*ps);
        return sql.toString();
    }

核心代码:

List<AttendancePoSzVO> list = query.unwrap(NativeQuery.class).setResultTransformer(Transformers.aliasToBean(AttendancePoSzVO.class)).getResultList();

但是这里的 setResultTransformer 已经过时了。

所以接下来寻找 setResultTransformer的替代API。

### 实现自定义VO返回 为了在 `JpaRepository` 中实现自定义 VO 返回,通常有两种主要方法来处理这个问题。 #### 方法一:使用 @Query 注解配合构造函数表达式 通过编写 JPQL 查询语句并指定目标实体的构造器,可以直接创建所需的 VO 对象。这种方式简洁明了[^2]: ```java public interface UserRepository extends JpaRepository<User, Long> { @Query("SELECT new com.example.demo.vo.UserVo(u.id, u.name, r.roleName) " + "FROM User u JOIN u.roles r WHERE u.id = :userId") List<UserVo> findUserWithRoles(@Param("userId") Long userId); } ``` 这里假设存在一个名为 `UserVo` 的类用于封装查询结果,并且该类拥有相应的构造函数接受参数初始化成员变量。 #### 方法二:利用投影接口或DTO作为查询结果类型 另一种常见的方式是采用 Spring Data JPA 提供的支持——即声明一个接口形式的投影(Projection)。这允许开发者定义只读属性访问器而无需实际实例化任何对象。对于更复杂的场景,则可以通过 DTO 类型接收数据。 ##### 使用 Projection 接口方式 首先定义 projection 接口: ```java public interface UserSummary { String getName(); Set<String> getRoleNames(); // 假设角色名称合 } ``` 接着修改 Repository 定义如下所示: ```java public interface UserRepository extends JpaRepository<User, Long> { Collection<UserSummary> findByUsername(String username); } ``` 当执行上述方法时,框架会自动映射数据库字段到 projection 属性上,从而简化了代码逻辑。 ##### 使用 DTO 方式 如果偏好于显式的 POJO 结构而非接口,则可选择此方案。只需确保所使用的 DTO 构造器签名匹配查询列顺序即可。 ```java // VO/DTO class definition @Data // Lombok annotation to generate getters/setters @AllArgsConstructor // Lombok constructor generation based on all fields @NoArgsConstructor(access = AccessLevel.PRIVATE, onConstructor_ = {@Deprecated}) public static class UserVo { private Long id; private String name; private Set<String> roleNames; } // In repository layer @Query("select new package.to.UserVo(u.id, u.username, (select collect(r.name) from Role r join ur.userRoles where ur.user = u)) " + "from User u where u.id = ?1") Optional<UserVo> findByIdAsVo(Long id); ``` 以上两种途径均可有效解决从持久层获取特定结构的数据需求,具体选用哪一种取决于应用场景和个人喜好。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乐闻x

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值