【问题标题】:Fetch List Using DTO projections using a Constructor Expression and JPQL使用构造函数表达式和 JPQL 使用 DTO 投影获取列表
【发布时间】:2018-12-27 05:26:30
【问题描述】:

在 DisabScreenRequest 上执行搜索并获取其子详细信息。使用构造函数表达式和 JPQL 使用 DTO 投影。

具有子表的父实体。

@Entity
@Table(name = "SCREEN_REQUEST")
public class DisabScreenRequest implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id 
    private long requestId;

    @Column(name = "CIVILID")
    private Long civilId;

    @ManyToMany()
    @JoinTable(name = "_DISAB_SCREEN_REQ_DETAILS", joinColumns = {
            @JoinColumn(name = "REQUEST_ID") }, inverseJoinColumns = { @JoinColumn(name = "DISABILTY_TYPE_ID") })
    private Set<DisabMaster> disabilities = new HashSet<DisabMaster>();

    public DisabScreenRequest() {
    }

}

这是残疾表。

@Entity
@Table(name="DISAB_MASTER")
@Immutable 
public class DisabMaster implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name="DIS_TYPE_ID")
    private long disabilityTypeId;

    @Column(name="DIS_TYPE_DESC")
    private String disTypeDesc;


    public DisabMaster() {
        super();
    }   

}

必须获取所有请求以及每个请求的残疾。

搜索 DTO(使用它我还有其他要添加的连接,而不是这里提到的连接)。

public class RequestSearchDto {

    private long requestId;     

    private Long civilId;   

    private  Set<DisabMaster> disabilities;

    public RequestSearchDto() {
        super();
    }

    public RequestSearchDto(long requestId, Long civilId) {
        super();
        this.requestId = requestId;     
        this.civilId = civilId;     
    }

    public RequestSearchDto(long requestId, Long civilId, Set<DisabMaster>  disabilities) {
        super();
        this.requestId = requestId;     
        this.civilId = civilId;     
        this.disabilities = disabilities;
    }   


}

这是我的 JPQL 查询

public interface ReposJPQL {

    public String GET__REQUEST = "SELECT DISTINCT new org.test.RequestSearchDto "
            + "(dsr.requestId, dsr.civilId, dsr.disabilities)"
            + " FROM DisabScreenRequest dsr WHERE 1=1 ";


}

这将得到一个 org.hibernate.exception.SQLGrammarException: 无法提取 ResultSet。

我在这里做错了什么,如何获取子表数据? 如果您需要任何信息,请告诉我

堆栈跟踪:

Caused by: java.sql.SQLException: ORA-00936: missing expression

    at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:113)
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:331)
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:288)
    at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:754)
    at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:219)
    at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:813)
    at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1051)
    at oracle.jdbc.driver.T4CPreparedStatement.executeMaybeDescribe(T4CPreparedStatement.java:854)
    at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1156)
    at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3415)
    at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3460)
    at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeQuery(NewProxyPreparedStatement.java:76)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:60)

【问题讨论】:

  • 您应该使用构造函数表达式来完全限定类名。类似 com.xxx.yyy.RequestSearchDto.
  • 使用这里提到的类名只是为了便于阅读。该错误与获取列表有关。
  • 你能分享异常的整个堆栈跟踪吗?
  • @Dherik 选择不同的 disabsc0_.request_id 作为 col_0_0_,disabsc0_.civilid 作为 col_1_0_,。 as col_2_0_ from disab_screen_request disabsc0 inner join disab_screen_req_details disabiliti1 on disabsc0_.request_id=disabiliti1_.request_id inner join disab_master disabma2 on disabiliti1_.disabilty_type_id=disabma2_.dis_type_id where 1=1 ... 这是生成的查询。第二个参数有问题
  • @Dherik 用完整的堆栈跟踪更新了问题

标签: spring spring-mvc spring-boot spring-data-jpa jpql


【解决方案1】:

如果您需要使用其嵌套子实体的集合来获取父实体,您可以使用@EntityGraph 注释或带有join fetch 的JPQL 的这种简单方法:

@Entity
public class Parent {
    //...
    @OneToMany
    private List<Child> children;
}

@Entity
public class Child {
    //...
}

interface ParentRepo extends JpaRepository<Parent, Integer> {

    // with @EntityGraph
    @EntityGraph(attributePaths = "children")
    @Override
    List<Parent> findAll(); 

    // or manually
    @Query("select distinct p from Parent p left join fetch p.children")
    List<Parent> findWithQuery(); 
}

注意在查询中使用distinct 以避免重复记录。

示例:duplicate-parent-entities

更多信息:DATAJPA-1299

【讨论】:

  • 这个答案必须被接受。加入 Fetch 提供了太多控制权:)
  • 我想说left join fetch 在这种情况下会更有意义,因为关联中没有空约束。
  • 我可以在 DTO 投影 中使用 join fetch 子句吗?
  • @Arash 如果您没有没有孩子的父母,您可以使用它。否则 - 他们不会被选中。
  • 谢谢@Cepr0。你的意思是如果我有带房间的住宿并使用 DTO 投影和连接提取来获取住宿,它将包含房间列表?
【解决方案2】:

AFAIK,您不能使用采用 Collection 的构造函数表达式。

参见JPA 2.2 Spec,第 4.14 节 BNF,了解构造函数表达式:

constructor_expression ::=
    NEW constructor_name ( constructor_item {, constructor_item}* )
constructor_item ::=
    single_valued_path_expression |
    scalar_expression |
    aggregate_expression |
    identification_variable

【讨论】:

    【解决方案3】:

    这是Blaze-Persistence Entity Views 的完美用例。

    我创建了该库以允许在 JPA 模型和自定义接口定义模型之间轻松映射,例如 Spring Data Projections on steroids。这个想法是您以您喜欢的方式定义您的目标结构,并通过 JPQL 表达式将属性(getter)映射到实体模型。由于属性名称用作默认映射,因此您通常不需要显式映射,因为 80% 的用例是拥有作为实体模型子集的 DTO。

    您的模型的映射可能如下所示简单

    @EntityView(DisabScreenRequest.class)
    interface RequestSearchDto extends Serializable {
      @IdMapping
      long getRequestId();
      Long getCivilId();
      Set<DisabMaster> getDisabilities();
    }
    

    查询是将实体视图应用于查询的问题,最简单的就是通过 id 进行查询。

    RequestSearchDtodto = entityViewManager.find(entityManager, RequestSearchDto.class, id);

    但是 Spring Data 集成允许您几乎像 Spring Data Projections 一样使用它:https://persistence.blazebit.com/documentation/1.4/entity-view/manual/en_US/#spring-data-features

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-16
      • 1970-01-01
      • 2018-09-03
      • 2012-08-29
      • 2013-08-20
      • 2021-01-09
      • 2017-08-21
      • 1970-01-01
      相关资源
      最近更新 更多