使用nativeQuery时Spring Data JPA n + 1查询问题

o8x7eapl  于 7个月前  发布在  Spring
关注(0)|答案(1)|浏览(58)

考虑两个实体ThreadUser,每个线程都属于一个用户,ThreadRepository中有两个方法:

@Repository
public interface ThreadRepository extends JpaRepository<Thread, Long> {

    @EntityGraph(type = EntityGraph.EntityGraphType.FETCH, attributePaths = {"user"})
    Page<Thread> findAllByOrderByCreatedDateDesc(Pageable pageable);

    @Query(value = "SELECT T.*, U.* FROM THREAD T LEFT JOIN FRM_USER U ON U.id = T.USER",
            countQuery = "SELECT COUNT(T.ID) FROM THREAD T",
            nativeQuery = true)
    Page<Thread> findAllCustomMethod(Pageable pageable);
}

字符串
我可以使用@EntityGraph处理findAllByOrderByCreatedDateDesc方法的 *n + 1个查询问题 *。
我的问题是,对于包含nativeQueryfindAllCustomMethod方法,解决这个问题的最佳解决方案是什么?

ymdaylpp

ymdaylpp1#

LEFT JOIN FRM_USER在这种情况下没有使用,Spring Data JPA只会将T.*字段Map到Thread,忽略U.*字段。
相反,它会导致重复!
JPA实现将延迟选择User示例,从而导致N+1问题。
你不能使用投影,因为“基于类的投影根本不适用于本机查询”。
在这种情况下,nativeQuery没有JPQL查询的好处,在JPQL查询中,您可以使用LEFT JOIN FETCH来急切地获取User示例并避免N+1。
Spring Data在这里没有帮助。
如果您确实需要SQL查询,可以使用SqlResultSetMapping。
当使用SQL查询(又名. native queries)时,您可以从Spring的JdbcTemplate.query方法中受益,该方法具有自定义ResultSetExtractor,该方法将使用自定义实现构建您的Object。使用ResultSetExtractor实现这种精确情况的解决方案非常简单,但分页部分将非常冗长。

public class ThreadJdbcRepository {

    private static final String QUERY = """
        SELECT t.*, u.*
        FROM thread t
        LEFT JOIN frm_user u ON u.id = t.user
        """;

    private final JdbcTemplate jdbcTemplate;

    public ThreadJdbcRepository (JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public Collection<Thread> findAllCustomMethod() {
        return jdbcTemplate.query(QUERY, rs -> {
            Map<Long, Thread> idToThread = new HashMap<>();
            while (rs.next()) {
                Thread thread;
                Long id = rs.getLong("id");
                thread = idToThread.computeIfAbsent(id, k ->
                    new Thread()
                    // build thread...
                );
                User user = new User();
                // build user...
                thread.addUser(user);
            }
            return idToThread.values();
        });
    }
}

字符串

相关问题