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

如何使用@SqlResultSetMapping映射单个Person至地址集合?

Hibernate原生查询映射Person到地址集合的解决方案

你的问题核心在于:用LISTAGG聚合后的字符串结果,无法直接通过@SqlResultSetMapping映射成List<Address>集合。以下提供两种可行的实现思路:


方案一:利用Hibernate实体关联自动映射(推荐)

如果你的Person和Address实体本身已定义一对多关联(比如Person类里有@OneToMany(mappedBy = "person") private List<Address> addresses;),完全不需要用LISTAGG聚合,直接通过关联查询让Hibernate自动处理集合映射。

1. 修改原生SQL语句

去掉聚合逻辑,返回关联的原始数据:

SELECT p.ID, p.Name, a."Person-ID", a.Address, a.City
FROM PERSON p
JOIN Address a ON p.ID = a."Person-ID"
WHERE p.ID = 1

2. 配置@SqlResultSetMapping

同时映射Person和Address实体,Hibernate会自动将同ID的地址合并到Person的集合中:

@SqlResultSetMapping(
    name = "PersonWithAddressesMapping",
    entities = {
        @EntityResult(
            entityClass = Person.class,
            fields = {
                @FieldResult(name = "id", column = "ID"),
                @FieldResult(name = "name", column = "Name")
            }
        ),
        @EntityResult(
            entityClass = Address.class,
            fields = {
                @FieldResult(name = "personId", column = "Person-ID"),
                @FieldResult(name = "address", column = "Address"),
                @FieldResult(name = "city", column = "City")
            }
        )
    }
)

3. 执行查询

直接获取Person实例,其addresses集合会自动填充:

Query query = entityManager.createNativeQuery(sql, "PersonWithAddressesMapping");
query.setParameter("id", 1);
Person person = (Person) query.getSingleResult();
// person.getAddresses() 即为该用户的地址集合

方案二:如果必须用LISTAGG聚合,手动拆分字符串转集合

若因业务限制必须用聚合后的字符串,需通过自定义DTO手动拆分字符串生成Address集合。

1. 定义结果DTO

编写带构造方法的DTO,在构造时拆分聚合字符串:

public class PersonWithAddressesDto {
    private Integer id;
    private String name;
    private List<Address> addresses;

    // 构造方法接收聚合后的字符串参数并拆分
    public PersonWithAddressesDto(Integer id, String name, String addressStr, String cityStr) {
        this.id = id;
        this.name = name;
        this.addresses = new ArrayList<>();
        String[] addressArr = addressStr.split(",");
        String[] cityArr = cityStr.split(",");
        for (int i = 0; i < addressArr.length; i++) {
            Address addr = new Address();
            addr.setAddress(addressArr[i]);
            addr.setCity(cityArr[i]);
            addr.setPersonId(id);
            this.addresses.add(addr);
        }
    }

    // getter、setter方法
}

2. 配置@SqlResultSetMapping

使用@ConstructorResult映射到DTO的构造方法:

@SqlResultSetMapping(
    name = "PersonWithAddressesDtoMapping",
    classes = {
        @ConstructorResult(
            targetClass = PersonWithAddressesDto.class,
            columns = {
                @ColumnResult(name = "ID", type = Integer.class),
                @ColumnResult(name = "Name", type = String.class),
                @ColumnResult(name = "address_list", type = String.class),
                @ColumnResult(name = "city_list", type = String.class)
            }
        )
    }
)

3. 修改SQL并执行

给聚合列起别名,匹配映射配置:

SELECT 
    p.ID, 
    p.Name,
    LISTAGG(a.Address, ',') AS address_list, 
    LISTAGG(a.City, ',') AS city_list
FROM PERSON p
JOIN Address a ON p.ID = a."Person-ID"
WHERE p.ID = 1
GROUP BY p.ID, p.Name

执行查询获取DTO:

Query query = entityManager.createNativeQuery(sql, "PersonWithAddressesDtoMapping");
query.setParameter("id", 1);
PersonWithAddressesDto dto = (PersonWithAddressesDto) query.getSingleResult();
// dto.getAddresses() 即为拆分后的地址集合

注意:要确保LISTAGG使用的分隔符不会出现在地址/城市内容中,避免拆分错误,必要时更换为无冲突的分隔符。

内容的提问来源于stack exchange,提问作者sensen ol

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 09:51:12