Spring Data JPA将本机查询结果映射到非实体POJO


91

我有一个带有本地查询的Spring Data Repository方法

@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);

并且我想将结果映射到Non-Entity POJO GroupDetails

是否可以,如果可以,请提供示例吗?

Answers:


64

假设在Orid的答案中使用GroupDetails,您是否尝试过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);

根据春天JPA的数据文件,春季将首先尝试找到一个名为查询匹配你的方法名-因此使用@NamedNativeQuery@SqlResultSetMapping并且@ConstructorResult你应该能够实现这一行为


15
为了使Spring数据能够与NamedNativeQuery匹配,需要在域实体的Class名称后加上点号作为NamedNativeQuery的名称的前缀。因此,名称应为(假设域实体为Group)“ Group.getGroupDetails”。
Grant Lay

@GrantLay您可以看一下这个问题:stackoverflow.com/q/44871757/7491770我确实有这种问题。
ram

我将如何返回此类对象的列表?
Nikhil Sahu

1
要使其正常工作,应GroupDetails标记@Entity?如果可能的话,请告诉您必须在哪个类上应用注释@NamedNativeQuery
Manu

3
@SqlResultSetMapping并且@NamedNativeQuery注释必须出现在您的Spring Data存储库中使用的实体上(例如,因为public interface CustomRepository extends CrudRepository<CustomEntity, Long>它是CustomEntity类)
Tomasz W

109

我认为最简单的方法是使用所谓的投影。它可以将查询结果映射到接口。使用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();

  }
}

您也可以使用此方法获取投影列表。

请查看此春季数据JPA docs条目,以获取有关预测的更多信息。

注1:

请记住,将您的User实体定义为正常状态-投影接口中的字段必须与该实体中的字段匹配。否则,字段映射可能会损坏(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自定义存储库。


这是一种更清洁的解决方案。我检查了一下,但是性能比使用SqlResultSetMapping差得多(大约慢30-40%:()
kidnan1991 '18

效果很好!使界面公众,如果你想在其他地方使用它
蒂维

如果要提取XML类型(clob)字段,则不起作用。有什么建议吗?
Ashish

@Ashish我宁愿使用JdbcTemplatedocs.spring.io/spring-framework/docs/current/javadoc-api/org/…)代替。您可以getClob在上使用methodresultSet来获取clob InputStream。例如:rs.getClob("xml_column").getCharacterStream()
米哈尔Stochmal

如果我在查询中使用SELECT *并且查询是本机查询,该怎么办?
Salman Kazmi '18年

16

我认为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字符串数组转换为所需的实体。


1
简单而优雅
约翰

9

您可以按照自己的方式编写本机或非本机查询,还可以使用自定义结果类的实例包装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;
    }
}

更正:名称不是强制性的...构造函数和返回的结果集中的参数序列相同。
Waqas Memon

仅当Country是您的java实体类时,此方法才有效。如果Country不是您的java实体类,则不会。
Yeshwant KAKAD

1
您说这也应与本机查询一起使用?你能举个例子吗?
理查德·廷格

OP要求进行本机查询,但给出的示例是非本机查询
CLS

1

你可以做类似的事情

@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;
    }

12
问题是关于本机查询,而不是用HQL编写的查询。
DBK

-5

在我的计算机上,我得到的代码可以正常工作,与Daimon的答案有些不同。

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

@NamedNativeQuery(name="User.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")

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.