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

如何在Spring Boot JPA中调用返回无主键复合表结果的SQL Server存储过程

如何在Spring Boot JPA中调用返回无主键复合表结果的SQL Server存储过程

针对你遇到的这种情况——存储过程返回的是来自多张表的复合字段,而且没有对应实体、也没有主键——其实Spring Boot JPA有两种靠谱的解决方式,我给你一步步拆解:

方案一:用EntityManager手动调用存储过程(灵活直接)

这种方法不需要定义额外的实体类,适合快速实现,步骤如下:

  • 注入EntityManager
    在你的Service类里注入EntityManager,用来执行存储过程:

    @Autowired
    private EntityManager entityManager;
    
  • 创建并配置存储过程查询
    调用createStoredProcedureQuery方法指定存储过程名,然后注册输入参数,注意参数的顺序和类型要和SQL Server里的存储过程一致:

    StoredProcedureQuery query = entityManager.createStoredProcedureQuery("dbo.sp_query_client");
    
    // 注册输入参数:第一个参数@sIdClient(必填INT)
    query.registerStoredProcedureParameter("sIdClient", Integer.class, ParameterMode.IN);
    // 注册第二个参数@iCodFamily(可选INT,默认NULL)
    query.registerStoredProcedureParameter("iCodFamily", Integer.class, ParameterMode.IN);
    
    // 设置参数值,比如调用时只传sIdClient的情况
    query.setParameter("sIdClient", 2000);
    // 如果需要传第二个参数,就加上这行:query.setParameter("iCodFamily", 1);
    
  • 执行并处理结果
    执行查询后,结果会以Object[]的形式返回,每个数组元素对应存储过程返回的一列,你可以手动把这些值映射到自定义的DTO里:

    List<Object[]> results = query.getResultList();
    List<ClientFamilyDTO> dtoList = new ArrayList<>();
    
    for (Object[] result : results) {
        ClientFamilyDTO dto = new ClientFamilyDTO();
        dto.setCreationDate((Timestamp) result[0]);
        dto.setIdClient((Integer) result[1]);
        dto.setDvClient((String) result[2]);
        dto.setCodFamilyProd((Integer) result[3]);
        dto.setDescFamily((String) result[4]);
        dto.setQtyProduct((Integer) result[5]);
        dto.setBalance((Long) result[6]);
        dtoList.add(dto);
    }
    

    这里的ClientFamilyDTO是你自己定义的普通Java类,包含对应字段的getter和setter就行,不需要任何JPA注解:

    public class ClientFamilyDTO {
        private Timestamp creationDate;
        private Integer idClient;
        private String dvClient;
        private Integer codFamilyProd;
        private String descFamily;
        private Integer qtyProduct;
        private Long balance;
    
        // 省略getter和setter方法
    }
    

方案二:用@NamedStoredProcedureQuery配合结果映射(更规范)

如果想要更贴合JPA的规范写法,可以通过定义结果映射和存储过程查询来实现,步骤如下:

  • 创建DTO并定义结果映射
    先创建刚才的ClientFamilyDTO,然后需要一个空的实体类(或者用现有实体,如果有的话)来挂载@NamedStoredProcedureQuery和@SqlResultSetMapping——因为JPA的这些注解必须放在实体类上:

    @Entity
    @SqlResultSetMapping(
        name = "ClientFamilyResult",
        classes = @ConstructorResult(
            targetClass = ClientFamilyDTO.class,
            columns = {
                @ColumnResult(name = "creation_date", type = Timestamp.class),
                @ColumnResult(name = "id_client", type = Integer.class),
                @ColumnResult(name = "dv_client", type = String.class),
                @ColumnResult(name = "cod_family_prod", type = Integer.class),
                @ColumnResult(name = "desc_family", type = String.class),
                @ColumnResult(name = "qty_product", type = Integer.class),
                @ColumnResult(name = "balance", type = Long.class)
            }
        )
    )
    @NamedStoredProcedureQuery(
        name = "sp_query_client",
        procedureName = "dbo.sp_query_client",
        parameters = {
            @StoredProcedureParameter(name = "sIdClient", mode = ParameterMode.IN, type = Integer.class),
            @StoredProcedureParameter(name = "iCodFamily", mode = ParameterMode.IN, type = Integer.class)
        },
        resultSetMappings = "ClientFamilyResult"
    )
    public class DummyEntity {
        @Id
        private Long id; // 因为JPA实体必须有@Id,随便定义一个就行,不会用到数据库里的字段
    }
    

    这里的DummyEntity只是用来承载JPA注解的,不需要对应数据库里的表,@Id字段随便定义一个就行,因为我们不会操作这个实体的CRUD。

  • 调用存储过程
    现在可以通过EntityManager来调用这个命名存储过程查询,直接获取DTO列表:

    StoredProcedureQuery query = entityManager.createNamedStoredProcedureQuery("sp_query_client");
    query.setParameter("sIdClient", 1000);
    query.setParameter("iCodFamily", 1);
    
    List<ClientFamilyDTO> dtoList = query.getResultList();
    

注意事项

  • 确保参数名称、类型和SQL Server存储过程里的完全一致,尤其是参数的mode(IN/OUT/INOUT)
  • 如果存储过程有可选参数,设置时可以传null,对应SQL里的默认值
  • 处理结果时,要注意SQL返回的字段类型和Java类型的对应关系,比如SQL的datetime对应Java的Timestamp,bigint对应Long

备注:内容来源于stack exchange,提问作者joseluisbz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.20 10:34:36