【问题标题】:How can I use fetch join with @OneToOne relationship?如何将 fetch join 与 @OneToOne 关系一起使用?
【发布时间】:2017-06-24 12:42:25
【问题描述】:

我有实体 Post、PostDetail。 它们是一对一的关系,PostDetail 是可选的。

class Post {
    @Id
    private int id;

    @Column
    private String title;

    @Column
    private Strint contents;

    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private PostDetail postDetail;
}

class PostDetail {
    @Id
    private int id;

    @Column
    private boolean duplicated;

    @OneToOne
    private Post post;
}

public interface PostRepository extends JpaRepository<Post, Integer> {
    @Transactional
    @Query("SELECT a FROM Post a LEFT JOIN FETCH a.postDetail")
    public Page<Post> getAll(Example<Post> example, Pageable pageable);

    @Transactional
    @Query("SELECT a FROM Post a LEFT JOIN FETCH a.postDetail")
    public List<Post> getAll();
}

应用程序启动时发生异常。

原因:org.hibernate.QueryException:查询指定连接提取,但提取关联的所有者不在选择列表中...

有什么问题?我正在尝试这样做以避免在查询帖子列表时出现 N+1 问题 (getAll())。


抱歉,我修改了我的问题。

两个 PostRepository 的方法都会出错。

first getAll() 抛出错误“query specified join fetching ...”

第二个 getAll() 抛出错误

org.mariadb.jdbc.internal.common.QueryException:“字段列表”中的未知列“postdetail1_.post_id”

【问题讨论】:

  • 这对我来说看起来不错(除了无用的 Transactional 注释)。我的猜测是例外是关于另一个查询。发布完整的堆栈跟踪。
  • 很奇怪。获取的关联的所有者显然 出现在选择列表中。 @PrimaryKeyJoinColumn 可能有问题,如果您删除注释,代码是否有效?一种解决方法是使用 hibernate 的 @Fetch 注释声明默认获取策略并完全删除 JOIN FETCH
  • 我修改了问题。

标签: java spring spring-data-jpa jpql


【解决方案1】:

如果您想避免 1+N 查询问题,您可以使用 EntityGraph。只需覆盖自定义 Repo 中的“findAll”方法并在它们上使用 EntityGraph 注释,如下所示:

public interface PostRepository extends JpaRepository<Post, Integer> {

    @Override
    @EntityGraph(attributePaths = {"postDetail"})
    Page<Post> findAll(Pageable pageable);

    @Override
    @EntityGraph(attributePaths = {"postDetail"})
    List<Post> findAll();
}

【讨论】:

  • 有效!甚至我的 sub 也是独立共享和使用的,没有与 base 的链接(单向关系)。我获取了整个基础表(因为外部 csv 更新)。现在只需几秒钟,您可以节省我的时间!
【解决方案2】:

如果你正在使用Spring Data JPAPost中的id的类型应该与JpaRepository&lt;T, ID extends Serializable&gt;中的ID一致,只需进行一些修改:

@Entity
@Table(name = "post")
class Post {
    @Id
    private Integer id;

    @Column
    private String title;

    @Column
    private String contents;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "postDetail_id")//assume this is the name
    private PostDetail postDetail;
}

而且您不必为getAll() 定义查询,Spring Data JPA 为常用查询提供了一些嵌入式实现,只需调用它们即可:

public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {

    <S extends T> S save(S entity); 

    T findOne(ID primaryKey);       

    Iterable<T> findAll();          

    Long count();                   

    void delete(T entity);          

    boolean exists(ID primaryKey);  

    // … more functionality omitted.
}

所以PostRepository 就像:

public interface PostRepository extends JpaRepository<Post, Integer> {

}

然后你可以在你的单元测试中测试它:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTest {

    @Autowired
    private PostRepository repo;

    @Test
    public void testFindAll(){
        repo.findAll();
    }

    @Test
    public void testFindById(){
        repo.findOne(id);
    }
}

【讨论】:

  • 谢谢,第二个 getAll() 问题解决了。但是,我不能使用 findAll(),因为这会产生太多查询。
  • 最终我应该首先使用 getAll() 进行页面导航。如何将 FETCH JOIN 与可分页一起使用?
  • @chaeyk 我很确定你会意识到问题所在,以及如何解决它,如果你阅读了异常的堆栈跟踪。我要求你发帖,但你没有。提示:当使用 Pageable 时,Spring 需要执行一个查询来统计元素的总数。
  • @JBNizet 不,我找不到解决方案。第二个 getAll() 是我的错。我删除了 PostDetail.post,问题就解决了。但是当有 Example 和 Pageable 参数时,我无法使用 fetch join。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-11
  • 1970-01-01
  • 1970-01-01
  • 2023-02-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多