Spring Data JPA将本地查询结果Map到Non-Entity POJO

utugiqy6  于 2023-11-18  发布在  Spring
关注(0)|答案(9)|浏览(135)

我有一个Spring Data repository方法和一个native query

@Query(value = "SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", nativeQuery = true)
GroupDetails getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);

字符串
我想把结果Map到非实体POJO GroupDetails
有没有可能,你能举个例子吗?

pepwfjgg

pepwfjgg1#

我认为最简单的方法是使用所谓的投影。它可以将查询结果Map到接口。使用SqlResultSetMapping是不方便的,会让你的代码变丑:)。
一个来自spring data JPA源代码的例子:

public interface UserRepository extends JpaRepository<User, Integer> {

   @Query(value = "SELECT firstname, lastname FROM SD_User WHERE id = ?1", nativeQuery = true)
   NameOnly findByNativeQuery(Integer id);

   public static interface NameOnly {

     String getFirstname();

     String getLastname();

  }
}

字符串
您也可以使用此方法来获取投影列表。
查看这个Spring Data JPA文档条目以了解有关投影的更多信息。

注1:

请记住将User实体定义为普通-来自投影接口的字段必须匹配此实体中的字段。否则字段Map可能会中断(getFirstname()可能返回姓氏等值)。

注2:

如果你使用SELECT table.column ...表示法,总是定义与实体名称匹配的别名。例如,这段代码将无法正常工作(投影将为每个getter返回null):

@Query(value = "SELECT user.firstname, user.lastname FROM SD_User user WHERE id = ?1", nativeQuery = true)
NameOnly findByNativeQuery(Integer id);


但这很好:

@Query(value = "SELECT user.firstname AS firstname, user.lastname AS lastname FROM SD_User user WHERE id = ?1", nativeQuery = true)
NameOnly findByNativeQuery(Integer id);


在更复杂的查询情况下,我宁愿使用JdbcTemplate与自定义存储库。

hpxqektj

hpxqektj2#

假设GroupDetails和orid的答案一样,你试过JPA 2.1@ConstructorResult吗?

@SqlResultSetMapping(
    name="groupDetailsMapping",
    classes={
        @ConstructorResult(
            targetClass=GroupDetails.class,
            columns={
                @ColumnResult(name="GROUP_ID"),
                @ColumnResult(name="USER_ID")
            }
        )
    }
)

@NamedNativeQuery(name="getGroupDetails", query="SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", resultSetMapping="groupDetailsMapping")

字符串
并在存储库界面中使用以下内容:

GroupDetails getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);


根据Spring Data JPA文档,Spring将首先尝试查找与方法名称匹配的命名查询-因此通过使用@NamedNativeQuery@SqlResultSetMapping@ConstructorResult,您应该能够实现该行为

lhcgjxsq

lhcgjxsq3#

我认为Michal的方法更好。但是,还有一种方法可以从原生查询中获得结果。

@Query(value = "SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", nativeQuery = true)
String[][] getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);

字符串
现在,您可以将此2D字符串数组转换为所需的实体。

x9ybnkn6

x9ybnkn64#

您可以按照自己的方式编写本机或非本机查询,并且可以使用自定义结果类的示例 Package JPQL查询结果。使用查询中返回的列名创建DTO,并使用查询返回的序列和名称创建全参数构造函数。然后使用以下方式查询数据库。

@Query("SELECT NEW example.CountryAndCapital(c.name, c.capital.name) FROM Country AS c")

字符串
创建DTO:

package example;

public class CountryAndCapital {
    public String countryName;
    public String capitalName;

    public CountryAndCapital(String countryName, String capitalName) {
        this.countryName = countryName;
        this.capitalName = capitalName;
    }
}

mo49yndu

mo49yndu5#

使用JPA PROJECTIONS在您的情况下,可能需要将数据作为自定义类型的对象进行检索。这些类型反映了根类的部分视图,仅包含我们关心的属性。这就是投影派上用场的地方。首先将Entity声明为@immutable

@Entity
@Immutable

字符串
public class String {

@Id
private Long id;


设置存储库

public interface AddressView {
    String getZipCode();
}


然后在仓库界面中使用它:

public interface AddressRepository extends Repository<Address, Long> {
      @Query("EXEC SP_GETCODE ?1")
    List<AddressView> getAddressByState(String state);
}

hc8w905p

hc8w905p6#

这是我的解决方案转换为Map,然后自定义对象

private ObjectMapper objectMapper;

public static List<Map<String, Object>> convertTuplesToMap(List<?> tuples) {
    List<Map<String, Object>> result = new ArrayList<>();

    tuples.forEach(object->{
        if(object instanceof Tuple single) {
            Map<String, Object> tempMap = new HashMap<>();
            for (TupleElement<?> key : single.getElements()) {
                tempMap.put(key.getAlias(), single.get(key));
            }
            result.add(tempMap);
        }else{
            throw new RuntimeException("Query should return instance of Tuple");
        }
    });

    return result;
}

public <T> List<T> parseResult(List<?> list, Class<T> clz){
    List<T> result = new ArrayList<>();
    convertTuplesToMap(list).forEach(map->{
        result.add(objectMapper.convertValue(map, clz));
    });
    return result;
}

public static class CustomDTO{
    private String param1;
    private Integer param2;
    private OffsetDateTime param3;
}

public List<CustomDTO> doSomeQuery(){
    Query query = entityManager.createNativeQuery("SELECT param1, param2 param3 ... ", Tuple.class);
    return parseResult(query.getResultList(), CustomDTO.class);
}

字符串

fbcarpbf

fbcarpbf7#

使用接口中的default方法,并获取ResultyManager以获得设置ResultTransformer的机会,然后您可以返回纯POJO,如下所示:

final String sql = "SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = ? WHERE g.group_id = ?";
default GroupDetails getGroupDetails(Integer userId, Integer groupId) {
    return BaseRepository.getInstance().uniqueResult(sql, GroupDetails.class, userId, groupId);
}

字符串
BaseRepository.java是这样的:

@PersistenceContext
public EntityManager em;

public <T> T uniqueResult(String sql, Class<T> dto, Object... params) {
    Session session = em.unwrap(Session.class);
    NativeQuery q = session.createSQLQuery(sql);
    if(params!=null){
        for(int i=0,len=params.length;i<len;i++){
            Object param=params[i];
            q.setParameter(i+1, param);
        }
    }
    q.setResultTransformer(Transformers.aliasToBean(dto));
    return (T) q.uniqueResult();
}


此解决方案不会影响存储库接口文件中的任何其他方法。

rjjhvcjd

rjjhvcjd8#

  • 如果您想在Sping Boot 中使用@repository和@service结构运行自定义SQL查询,请查看。*

https://stackoverflow.com/a/71501509/4735043

mrzz3bfm

mrzz3bfm9#

你可以这样做

@NamedQuery(name="IssueDescriptor.findByIssueDescriptorId" ,

    query=" select new com.test.live.dto.IssuesDto (idc.id, dep.department, iss.issueName, 
               cat.issueCategory, idc.issueDescriptor, idc.description) 
            from Department dep 
            inner join dep.issues iss 
            inner join iss.category cat 
            inner join cat.issueDescriptor idc 
            where idc.id in(?1)")

字符串
必须有像这样的构造函数

public IssuesDto(long id, String department, String issueName, String issueCategory, String issueDescriptor,
            String description) {
        super();
        this.id = id;
        this.department = department;
        this.issueName = issueName;
        this.issueCategory = issueCategory;
        this.issueDescriptor = issueDescriptor;
        this.description = description;
    }

相关问题