使用Spring JPA DTO投影,如何处理包含空值的嵌套投影。

4

我正在使用基于类的投影和构造函数表达式。这是我的工作中的示例代码:

@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。有人能帮我解决以下问题吗?

  1. 有没有办法使用相同的构造函数表达式来处理这种情况?我尝试查看文档,但没有找到细节。
  2. 我认为另一种选择可能是使用ResultTransformer(它将我的代码绑定到特定的JPA),但我没有找到任何有关如何在Spring JPA中使用它的信息。

更新 我甚至尝试了使用CASE选项的选项,但似乎对我也不起作用,因为我仍然得到null实体(而数据库中有数据)。这是我尝试的更新代码:

@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 我甚至尝试了使用join,但似乎也不起作用。

更新:我犯了一个愚蠢的错误。使用了简单的join而不是left join导致了这个问题。


吹毛求疵:使用ResultTransformer将使您与Hibernate绑定。您已经通过在许多地方使用它来使用JPA了。 - Jens Schauder
@JensSchauder 首先,将自己与 Hibernate 或 JPA 绑定意味着什么?其次,这样做有什么危害吗? - humbleCodes
2个回答

3
尝试使用左连接。
@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); 

抱歉,我忘记更新了。我已经尝试使用join,但似乎对我的情况不起作用。我也已经更新了我的帖子。 - Umesh Awasthi
@UmeshAwasthi,您能确认一下您尝试了左连接而不仅仅是连接吗?您可以分享一下连接试验的代码吗? - tremendous7
你说得对,我一直在尝试使用左连接而不使用 left,并且一直在寻找其他问题 :) - Umesh Awasthi
@tremendous7,你能帮我理解为什么使用简单的内连接会将整个对象作为空值吗? - humbleCodes
@UmeshAwasthi 你知道为什么内连接获取整个空对象,而左连接对你起作用的原因吗? - humbleCodes
@humbleCodes 内连接仅保留满足内连接条件的记录。左连接保留连接左侧的所有记录。 - tremendous7

2

我怀疑你的问题是完全不同的,因为使用左外连接可以解决你描述的问题。

将查询更改为:

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

我创建了一个演示外连接如何解决问题

相关代码:

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

}

我的问题是没有仔细查看我的查询。我没有使用左连接,而是使用了简单的连接 :) - Umesh Awasthi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接