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

时间:2015-03-16 17:04:00

标签: spring hibernate jpa spring-data pojo

我有一个带有本机查询的Spring Data存储库方法

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

我想将结果映射到非实体POJO GroupDetails

是否有可能,如果可以,请您提供一个例子吗?

8 个答案:

答案 0 :(得分:52)

我认为最简单的方法是使用所谓的投影。它可以将查询结果映射到接口。使用SqlResultSetMapping是不方便的,会使您的代码变得丑陋:)。

Spring数据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();

  }
}

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

Check out this spring data JPA docs entry for more info about projections.

注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代替自定义存储库。

答案 1 :(得分:49)

假设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 documentation,spring将首先尝试查找与您的方法名称匹配的命名查询 - 因此,使用@NamedNativeQuery@SqlResultSetMapping@ConstructorResult您应该是能够实现这种行为

答案 2 :(得分:3)

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

答案 3 :(得分:3)

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

答案 4 :(得分: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;
    }

答案 5 :(得分:0)

使用界面中的默认方法并获取EntityManager来获得设置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();
}

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

答案 6 :(得分:0)

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

<块引用>
@Entity
@Immutable

公开课地址{

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

答案 7 :(得分:-2)

在我的电脑中,我得到了这段代码。它与Daimon的答案略有不同。

&#13;
&#13;
@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")
&#13;
&#13;
&#13;