如何将 fetch join 与 @OneToOne 关系一起使用?

How can I use fetch join with @OneToOne relationship?

我有实体 Post、Post详细信息。 它们是一对一的关系,Post详细信息是可选的。

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

应用启动时出现异常

Caused by: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list ...

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


抱歉,我修改了问题。

两个PostRepository的方法都出错。

首先 getAll() 抛出错误 "query specified join fetching ..."

第二次 getAll() 抛出错误

org.mariadb.jdbc.internal.common.QueryException: Unknown column 'postdetail1_.post_id' in 'field list'

由于您使用的是Spring Data JPAPostid的类型应该与JpaRepository<T, ID extends Serializable>中的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 数据定义查询 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);
    }
}

如果您想避免 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();
}