【发布时间】:2020-06-22 15:05:27
【问题描述】:
如何在某些存储库中使用任意 sql 查询(我的意思是本机 sql 查询)?我的实际问题是这样的:
@Data //lombok thing
@Entity
public class A extends AuditModel {
private long id;
private String name;
@OneToMany(mappedBy="a") //Comments.a is owning side of association, i.e. comments table does have column called a_id as foreign key
@ToString.Exclude
private Set<Comments> comments = new HashSet();
@OneToMany(mappedBy="a") //SimpleFile.a is owning side of association
private Set<SimpleFile> comments = new HashSet();
}
我有我的存储库,它使用 HAL+json 表示公开了不错的 crud 接口。我正在尝试通过一些投影/视图来丰富它,特别是由于 Web UI 在单个请求中加载一页数据。我知道摘录和预测,但它们似乎不够强大。
@Repository
@RepositoryRestResource
@Transactional(readOnly = true)
public interface ARepository extends PagingAndSortingRepository<A, Long> {
Page<A> findByNameContaining(String namePart, Pageable pageable);
@Query(
value = "SELECT a.name,\n" +
"(SELECT CAST(count(ac.id) AS int) FROM COMMENTS ac WHERE ac.a_id = a.id),\n" +
"(SELECT listagg(asf.id) FROM SIMPLE_FILES asf WHERE asf.a_id = a.id)\n" +
"FROM AS a\n" +
"WHERE a.id = :id",
nativeQuery = true
)
Optional<ACustomPage42DTO> getByIdProjectedForScreen42(Long id);
}
我也尝试过使用 JPQL,但是我在 fetch join 上遇到了问题(因为我不熟悉 JPQL)。我的最后一个评估查询是这样的:
@Query("SELECT new sk.qpp.qqq.documents.projections.ACustomPage42DTO(" +
"a " +
"(SELECT CAST(count(ac) AS int) FROM COMMENTS ac WHERE ac.a = a)" +
")\n" +
"FROM A a\n" +
"LEFT JOIN FETCH a.simpleFiles\n" +
"WHERE a.id = :id"
)
我想获得一些关于哪种方法最适合实现要在 DTO 中返回的自定义和复杂查询的一般建议(理想情况下,需要一些特定的操作链接)。
PS:实现接口并返回简单(原始)数据有效。也可以使用 JPQL 创建自定义 DAO 实例(例如,使用简单类型和 A 类型的单个实例)。使用给定查询方法的方法确实出现在给定实体端点的搜索方法中。我想要更合理的东西,所以我想要projection as defined in spring data rest项目。
我的 DTO 对象完全在我的控制之下。我更喜欢使用来自项目 lombok 的 @Value 或 @Data 注释,但这不是必需的。我也尝试过这些版本的 DTO 定义(使用接口适用于简单数据,类似的类适用于简单数据)。
interface ACustomPage42DTO {
String getName();
long getCommentsCount();
Object getAsdf();
}
或者使用具有一些好处的等效类,例如可能的自定义 toString() 方法,或者用于计算数据的一些自定义 getter:
@Value //lombok thing, imutable "POJO"
public class ACustomPage42DTO {
String name;
long commentsCount;
Set<SimpleFile> simpleFiles;
public ACustomPage42DTO(A a, long count) {
// constructor used by JPQL, if it works
name = a.getName();
this.commentsCount = count;
this.simpleFiles = a.getSimpleFiles(); // should be already fetched, due to fetch join in JPQL
}
}
这两种工作方法都可以使用“搜索”网址而不是投影来调用。我在 url http://localhost:9091/api/a/search 列表上看到了我的方法 getByIdProjectedForScreen42。我想使用它(我认为这是“正确”的方式)http://localhost:8080/api/a?projection=ACustomPage42DTOProjection。
【问题讨论】:
-
请显示
ACustomPage42DTO是什么,为了清楚起见,请指定本机查询是否有效 -
@Aivaras 首先,感谢您的帮助。其次,当我使用 array_agg 函数时会失败,因为 hibernate 不知道如何映射数组(我不想这样,因为它增加了复杂性或库)。我使用了简单的方法 listagg(没有破折号),它确实返回字符串。可以作为概念证明。通过更好的方法是使用JPQL 及其join fetch,在A 实例中使用SimpleFile 列表。这样就可以直接使用了。 “非拥有方”存在问题。没关系。我的问题是,我当前的方法显示在搜索方法中,而不是“投影”
标签: java spring jpa spring-data-jpa spring-data-rest