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

如何用Spring Data JPA调用存储过程并映射到无对应实体的自定义对象

Spring Data JPA调用存储过程映射自定义DTO(无需实体类)

针对多表关联存储过程返回的结果集,以下是几种可行的实现方案,无需为结果集创建对应实体类:


方案1:@SqlResultSetMapping + @ConstructorResult(JPA标准方式)

这种方式利用JPA的结果集映射注解,将存储过程返回的列直接映射到自定义DTO的构造函数,无需实体类对应结果表。

步骤1:定义自定义DTO

创建包含目标字段的DTO,并提供与存储过程返回列顺序匹配的构造函数:

import java.math.BigDecimal;

public class EmployeeSalaryDTO {
    private String empName;
    private String deptName;
    private BigDecimal salary;

    // 构造函数参数顺序必须和存储过程返回列的顺序一致
    public EmployeeSalaryDTO(String empName, String deptName, BigDecimal salary) {
        this.empName = empName;
        this.deptName = deptName;
        this.salary = salary;
    }

    // 按需添加getter方法
    public String getEmpName() { return empName; }
    public String getDeptName() { return deptName; }
    public BigDecimal getSalary() { return salary; }
}

步骤2:定义结果集映射

由于JPA要求@SqlResultSetMapping必须绑定到实体类,可创建一个空的占位实体类来承载映射:

import javax.persistence.*;
import java.math.BigDecimal;

@Entity
@SqlResultSetMapping(
    name = "EmployeeSalaryDTOMapping",
    classes = @ConstructorResult(
        targetClass = EmployeeSalaryDTO.class,
        columns = {
            @ColumnResult(name = "emp_name", type = String.class),
            @ColumnResult(name = "dept_name", type = String.class),
            @ColumnResult(name = "salary", type = BigDecimal.class)
        }
    )
)
public class DummyMappingEntity {
    @Id
    private Long id; // JPA实体必须包含@Id字段,无需对应数据库表
}

步骤3:调用存储过程

通过EntityManager执行存储过程并指定映射名称:

import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.StoredProcedureQuery;
import java.util.List;

@Repository
public class EmployeeSalaryRepository {

    @PersistenceContext
    private EntityManager entityManager;

    public List<EmployeeSalaryDTO> fetchEmployeeSalaryData() {
        // 第一个参数是存储过程名称,第二个是结果集映射名称
        StoredProcedureQuery query = entityManager.createStoredProcedureQuery(
                "get_employee_multi_table_data", 
                "EmployeeSalaryDTOMapping"
        );

        // 若存储过程需要输入参数,添加以下代码:
        // query.registerStoredProcedureParameter("dept_id", Integer.class, ParameterMode.IN);
        // query.setParameter("dept_id", 10);

        return query.getResultList();
    }
}

方案2:Spring Data JPA Repository简化调用

基于方案1的映射,可直接在Repository接口中定义方法,通过@Procedure注解调用存储过程:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.query.Procedure;
import java.util.List;

public interface DummyMappingRepository extends JpaRepository<DummyMappingEntity, Long> {

    // name属性对应存储过程名称,或用procedureName指定数据库中的存储过程名
    @Procedure(name = "get_employee_multi_table_data")
    List<EmployeeSalaryDTO> getEmployeeSalaryData();
}

方案3:EntityManager原生SQL调用(灵活无注解)

如果觉得JPA注解过于繁琐,可直接用原生SQL调用存储过程,手动映射结果数组到DTO:

import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;

@Repository
public class EmployeeSalaryNativeRepository {

    @PersistenceContext
    private EntityManager entityManager;

    public List<EmployeeSalaryDTO> fetchEmployeeSalaryData() {
        String sql = "CALL get_employee_multi_table_data()";
        Query query = entityManager.createNativeQuery(sql);

        // 添加参数:query.setParameter(1, "参数值");

        List<Object[]> resultRows = query.getResultList();
        return resultRows.stream()
                .map(row -> new EmployeeSalaryDTO(
                        (String) row[0],
                        (String) row[1],
                        (BigDecimal) row[2]
                ))
                .collect(Collectors.toList());
    }
}

方案4:Spring JDBC Template(原生JDBC封装)

既然原生JDBC可实现,Spring JDBC Template提供了更简洁的封装,完全脱离JPA约束,性能更优:

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

@Repository
public class EmployeeSalaryJdbcRepository {

    private final JdbcTemplate jdbcTemplate;

    // 构造函数注入JdbcTemplate
    public EmployeeSalaryJdbcRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public List<EmployeeSalaryDTO> fetchEmployeeSalaryData() {
        String sql = "CALL get_employee_multi_table_data()";

        return jdbcTemplate.query(sql, this::mapResultSetToDTO);
    }

    private EmployeeSalaryDTO mapResultSetToDTO(ResultSet rs, int rowNum) throws SQLException {
        return new EmployeeSalaryDTO(
                rs.getString("emp_name"),
                rs.getString("dept_name"),
                rs.getBigDecimal("salary")
        );
    }
}

关键注意事项

  • 存储过程返回的列名必须与映射中指定的name完全匹配(大小写敏感与否取决于数据库配置)
  • DTO构造函数的参数类型必须与数据库返回的字段类型兼容
  • 若存储过程返回数组格式结果,方案3和方案4可直接通过Object[]或ResultSet处理
  • 使用@SqlResultSetMapping时,必须绑定到一个实体类(即使是空的占位类),这是JPA的设计要求

内容的提问来源于stack exchange,提问作者SYED SAQIB S A

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 23:15:39