【问题标题】:Left join without using Hibernate join relation不使用 Hibernate 连接关系的左连接
【发布时间】:2021-10-15 14:01:23
【问题描述】:

假设我有一个休眠 jpa 实体-A:

public class A{
   id ,//pk
   name,
   age
   //getters and setters
}

另一个实体-B:

public class B{
   id, //pk
   a_id, //pk id of A
   degree,
   institute
   //getters and setters
}

这里要注意实体 A 和 B 之间没有隐式关系,B 表中的 a_id 是手动处理的,因此 - 关系就像一对多(A-->B)但不是jpa-hibernate 关系。 和, 我的 FullDto 具有 A 的所有属性和 B 的列表:

public class FullDto{
      id ,//pk
      name,
      age,
     List<B> bList;

    public class FullDto(id, age, name, bList)
     {
        this.id.id;
        this.age=age;
        this.name=name;
        this.bList=bList;
     }
    //getters and setters

}

现在,我想从 A 的存储库接口中提取所有内容(一次 A 和 B 表),如下所示:

public interface ARepository extends JpaRepository<A, Long> {
@Query("select " +
            "new packageName.FullDto(a.id, a.name, a.age, b) " +
            "from" +
            "   A a " +
            " left join B  b "+
            " on a.id=b.a_id "+
            "where " +
            "   a.id=:id ")
      FullDto getFullDetails(@Param("id") Long id);
}

但这似乎不是这样做的方法!

当我尝试这种方式时:我得到了这个错误

警告] 2021-08-12 13:20:13.091 [restartedMain] TemplateRenderer - HHH000174:函数模板预期有 4 个参数,但有 1 个参数 遇到 [ERROR] 2021-08-12 13:20:13.123 [restartedMain] ErrorTracker - 第 1:51 行:意外令牌:) [错误] 2021-08-12 13:20:13.123 [restartedMain] ErrorTracker - 第 1:51 行:意外 令牌:)antlr.NoViableAltException:意外令牌:)在 org.hibernate.hql.internal.antlr.HqlBaseParser.negatedExpression(HqlBaseParser.java:2534) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.hql.internal.antlr.HqlBaseParser.logicalAndExpression(HqlBaseParser.java:2438) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.hql.internal.antlr.HqlBaseParser.logicalOrExpression(HqlBaseParser.java:2403) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.hql.internal.antlr.HqlBaseParser.expression(HqlBaseParser.java:2116) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.hql.internal.antlr.HqlBaseParser.aliasedExpression(HqlBaseParser.java:2357) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.hql.internal.antlr.HqlBaseParser.selectedPropertiesList(HqlBaseParser.java:1390) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.hql.internal.antlr.HqlBaseParser.newExpression(HqlBaseParser.java:1434) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.hql.internal.antlr.HqlBaseParser.selectClause(HqlBaseParser.java:1306) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.hql.internal.antlr.HqlBaseParser.selectFrom(HqlBaseParser.java:1040) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.hql.internal.antlr.HqlBaseParser.queryRule(HqlBaseParser.java:748) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.hql.internal.antlr.HqlBaseParser.selectStatement(HqlBaseParser.java:319) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.hql.internal.antlr.HqlBaseParser.statement(HqlBaseParser.java:198) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:289) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:188) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:143) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.engine.query.spi.HQLQueryPlan.(HQLQueryPlan.java:119) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.engine.query.spi.HQLQueryPlan.(HQLQueryPlan.java:80) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:153) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:595) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:704) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:23) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final] 在 sun.reflect.GeneratedMethodAccessor88.invoke(Unknown Source) ~[?:?] 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_121] 在 java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_121] 在 org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:350) ~[spring-orm-5.1.4.RELEASE.jar:5.1.4.RELEASE] 在 com.sun.proxy.$Proxy155.createQuery(Unknown Source) ~[?:?] at org.springframework.data.jpa.repository.query.SimpleJpaQuery.validateQuery(SimpleJpaQuery.java:87) ~[spring-data-jpa-2.1.4.RELEASE.jar:2.1.4.RELEASE] 在 org.springframework.data.jpa.repository.query.SimpleJpaQuery.(SimpleJpaQuery.java:63) ~[spring-data-jpa-2.1.4.RELEASE.jar:2.1.4.RELEASE] 在 org.springframework.data.jpa.repository.query.JpaQueryFactory.fromMethodWithQueryString(JpaQueryFactory.java:76) ~[spring-data-jpa-2.1.4.RELEASE.jar:2.1.4.RELEASE] 在 org.springframework.data.jpa.repository.query.JpaQueryFactory.fromQueryAnnotation(JpaQueryFactory.java:56) ~[spring-data-jpa-2.1.4.RELEASE.jar:2.1.4.RELEASE] 在 org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$DeclaredQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:139) ~[spring-data-jpa-2.1.4.RELEASE.jar:2.1.4.RELEASE] 在 org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:206) ~[spring-data-jpa-2.1.4.RELEASE.jar:2.1.4.RELEASE] 在 org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:79) ~[spring-data-jpa-2.1.4.RELEASE.jar:2.1.4.RELEASE] 在 org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lookupQuery(RepositoryFactorySupport.java:566) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE] 在 org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$mapMethodsToQuery$1(RepositoryFactorySupport.java:559) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE] 在 java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[?:1.8.0_121]...

