Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Postgresql 如何使用Hibernate避免springboot中本机SQL查询的N+1问题?_Postgresql_Spring Boot_Hibernate_Postgis_Rawsql - Fatal编程技术网

Postgresql 如何使用Hibernate避免springboot中本机SQL查询的N+1问题?

Postgresql 如何使用Hibernate避免springboot中本机SQL查询的N+1问题?,postgresql,spring-boot,hibernate,postgis,rawsql,Postgresql,Spring Boot,Hibernate,Postgis,Rawsql,我正在使用POSTGIS内置查询我的数据库,以检索给定位置的最近机器。 我必须使用本机SQL,因为Hibernate不支持POSTGIS和CTEs: @Repository public interface MachineRepository extends JpaRepository<Machine, Long>{ @Query(value = "with nearest_machines as\n" +

我正在使用POSTGIS内置查询我的数据库,以检索给定位置的最近机器。 我必须使用本机SQL,因为Hibernate不支持POSTGIS和CTEs:

    @Repository
    public interface MachineRepository extends JpaRepository<Machine, Long>{
    @Query(value =
                "with nearest_machines as\n" +
                "         (\n" +
                "             select distance_between_days(:id_day, machine_availability.id_day) as distance_in_time,\n" +
                "                    ST_Distance(geom\\:\\:geography, ST_SetSrid(ST_MakePoint(:longitude, :latitude), 4326)\\:\\:geography) as distance_in_meters,\n" +
                "                    min(id_day) over (partition by machine.id) as closest_timeslot_per_machine,\n" +
                "                    machine_availability.id_day,\n" +
                "                    machine.*\n" +
                "             from machine\n" +
                "                      join machine_availability on machine.id = machine_availability.id_machine\n" +
                "             where machine_availability.available = true\n" +
                "               and machine_availability.id_day >= :today\n" +
                "               and ST_DWithin(geom\\:\\:geography, ST_SetSrid(ST_MakePoint(:longitude, :latitude), 4326)\\:\\:geography, 1000)\n" +
                "         )\n" +
                "select nearest_machines.*\n" +
                    "from nearest_machines\n" +
                    "where id_day = closest_timeslot_per_machine\n" +
                    "order by distance_in_time, distance_in_meters\n" +
                    "limit 20;",
            nativeQuery = true)
        List<Machine> findMachinesAccordingToAvailabilities(@Param("longitude") BigDecimal longitude,
                                                            @Param("latitude") BigDecimal latitude,
                                                            @Param("id_day") String idDay,
                                                            @Param("today") String today);
}
当然,Machine和MachineAvailability是@Entity的。它们是@OneToManyfetch=FetchType.EAGER相关的。我将默认的LAZY更改为EAGER,因为我需要最终JSON中的MachineAvailability

问题是,它通过产生机器(即著名的N+1问题)再触发2个请求

1.如何在一个请求中解决该问题


2.是否有可能以某种方式在JSON上创建my并直接在MachineController中返回它?

在1个请求中解决此问题很困难,因为您必须使用Hibernate本机API来映射可用性集合的表别名。您需要为主查询中的可用性添加一个联接,并执行如下操作:session.createNativeQuery….addEntitym,Machine.class.addFetchav,m,availabilities

另一种选择是使用Blaze Persistence,因为它支持CTE和PostgreSQL提供的更多功能,这可能是一个有趣的解决方案

我创建了这个库,以便在JPA模型和自定义接口或抽象类定义的模型之间进行简单的映射,类似于类固醇上的Spring数据投影。其思想是以您喜欢的方式定义目标structuredomain模型,并通过JPQL表达式将AttributesGetter映射到实体模型

我不知道您的模型,但您的用例可能的DTO模型在Blaze Persistence实体视图中如下所示:

@EntityView(Machine.class)
@With(NearestMachineCteProvider.class)
@EntityViewRoot(name = "nearest", entity = NearestMachine.class, condition = "machineId = VIEW(id)", joinType = JoinType.INNER)
public interface MachineDto {
    @IdMapping
    Integer getId();
    String getName();
    @Mapping("nearest.distanceInTime")
    Integer getDistanceInTime();
    @Mapping("nearest.distanceInMeters")
    Double getDistanceInMeters();
    Set<MachineAvailabilityDto> getAvailabilities();

    @EntityView(MachineAvailability.class)
    interface MachineAvailabilityDto {
        @IdMapping
        Integer getId();
        String getName();
    }

    class NearestMachineCteProvider implements CTEProvider {
        @Override
        public void applyCtes(CTEBuilder<?> builder, Map<String, Object> optionalParameters) {
            builder.with(NearestMachine.class)
                .from(Machine.class, "m")
                .bind("distanceInTime").select("CAST_INTEGER(FUNCTION('distance_between_days', :id_day, m.availabilities.idDay))")
                .bind("distanceInMeters").select("CAST_DOUBLE(FUNCTION('ST_Distance', m.geom, FUNCTION('ST_SetSrid', FUNCTION('ST_MakePoint', :longitude, :latitude), 4326)))")
                .bind("closestTimeslotId").select("min(m.availabilities.idDay) over (partition by m.id)")
                .bind("machineId").select("m.id")
                .bind("machineAvailabilityDay").select("m.availabilities.idDay")
                .where("m.availabilities.available").eqLiteral(true)
                .where("m.availabilities.idDay").geExpression(":today")
                .where("FUNCTION('ST_DWithin', m.geom, FUNCTION('ST_SetSrid', FUNCTION('ST_MakePoint', :longitude, :latitude), 4326), 1000)").eqLiteral(true)
                .end();
        }
    }
}

@CTE
@Entity
public class NearestMachine {
    private Integer distanceInTime;
    private Double distanceInMeters;
    private Integer closestTimeslotId;
    private Integer machineId;
    private Integer machineAvailabilityDay;
}
然后可以使用sort.ascdanceintime和sort.ascdanceintimeters进行排序


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

这里有点不对劲,因为如果您发出的是本机SQL查询,那么Hibernate N+1问题根本不应该是问题,甚至不应该是可能的。是什么让你认为这里发生了N+1?因为我在日志中看到了请求。然后它们来自原始SQL查询,而不是Hibernate/JPA。好的,当您将native设置为true时,您基本上只是在执行一个本地JDBC准备的语句。它们来自@OneToManyfetch=FetchType.EAGER。当我切换到懒惰,问题消失了,但我没有可用性!!!thx用于addFetch!
Page<MachineDto> findAll(Pageable pageable);