Spring数据分页查询在H2和MySQL之间给出不同的结果

时间:2018-09-18 23:06:19

标签: mysql spring-boot spring-data-jpa spring-data h2

我正在使用Spring Boot 2.0.5,Spring Data和Hibernate / JPA。对于嵌入式数据库,我使用H2;对于暂存/生产,我使用MySQL。我无法将此代码发布在GitHub / GitLab存储库中,因为它是专有的,因此我将在此处尽力呈现:

我有一个分页控制器

@RestController
@RequestMapping("/api")
public class AircraftListController {

    private static final int DEFAULT_PAGE_NUMBER = 0;
    private static final int DEFAULT_PAGE_SIZE = 10;

    private AircraftService aircraftService;

    @Autowired
    public AircraftListController(AircraftService aircraftService) {
        this.aircraftService = aircraftService;
    }

    @Secured("ROLE_ADMIN")
    @GetMapping(value = {"/maintainers/{mid}/aircrafts"}, produces = "application/json")
    @ResponseStatus(value = HttpStatus.OK)
    Response<Page<AircraftRow>> getPagedMaintainers(
            @PathVariable("mid") Optional<Long> maintainerId,
            @PageableDefault(page = DEFAULT_PAGE_NUMBER, size = DEFAULT_PAGE_SIZE)
            @SortDefault.SortDefaults({
                    @SortDefault(sort = "a.registration", direction = Sort.Direction.ASC)
            }) Pageable pageable) {

        Page<AircraftRow> aircraft = aircraftService.findSortedSummary(maintainerId.get(), pageable);


        return Response.of(aircraft);
    }
}

主要模型类型为: 飞机:

@Entity
public class Aircraft {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, updatable = false)
    private String registration;

    private String make;

    private String model;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    private LocalDate manufacture;

    @ManyToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER)
    private Client owner;

    @ManyToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER)
    private Client operator;

    private String base;

    @OneToOne(cascade={CascadeType.ALL})
    @JoinColumn(name = "airframe_id")
    private Airframe airframe;

    @OneToMany(cascade={CascadeType.ALL}, fetch = FetchType.EAGER)
    @JoinColumn(name = "aircraft_id")
    private Set<Prop> props;

    @OneToMany(cascade={CascadeType.ALL}, fetch = FetchType.EAGER)
    @JoinColumn(name = "aircraft_id")
    private Set<Engine> engines;

    // builder, accessors, hashCode, equals, and toString omitted
}

MaintenanceContract:

@Entity
public class MaintenanceContract {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, updatable = false)
    private String nk;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "maintainer_id")
    @JsonBackReference
    private Maintainer maintainer;

    private boolean current;

    private String image;

    @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER)
    @JoinColumn(name = "aircraft_id")
    private Aircraft aircraft;

    @OneToOne
    @JoinColumn(name = "primary_contact_id")
    private Client primaryContact;

    @OneToMany(cascade = {CascadeType.ALL})
    @JoinColumn(name = "maintenance_contract_id", referencedColumnName = "id")
    private Set<WorkOrder> workOrders = new HashSet<>();

    @OneToMany
    @JoinTable(name = "maintenance_contract_outstanding_ad",
            joinColumns={ @JoinColumn(name="maintenance_contract_id", referencedColumnName="id") },
            inverseJoinColumns={ @JoinColumn(name="outstanding_ad_id", referencedColumnName="id") }
    )
    private Set<AirworthinessDirective> outstandingAds = new HashSet<>();

    @OneToMany
    @JoinTable(name = "maintenance_contract_completed_ad",
            joinColumns={ @JoinColumn(name="maintenance_contract_id", referencedColumnName="id") },
            inverseJoinColumns={ @JoinColumn(name="completed_ad_id", referencedColumnName="id") }
    )
    private Set<AirworthinessDirective> completedAds = new HashSet<>();
}

客户:

@Entity
public class Client {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Access(AccessType.PROPERTY)
    @JsonView({View.ClientView.class, View.AircraftView.class})
    private Long id;

    @Column(unique = true, updatable = false)
    @JsonView({View.ClientView.class, View.AircraftView.class})
    private String nk;

    @Min(10000000000L)
    @JsonView({View.ClientView.class, View.AircraftView.class})
    private Long abn;

    @JsonView({View.ClientView.class, View.AircraftView.class})
    private String name;