如何在不使用 hibernate oneToMany 和 ManyToOne 关系连接的情况下获取所有数据?

【问题讨论】:

  • 问题是new FullDto() 。您需要提供参数。

标签: java spring spring-boot hibernate jpa


【解决方案1】:

这是不可能的,因为构造函数语法只允许传递单个属性,即在此级别上没有记录聚合之类的东西,因此您必须创建某种 IntermediateDto 并将其聚合为 @987654325 @通过一些自定义代码,例如 Vlad Mihalcea 在这里解释:https://vladmihalcea.com/one-to-many-dto-projection-hibernate/

我认为这是 Blaze-Persistence Entity Views 的完美用例,因为它采用了更有效的方法,更易于使用和维护。

我创建了该库以允许在 JPA 模型和自定义接口或抽象类定义模型之间轻松映射,例如 Spring Data Projections on steroids。这个想法是您按照自己喜欢的方式定义目标结构(域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。

使用 Blaze-Persistence Entity-Views 的用例的 DTO 模型可能如下所示:

@EntityView(A.class)
public interface FullDto {
    @IdMapping
    Long getId();
    String getName();
    Long getAge();
    @Mapping("B[aId = VIEW(id)]")
    Set<BDto> getBList();

    @EntityView(B.class)
    interface BDto {
        @IdMapping
        Long getId();
        String getName();
    }
}

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

FullDto a = entityViewManager.find(entityManager, FullDto.class, id);

Spring Data 集成让您可以像使用 Spring Data Projections 一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Page<FullDto> findAll(Pageable pageable);

最好的部分是,它只会获取实际需要的状态!

【讨论】:

    【解决方案2】:

    FullDto 必须有一个与查询中的投影相匹配的构造函数。 并且您必须在查询中提供 FullDto 的完全限定名称:

     @Query("select " +
            "   new packagename.FullDto(a.id, a.name, a.age)" +
            " from A a " +
            " left join B b on a.id = b.a_id "+
            " where a.id=:id ")
      FullDto getFullDetails(@Param("id") Long id);
    

    【讨论】:

    • 我已经添加了完全限定的 dto 和你展示的构造函数,但我也需要从 b 表中获取结果,它是一个列表 并传入构造函数。我试过这种方式:new packagename.FullDto(a.id, a.name, a.age, b.education) 但没有用。
    • B 表有多个相同a.id 的行,所以getFullDetails 需要返回一个List。如果我这样做,它工作正常。但我想获得一个 FullDto,其中来自表 a 的数据和来自表 b 的数据实际上是 List 将在这个方法的单个 FullDto 中。
    • 可以用jdbc模板吗?
    • JPA 和 JdbcTemplate 都无法做到这一点。我知道的唯一方法是使用 jOOQ 进行多重设置:blog.jooq.org/2021/07/06/…
    【解决方案3】:

    DTO projection 需要使用构造函数表达式来填充 DTO。

    此外,DTO 类名应是完全限定的。

    【讨论】:

    • 我将完全限定的 dto 与构造函数一起使用。但是不知道如何将它用于查询中的列表,尽管它存在于构造函数中
    • 可以用jdbc模板吗?
    • 这是不可能的,无论是使用 JPA DTO 投影还是使用 JdbcTemplate。无论哪种方式,您都会收到平面数据,并且必须手动将它们标准化为所需的父子关系。
    • JPA Query 可以,但即使这样也需要使用 ManyToOne 和 OneToMany 关系更新模型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-05
    • 2013-02-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多