You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hibernate原生查询复杂构造映射:Spring Data JPA结果封装问题

完整实现方案

刚好做过类似的需求,给你整理两种常用持久化框架的完整实现方案,你可以按需选用:

一、MyBatis 实现方式

MyBatis 可以通过注解或者 XML 配置完成结果映射,这里推荐更简洁的注解方式:

1. 完善实体类的 getter/setter 和构造器

确保你的 Source、MyResult 类具备无参构造器和对应的 getter/setter 方法(MyBatis 自动映射需要):

public class Source {
    private Integer id;
    private String name;

    // 无参构造器
    public Source() {}

    // 全参构造器(可选,方便手动创建实例)
    public Source(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    // getter & setter
    public Integer getId() { return id; }
    public void setId(Integer id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

public class MyResult {
    private Source source;
    private Integer itemCount;

    // 你原有的构造器保留
    public MyResult(Source source, Integer itemCount) {
        this.source = source;
        this.itemCount = itemCount;
    }

    // 新增无参构造器+getter/setter,用于MyBatis自动映射
    public MyResult() {}

    public Source getSource() { return source; }
    public void setSource(Source source) { this.source = source; }
    public Integer getItemCount() { return itemCount; }
    public void setItemCount(Integer itemCount) { this.itemCount = itemCount; }
}

2. 编写 Mapper 接口并配置结果映射

通过 @Select 编写原生SQL,用 @Results 配置嵌套对象的映射关系:

@Mapper
public interface SourceMapper {
    @Select("select s.id source_id, s.name source_name, count(i.id) item_count " +
            "from source s left join item i on s.id = i.source_id " +
            "group by s.id")
    @Results({
            // 映射到MyResult.source.id
            @Result(property = "source.id", column = "source_id"),
            // 映射到MyResult.source.name
            @Result(property = "source.name", column = "source_name"),
            // 映射到MyResult.itemCount
            @Result(property = "itemCount", column = "item_count")
    })
    List<MyResult> getSourceItemCount();
}

3. 调用测试

在Service层注入 SourceMapper,直接调用方法即可得到封装好的结果:

@Service
public class SourceService {
    @Autowired
    private SourceMapper sourceMapper;

    public List<MyResult> getSourceItemStats() {
        return sourceMapper.getSourceItemCount();
    }
}

二、Spring Data JPA 实现方式

如果你的项目用JPA,分JPQL写法和原生SQL写法两种:

方式1:JPQL 写法(更贴合JPA风格)

JPQL支持直接调用实体类构造器,无需额外映射:

// 先确保Source和Item类已加@Entity注解,对应数据库表
@Entity
@Table(name = "source")
public class Source { 
    @Id // 标记主键
    private Integer id;
    private String name;
    // 其余构造器、getter/setter同之前代码
}

@Entity
@Table(name = "item")
public class Item { 
    @Id // 标记主键
    private Integer id;
    private String name;
    private Integer sourceId;
    // 其余构造器、getter/setter同之前代码
}

// 定义Repository接口
public interface SourceRepository extends JpaRepository<Source, Integer> {
    @Query(value = "select new com.yourpackage.MyResult(" +
            "new com.yourpackage.Source(s.id, s.name), count(i.id)) " +
            "from Source s left join Item i on s.id = i.sourceId " +
            "group by s.id")
    List<MyResult> getSourceItemCount();
}

注意:把 com.yourpackage 替换成你实际的包路径,并且 MyResult 需要保留你原来定义的 MyResult(Source source, Integer itemCount) 构造器。

方式2:原生SQL 写法(严格对应你的原生SQL)

如果必须用原生SQL,需要通过 @SqlResultSetMapping 来映射结果:

  1. 先在任意实体类上定义结果映射(比如放在Source类上):
@Entity
@Table(name = "source")
@SqlResultSetMapping(
        name = "MyResultMapping",
        classes = @ConstructorResult(
                targetClass = MyResult.class,
                columns = {
                        @ColumnResult(name = "source_id", type = Integer.class),
                        @ColumnResult(name = "source_name", type = String.class),
                        @ColumnResult(name = "item_count", type = Integer.class)
                }
        )
)
public class Source { /* ... 原有代码 ... */ }
  1. 给 MyResult 新增一个适配原生SQL结果的构造器:
public class MyResult {
    // ... 原有代码 ...

    // 新增构造器,用于原生SQL结果映射
    public MyResult(Integer sourceId, String sourceName, Integer itemCount) {
        this.source = new Source(sourceId, sourceName);
        this.itemCount = itemCount;
    }
}
  1. 在Repository中编写原生查询:
public interface SourceRepository extends JpaRepository<Source, Integer> {
    @Query(value = "select s.id source_id, s.name source_name, count(i.id) item_count " +
            "from source s left join item i on s.id = i.source_id " +
            "group by s.id",
            nativeQuery = true,
            resultSetMapping = "MyResultMapping")
    List<MyResult> getSourceItemCount();
}

调用测试

同样在Service层注入Repository调用即可:

@Service
public class SourceService {
    @Autowired
    private SourceRepository sourceRepository;

    public List<MyResult> getSourceItemStats() {
        return sourceRepository.getSourceItemCount();
    }
}

内容的提问来源于stack exchange,提问作者Elegant.Obj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:53:01