    @OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
    @JoinColumn(name = "person_id")
    @JsonView({View.ClientView.class, View.AircraftView.class})
    private Person principal;

    @OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
    @JoinColumn(name = "contact_id")
    @JsonView({View.ClientView.class, View.AircraftView.class})
    private Contact contact;

    @OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
    @JoinColumn(name = "address_id")
    @JsonView({View.ClientView.class, View.AircraftView.class})
    private Address address;
}

...和工作指令:

@Entity
public class WorkOrder {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, updatable = false)
    private String nk;

    private String workOrderNumber;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    private LocalDate date;

    private Integer hours;

    private String title;

    @Enumerated(EnumType.STRING)
    @Column(length = 12)
    private WorkOrderStatus status;

    private String notes;

    @OneToMany(cascade = {CascadeType.ALL})
    @JoinColumn(name = "work_order_id", referencedColumnName = "id")
    private Set<WorkSet> workSets = new HashSet<>();

    @ManyToMany(fetch = FetchType.EAGER, cascade = {
            CascadeType.PERSIST,
            CascadeType.MERGE})
    @JoinTable(name = "work_order_airworthiness_directive",
            joinColumns = @JoinColumn(name = "work_order_id"),
            inverseJoinColumns = @JoinColumn(name = "airworthiness_directive_id"))
    private Set<AirworthinessDirective> attachedAds = new HashSet<>();

    @Column(name="maintenance_contract_id")
    private Long contractId;
}

有一些服务层,但到目前为止它们似乎都已经起作用,相关的存储库如下:

@Repository
public interface AircraftRepository extends JpaRepository<Aircraft, Long> {


    @Query(value = "select new au.com.avmaint.api.aircraft.model.AircraftRow(a.id, mc.id, wo.id, wo.date, a.registration, a.make, a.model, "
            + "c.principal.id, c.principal.firstName, c.principal.lastName, c.contact.phone, mc.current) "
                    + "from MaintenanceContract mc, WorkOrder wo "
                    + "inner join mc.primaryContact c "
                    + "inner join mc.aircraft a "
                    + "where mc.maintainer.id = ?1 "
                    + "and wo.contractId = mc.id "
 + "and wo.date = (select max(wo2.date) from WorkOrder wo2 where wo2.contractId = wo.contractId)",
            countQuery = "select count(*) "
                    + "from MaintenanceContract mc, WorkOrder wo "
                    + "inner join mc.primaryContact c "
                    + "inner join mc.aircraft a "
                    + "where mc.maintainer.id = ?1 "
                    + "and wo.contractId = mc.id "
 + "and wo.date = (select max(wo2.date) from WorkOrder wo2 where wo2.contractId = wo.contractId)")
    Page<AircraftRow> findSortedSummary(Long maintainerId, Pageable pageable);

}

我实际上从日志中拉出了结果查询,将其清理并用于查询MySQL:

select
    a.id as aid,
    mc.id as mcid,
    wo.id as woid,
    wo.date as woDate,
    a.registration as reg,
    a.make as make,
    a.model as model,
    client.person_id as pid,
    p.first_name as firstName,
    p.last_name as lastName,
    contact.phone as phone,
    mc.current as current
from
    maintenance_contract mc
inner join client client on mc.primary_contact_id=client.id
cross join person p
cross join contact contact
inner join aircraft a on mc.aircraft_id=a.id
cross join work_order wo
where
    client.person_id=p.id
    and client.contact_id=contact.id
    and mc.maintainer_id=1
    and wo.maintenance_contract_id=mc.id
    and wo.date=(
        select
            max(wo2.date)
        from
            work_order wo2
        where
            wo2.maintenance_contract_id=wo.maintenance_contract_id
    )
order by
    a.registration asc limit 4

我发现在H2中,存储库什么都不选择,而MySQL中完全相同的数据设置返回:

enter image description here

这是预期的结果。那么H2怎么了?另一件事是H2条件在功能测试中,而MySQL在主应用程序中完成。那么也许这与测试上下文有关?

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("embedded")
@EnableJpaRepositories({ "au.com.avmaint.api" })
@AutoConfigureMockMvc
public class AircraftListControllerFunctionalTest {

尽管通常这不是一个因素。我感觉H2不支持映射中的某些内容,因此无法在查询中选择任何内容,但是我不确定。

0 个答案:

没有答案