2016-02-05 15 views
6

Trong ứng dụng của tôi, tôi đang sử dụng Spring Data và hibernate như nhà cung cấp JPA để duy trì và đọc dữ liệu.Ngày dữ liệu mùa xuân JPA "giữa" vấn đề truy vấn khi sử dụng các tham số ngày

Tôi có đầu lớp mức Entity:

@Entity 
@Getter @Setter 
@Table(name = "operation") 
@Inheritance(strategy = InheritanceType.JOINED) 
@EqualsAndHashCode(of = {"operationId"}) 
public abstract class Operation implements Serializable { 
    public static final int OPERATION_ID_LENGTH = 20; 

    @Id 
    @Column(name = "operation_id", length = OPERATION_ID_LENGTH, nullable = false, columnDefinition = "char") 
    private String operationId; 

    @Column(name = "operation_type_code") 
    @Getter(AccessLevel.NONE) 
    @Setter(AccessLevel.NONE) 
    private String operationTypeCode; 

    @Temporal(TemporalType.TIMESTAMP) 
    @Column(name = "begin_timestamp", nullable = false) 
    private Date beginTimestamp = new Date(); 

    @Temporal(TemporalType.TIMESTAMP) 
    @Column(name = "end_timestamp") 
    private Date endTimestamp; 

    @Column(name = "operation_number", length = 6, columnDefinition = "char") 
    private String operationNumber; 


    @Enumerated(EnumType.STRING) 
    @Column(name = "operation_status", length = 32, nullable = false) 
    private OperationStatus status; 

    @ManyToOne(optional = false) 
    @JoinColumn(name = "user_id") 
    private User user; 

    @ManyToOne 
    @JoinColumn(name = "terminal_id") 
    private Terminal terminal; 


    @Column(name = "training_mode", nullable = false) 
    private boolean trainingMode; 
} 

Đối với lớp được thừa kế tôi có kho tương ứng:

public interface ConcreteOperationRepository extends JpaRepository<ConcreteOperation, String> { 

    @Query("SELECT o FROM ConcreteOperation o WHERE o.beginTimestamp BETWEEN :from AND :to AND o.status = :status AND o.terminal.deviceId = :deviceId AND o.trainingMode = :trainingMode") 
    Collection<ConcreteOperation> findOperations(@Param("from") Date startDay, 
                @Param("to") Date endDay, 
                @Param("status") OperationStatus status, 
                @Param("deviceId") String deviceId, 
                @Param("trainingMode") boolean trainingMode); 
} 

Và tôi có thử nghiệm tích hợp với các phương pháp sau đây:

@Transactional 
@Test 
public void shouldFindOperationByPeriodAndStatusAndWorkstationId() { 
    Date from = new Date(Calendar.getInstance().getTime().getTime()); 
    List<String> terminalIds = loadTerminalIds(); 
    List<OperationStatus> typeForUse = Arrays.asList(OperationStatus.COMPLETED, 
      OperationStatus.LOCKED, OperationStatus.OPEN); 
    int countRowsForEachType = 3; 
    int id = 100001; 
    for (String terminalId : terminalIds) { 
     for (OperationStatus status : typeForUse) { 
      for (int i = 0; i < countRowsForEachType; i++) { 
       concreteOperationRepository.save(createConcreteOperation(status, terminalId, 
         String.valueOf(++id))); 
      } 
     } 
    } 
    Date to = new Date(Calendar.getInstance().getTime().getTime()); 
    for (String terminalId : terminalIds) { 
     for (OperationStatus status : typeForUse) { 
      Collection<ConcreteOperation> operations = 
        concreteOperationRepository.findOperations(from, to, status, terminalId, false); 
      assertEquals(countRowsForEachType, operations.size()); 
     } 
    } 
} 

Nhưng kiểm tra này không thành công khi tôi sử dụng cơ sở dữ liệu MySql do kết quả trống (nhưng chuyển khi tôi chuyển đổi đến HSQLDB)

Ngoài ra, kiểm tra này sẽ trôi qua nếu tôi trì hoãn "Thread.sleep (1000)" trong một giây ở đầu bài kiểm tra, ngay sau dòng đầu tiên.

Khi tôi thực thi SQL từ nhật ký Hibernate, nó cho tôi kết quả đúng. Có gì sai với mã của tôi?

+0

Tôi đã thay đổi Ngày thành LocalDateTime nhưng không giải quyết được sự cố –

+0

Thử 'ZonedDateTime' –

Trả lời

7

Trong JPA, Date yêu cầu gợi ý tạm thời. Thông thường, bạn có thể thiết lập các TemporalType khi thiết lập các thông số JPA Query:

query.setParameter("from", from), TemporalType.TIMESTAMP); 

Với Data mùa xuân bạn cần phải sử dụng @Temporal chú thích, vì vậy truy vấn của bạn trở thành:

@Query("SELECT o FROM ConcreteOperation o WHERE o.beginTimestamp BETWEEN :from AND :to AND o.status = :status AND o.terminal.deviceId = :deviceId AND o.trainingMode = :trainingMode") 
Collection<ConcreteOperation> findOperations(
    @Param("from") @Temporal(TemporalType.TIMESTAMP) Date startDay, 
    @Param("to") @Temporal(TemporalType.TIMESTAMP) Date endDay, 
    @Param("status") OperationStatus status, 
    @Param("deviceId") String deviceId, 
    @Param("trainingMode") boolean trainingMode 
); 
+0

Tôi đã thêm chú thích @Temporal (TemporalType.TIMESTAMP) trước ngày. Nhưng điều này đã không đưa ra bất kỳ kết quả –

+0

Sau đó tốt hơn đăng nhập truy vấn SQL với các tham số bằng cách sử dụng P6Spy –

+0

Tất cả có vẻ chính xác. Tôi đã thực hiện truy vấn từ ứng dụng khách và tất cả đều là ok (concreop0_1_.begin_timestamp giữa '2016-02-07 11:44:21' và '2016-02-07 11:44:22') và concreop0_1_.training_mode = false –

0

tôi nhận ra vấn đề của tôi. Vấn đề là do sự khác biệt chính xác giữa loại trường trong MySql (timestamp mặc định độ chính xác cắt mili giây) và ngày Java (với mili giây) Tôi đã thay đổi bảng của tôi:

ALTER TABLE transaction modify end_timestamp TIMESTAMP(6) 

và đó là giải quyết vấn đề của tôi.

+0

Ý của bạn là nói [“micro giây” cho MySQL] (http://dev.mysql.com/doc/refman/5.7/en/datetime.html)? –

+0

Khuôn khổ java.time được xây dựng trong Java 8 và sau đó không có vấn đề như vậy, xử lý độ phân giải nano giây (lên đến 9 chữ số của một phân số thứ hai). Nếu bạn sử dụng 'java.sql.Timestamp' và' java.time.Instant', bạn sẽ không gặp vấn đề gì với việc mất dữ liệu. Tránh các lớp java.util.Date/.Calendar gây phiền toái. –

+0

Có, cảm ơn sự điều chỉnh. Tôi có nghĩa là "micro giây". Nhưng micro giây bị cắt bởi MySql. Ví dụ - khi tôi chèn "2016-02-09 08: 11: 37.567" dấu thời gian này được chèn vào MySql là "2016-02-09 08:11:37". –

Các vấn đề liên quan