2016-02-05 4 views
6

В моем приложении я использую данные Spring и спящий режим как поставщик JPA для сохранения и чтения данных.Spring Data JPA date «между» проблемой при использовании параметров даты

У меня есть верхний класс уровень 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; 
} 

Для унаследованного класса У меня есть соответствующий репозиторий:

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

И я интеграционный тест с следующим методом:

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

Но этот тест терпит неудачу, когда я использую базу данных MySql из-за пустого результата (но проходит, когда я переключаю к HSQLDB)

Кроме того, этот тест проходит, если я положил задержку «Thread.sleep (1000)» в течение одной секунды в начале теста сразу после первой строки.

Когда я запускаю SQL из журнала Hibernate, он дает правильный результат. Что случилось с моим кодом?

+0

Я изменил дату на LocalDateTime, но это не решило проблему –

+0

Попробуйте 'ZonedDateTime' –

ответ

7

Для JPA требуется Date. Как правило, вы можете установить TemporalType при установке параметра JPA Query:

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

С весной данными вы должны использовать @Temporal аннотации, так что ваш запрос становится:

@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

Я добавил аннотацию @Temporal (TemporalType.TIMESTAMP) до даты. Но это не дало результата –

+0

Тогда лучше зарегистрировать SQL-запрос с параметрами с помощью P6Spy –

+0

Все кажется правильным. Я выполнил запрос от клиента, и все в порядке (concreop0_1_.begin_timestamp между '2016-02-07 11:44:21' и '2016-02-07 11:44:22') и concreop0_1_.training_mode = false –

0

я понял мою проблему. Проблема была из-за разницы в точности между типом поля в MySql (метка времени по умолчанию точность реза миллисекунд) и даты Java (с миллисекундах) Я изменил мою таблицу:

ALTER TABLE transaction modify end_timestamp TIMESTAMP(6) 

и это решить мою проблему.

+0

Вы имели в виду ["microseconds" для MySQL] (http://dev.mysql.com/doc/refman/5.7/en/datetime.html)? –

+0

Рамка java.time, встроенная в Java 8 и более поздняя, ​​не имеет такой проблемы, обрабатывая наносекундное разрешение (до 9 цифр дробной секунды). Если вы используете 'java.sql.Timestamp' и' java.time.Instant', у вас не будет такой проблемы с потерей данных. Избегайте печально известных классов java.util.Date/.Calendar. –

+0

Да, спасибо за исправление. Я имел в виду «микросекунды». Но микросекунды были отключены MySql. Например, когда я вставляю «2016-02-09 08: 11: 37.567», эта временная метка вставлена ​​в MySql как «2016-02-09 08:11:37». –

Смежные вопросы