Spring JPA DTO 投影和处理具有空值的嵌套投影
Spring JPA DTO projection and handling the nested projection with null values
我正在使用基于 class 的投影和构造函数表达式。这是我工作中的示例代码
@Query("select new com.core.data.category.CategoryDto(c.id,c.code,c.externalCode,c.seoMeta, c.createdAt, c.updatedAt,c.parent.code) FROM Category c where c.code = :code")
CategoryDto findCategoryByCode(@Param("code") String code);
这是我的 CategoryDto
的样子:
public class CategoryDto implements Serializable {
private Long id;
private String code;
private String externalCode;
private SEOMeta seoMeta;
private CategoryDto parent;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
Map<String, LocCategoryDto> translation;
//constructor based on the requirement
}
除了嵌套对象的 属性 为 null
的一种情况外,这似乎工作正常。在我的例子中,父 属性 可以是 null
,以防这是一个根类别,但它似乎使用 c.parent.code
导致了问题并且整个对象即将出现 null
。有人可以帮我解决以下问题
- 有没有办法使用相同的构造函数表达式来处理这种情况?我试图查看文档,但没有找到详细信息。
- 我认为其他选项可能正在使用 ResultTransformer(它将我的代码绑定到特定的 JPA)但是我没有找到任何关于如何将它与 Spring JPA 一起使用的信息。
更新
我什至尝试了使用 CASE
选项的选项,但似乎这对我也不起作用,因为我仍然得到空实体(虽然数据在数据库中可用)。这是我尝试过的更新代码
@Query(value = "select new com.core.data.category.CategoryDto(c.id,c.code,c.externalCode,c.seoMeta, c.createdAt, c.updatedAt, " +
"CASE " +
"WHEN c.parent is NULL " +
"THEN NULL " +
"ELSE c.parent.code " +
"END ) " +
"FROM Category c where c.code = :code")
CategoryDto findCategoryByCode(@Param("code") String code);
编辑 2
我也尝试过加入,但这似乎也不起作用。
更新:我做了一个愚蠢的 mistake.Was 使用简单连接而不是左连接导致了这个问题。
尝试使用左连接
@Query("select new com.core.data.category.CategoryDto(c.id,c.code,c.externalCode,c.seoMeta, c.createdAt, c.updatedAt,parent.code) FROM Category c left join c.parent as parent where c.code = :code")
CategoryDto findCategoryByCode(@Param("code") String code);
我怀疑您的问题完全不同,因为使用左外连接可以解决您描述的问题。
将您的查询更改为:
select new com.core.data.category.CategoryDto(c.id,c.code,c.externalCode,c.seoMeta, c.createdAt, c.updatedAt, p.code)
FROM Category c
LEFT JOIN c.parent p
WHERE c.code = :code
我创建了一个reproducer demonstrating that an outer join fixes the problem。
相关代码:
@Entity
class SomeEntity {
@Id
@GeneratedValue
Long id;
String name;
@ManyToOne
SomeEntity parent;
}
public class Dto {
final String name;
final String parentName;
public Dto(String name, String parentName) {
this.name = name;
this.parentName = parentName;
}
@Override
public String toString() {
return name + " - " + parentName;
}
}
public interface SomeEntityRepository extends JpaRepository<SomeEntity, Long> {
@Query("select new de.schauderhaft.de.constructorexpressionwithnestedreference.Dto(e.name, p.name) " +
"from SomeEntity e " +
"left join e.parent p")
List<Dto> findDto();
@Query("select new de.schauderhaft.de.constructorexpressionwithnestedreference.Dto(e.name, e.parent.name) " +
"from SomeEntity e")
List<Dto> findDtoInnerJoin();
@Query("select e from SomeEntity e")
List<SomeEntity> findEntities();
}
@SpringBootTest
class ConstructorExpressionWithNestedReferenceApplicationTests {
@Autowired
SomeEntityRepository ents;
@Test
@Transactional
void testDtos() {
createEnts();
assertThat(ents.findDto()).extracting(Dto::toString).containsExactlyInAnyOrder("ents name - parents name", "parents name - null");
}
@Test
@Transactional
void testDtosInnerJoin() {
createEnts();
assertThat(ents.findDtoInnerJoin()).extracting(Dto::toString).containsExactly("ents name - parents name");
}
@Test
@Transactional
void testEntities() {
createEnts();
assertThat(ents.findEntities()).extracting(e -> e.name).containsExactlyInAnyOrder("ents name", "parents name");
}
private void createEnts() {
SomeEntity ent = new SomeEntity();
ent.name = "ents name";
ent.parent = new SomeEntity();
ent.parent.name = "parents name";
ents.saveAll(asList(ent, ent.parent));
}
}
我正在使用基于 class 的投影和构造函数表达式。这是我工作中的示例代码
@Query("select new com.core.data.category.CategoryDto(c.id,c.code,c.externalCode,c.seoMeta, c.createdAt, c.updatedAt,c.parent.code) FROM Category c where c.code = :code")
CategoryDto findCategoryByCode(@Param("code") String code);
这是我的 CategoryDto
的样子:
public class CategoryDto implements Serializable {
private Long id;
private String code;
private String externalCode;
private SEOMeta seoMeta;
private CategoryDto parent;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
Map<String, LocCategoryDto> translation;
//constructor based on the requirement
}
除了嵌套对象的 属性 为 null
的一种情况外,这似乎工作正常。在我的例子中,父 属性 可以是 null
,以防这是一个根类别,但它似乎使用 c.parent.code
导致了问题并且整个对象即将出现 null
。有人可以帮我解决以下问题
- 有没有办法使用相同的构造函数表达式来处理这种情况?我试图查看文档,但没有找到详细信息。
- 我认为其他选项可能正在使用 ResultTransformer(它将我的代码绑定到特定的 JPA)但是我没有找到任何关于如何将它与 Spring JPA 一起使用的信息。
更新
我什至尝试了使用 CASE
选项的选项,但似乎这对我也不起作用,因为我仍然得到空实体(虽然数据在数据库中可用)。这是我尝试过的更新代码
@Query(value = "select new com.core.data.category.CategoryDto(c.id,c.code,c.externalCode,c.seoMeta, c.createdAt, c.updatedAt, " +
"CASE " +
"WHEN c.parent is NULL " +
"THEN NULL " +
"ELSE c.parent.code " +
"END ) " +
"FROM Category c where c.code = :code")
CategoryDto findCategoryByCode(@Param("code") String code);
编辑 2 我也尝试过加入,但这似乎也不起作用。
更新:我做了一个愚蠢的 mistake.Was 使用简单连接而不是左连接导致了这个问题。
尝试使用左连接
@Query("select new com.core.data.category.CategoryDto(c.id,c.code,c.externalCode,c.seoMeta, c.createdAt, c.updatedAt,parent.code) FROM Category c left join c.parent as parent where c.code = :code")
CategoryDto findCategoryByCode(@Param("code") String code);
我怀疑您的问题完全不同,因为使用左外连接可以解决您描述的问题。
将您的查询更改为:
select new com.core.data.category.CategoryDto(c.id,c.code,c.externalCode,c.seoMeta, c.createdAt, c.updatedAt, p.code)
FROM Category c
LEFT JOIN c.parent p
WHERE c.code = :code
我创建了一个reproducer demonstrating that an outer join fixes the problem。
相关代码:
@Entity
class SomeEntity {
@Id
@GeneratedValue
Long id;
String name;
@ManyToOne
SomeEntity parent;
}
public class Dto {
final String name;
final String parentName;
public Dto(String name, String parentName) {
this.name = name;
this.parentName = parentName;
}
@Override
public String toString() {
return name + " - " + parentName;
}
}
public interface SomeEntityRepository extends JpaRepository<SomeEntity, Long> {
@Query("select new de.schauderhaft.de.constructorexpressionwithnestedreference.Dto(e.name, p.name) " +
"from SomeEntity e " +
"left join e.parent p")
List<Dto> findDto();
@Query("select new de.schauderhaft.de.constructorexpressionwithnestedreference.Dto(e.name, e.parent.name) " +
"from SomeEntity e")
List<Dto> findDtoInnerJoin();
@Query("select e from SomeEntity e")
List<SomeEntity> findEntities();
}
@SpringBootTest
class ConstructorExpressionWithNestedReferenceApplicationTests {
@Autowired
SomeEntityRepository ents;
@Test
@Transactional
void testDtos() {
createEnts();
assertThat(ents.findDto()).extracting(Dto::toString).containsExactlyInAnyOrder("ents name - parents name", "parents name - null");
}
@Test
@Transactional
void testDtosInnerJoin() {
createEnts();
assertThat(ents.findDtoInnerJoin()).extracting(Dto::toString).containsExactly("ents name - parents name");
}
@Test
@Transactional
void testEntities() {
createEnts();
assertThat(ents.findEntities()).extracting(e -> e.name).containsExactlyInAnyOrder("ents name", "parents name");
}
private void createEnts() {
SomeEntity ent = new SomeEntity();
ent.name = "ents name";
ent.parent = new SomeEntity();
ent.parent.name = "parents name";
ents.saveAll(asList(ent, ent.parent));
}
}