问题描述
我正在尝试将Spring Data JPA 1.8与Java 8 Date / Time API JSR-310一起使用。
一切似乎都有效,直到我尝试全部两个LocalDateTimes之间的车辆。
返回的实体数量似乎与它应该的数量只有松散的相关性。
实体
@Repository
public interface VehicleRepository扩展JpaRepository< Vehicle,Long> {
列表< Vehicle> findByDateTimeBetween(LocalDateTime begin,LocalDateTime end);
}
存储库
@Entity
@Table(name =VEHICLE)
public class Vehicle implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name =IDX,nullable = false,unique = true)
@GeneratedValue(strategy = GenerationType.AUTO)
private long vehicleId;
@Column(name =DATE_TIME,nullable = false)
private LocalDateTime dateTime = LocalDateTime.now();
// Getters and Setters
}
相关依赖
< dependency>
< groupId> org.springframework.data< / groupId>
< artifactId> spring-data-jpa< / artifactId>
< version> 1.8.0.RELEASE< / version>
< / dependency>
< dependency>
< groupId> org.springframework< / groupId>
< artifactId> spring-aspects< / artifactId>
< version> 4.0.9.RELEASE< / version>
< / dependency>
< dependency>
< groupId> org.hibernate< / groupId>
< artifactId> hibernate-core< / artifactId>
< version> 4.3.8.Final< / version>
< / dependency>
< dependency>
< groupId> org.hibernate< / groupId>
< artifactId> hibernate-entitymanager< / artifactId>
< version> 4.3.8.Final< / version>
< / dependency>
< dependency>
< groupId> com.h2database< / groupId>
< artifactId> h2< / artifactId>
< version> 1.4.186< / version>
< / dependency>
< dependency>
< groupId> com.zaxxer< / groupId>
< artifactId> HikariCP< / artifactId>
< version> 2.3.5< / version>
< / dependency>
测试失败示例
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({classpath *:applicationContextTesting.xml})
@Transactional
public class VehicleRepositoryTest {
@Autowired
private VehicleRepository vehicleRepository;
@Test
public void testVehicleBetween(){
//给定
Vehicle vehicleMarch1Twelve = new Vehicle();
Vehicle vehicleMarch1Eighteen = new Vehicle();
Vehicle vehicleMarch2Five = new Vehicle();
Vehicle vehicleMarch2Six = new Vehicle();
LocalDateTime march1Twelve = LocalDateTime.of(2015,Month.MARCH,1,12,0);
LocalDateTime march1Eighteen = LocalDateTime.of(2015,Month.MARCH,1,18,0);
LocalDateTime march2Five = LocalDateTime.of(2015,Month.MARCH,2,5,0);
LocalDateTime march2Six = LocalDateTime.of(2015,Month.MARCH,2,6,0);
vehicleMarch1Twelve.setDateTime(march1Twelve);
vehicleMarch1Eighteen.setDateTime(march1Eighteen);
vehicleMarch2Five.setDateTime(march2Five);
vehicleMarch2Six.setDateTime(march2Six);
vehicleRepository.save(vehicleMarch1Twelve);
vehicleRepository.save(vehicleMarch1Eighteen);
vehicleRepository.save(vehicleMarch2Five);
vehicleRepository.save(vehicleMarch2Six);
vehicleRepository.flush();
//当
List< Vehicle> allVehicles = vehicleRepository.findByDateTimeBetween(
march1Twelve,
march2Six);
列表< Vehicle> allVehicles2 = vehicleRepository.findByDateTimeBetween(
march1Twelve.minusMinutes(2),
march2Six.plusMinutes(2));
列表< Vehicle> threeVehicles = vehicleRepository.findByDateTimeBetween(
march1Twelve.plusMinutes(2),
march2Six);
列表< Vehicle> twoVehicles = vehicleRepository.findByDateTimeBetween(
march1Twelve.plusMinutes(2),
march2Six.minusMinutes(2));
列表< Vehicle> oneVehicles = vehicleRepository.findByDateTimeBetween(
march1Twelve.plusMinutes(2),
march2Six.minusHours(3));
//然后
Assert.assertTrue(size was+ allVehicles.size(),allVehicles.size()== 4);
Assert.assertTrue(size was+ allVehicles2.size(),allVehicles2.size()== 4);
Assert.assertTrue(size was+ threeVehicles.size(),threeVehicles.size()== 3);
Assert.assertTrue(size was+ twoVehicles.size(),twoVehicles.size()== 2);
Assert.assertTrue(size was+ oneVehicles.size(),oneVehicles.size()== 1);
Assert.assertTrue(oneVehicles.get(0).getDateTime()。equals(march1Eighteen));
}
}
第一个List包含2个元素(应该是4)。所有其他列表包含 0 元素!
鉴于第二个请求的时间跨度比第一个要大。
有人可以告诉我我做错了吗?
编辑
谢谢快速回答。
我能够通过将org.springframework.data.jpa.convert.threeten添加到packagesToScan属性来解决此问题。现在它似乎正常工作。
这里的参考是我工作的数据库相关(测试)配置。
< bean id =hikariConfigclass =com.zaxxer.hikari.HikariConfig>
< property name =driverClassNamevalue =org.h2.Driver/>
< property name =jdbcUrlvalue =jdbc:h2:mem:testing/>
< property name =usernamevalue =interface/>
< property name =passwordvalue =/>
< property name =connectionTestQueryvalue =SELECT 1/>
< / bean>
< bean id =dataSourceclass =com.zaxxer.hikari.HikariDataSource>
< constructor-arg index =0ref =hikariConfig/>
< / bean>
< bean id =transactionManagerclass =org.springframework.orm.jpa.JpaTransactionManager>
< property name =entityManagerFactoryref =entityManagerFactory/>
< / bean>
< tx:annotation-driven />
< bean id =hibernateJpaVendorAdapterclass =org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter/>
< bean id =entityManagerFactoryclass =org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean>
< property name =dataSourceref =dataSource/>
< property name =jpaVendorAdapterref =hibernateJpaVendorAdapter/>
< property name =packagesToScanvalue =com.company.project.domain,org.springframework.data.jpa.convert.threeten/>
< property name =jpaProperties>
< props>
< prop key =hibernate.dialect> org.hibernate.dialect.H2Dialect< / prop>
< prop key =hibernate.hbm2ddl.auto> update< / prop>
< / props>
< / property>
< / bean>
< jpa:repositories base-package =com.company.project.daotransaction-manager-ref =transactionManager
entity-manager-factory-ref =entityManagerFactory />
更新:如果您需要,以下答案有效继续使用Hibernate版本< 5.0。 Hibernate 5.0支持开箱即用的持久化JSR-310日期/时间类型。即如果您使用的是Hibernate 5.0或更高版本,那么即可。其他人,请继续阅读。
这一点的根本原因在于,没有一个广泛使用的JPA提供商实际上支持开箱即用的JSR-310类型。但是,从Spring Data JPA 1.8.0开始,我们发布了JPA 2.0转换器,它将非时区划分的JSR-310类型转换为遗留的 Date
,以便它们可以保留为了实现这个目的,只需注册 org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters
作为您的提供商的托管JPA类之一。有几种方法可以做到这一点:在非常标准的JPA设置中,您可以在 persistence.xml
中列出它。在基于 LocalContainerEntityManagerFactoryBean
的设置中,您只需将该类的包添加到 packagesToScan
属性即可。如果你正在使用Spring Boot将类添加到 @EntityScan
注释就可以了。
后者是更详细地描述了,涵盖Spring数据发布列车名为Fowler的新功能
I am trying to use Spring Data JPA 1.8 with the Java 8 Date/Time API JSR-310.
Everything seems to work, until I try to get all Vehicles between two LocalDateTimes.The number of Entities returned seems to only have a loose correlation with the number it should.
Entity
@Repository
public interface VehicleRepository extends JpaRepository<Vehicle, Long> {
List<Vehicle> findByDateTimeBetween(LocalDateTime begin, LocalDateTime end);
}
Repository
@Entity
@Table(name = "VEHICLE")
public class Vehicle implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "IDX", nullable = false, unique = true)
@GeneratedValue(strategy = GenerationType.AUTO)
private long vehicleId;
@Column(name = "DATE_TIME", nullable = false)
private LocalDateTime dateTime = LocalDateTime.now();
// Getters and Setters
}
Relevant Dependencies
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.8.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.8.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.3.8.Final</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.186</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.3.5</version>
</dependency>
Failing Test Example
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath*:applicationContextTesting.xml"})
@Transactional
public class VehicleRepositoryTest {
@Autowired
private VehicleRepository vehicleRepository;
@Test
public void testVehicleBetween() {
// Given
Vehicle vehicleMarch1Twelve = new Vehicle();
Vehicle vehicleMarch1Eighteen = new Vehicle();
Vehicle vehicleMarch2Five = new Vehicle();
Vehicle vehicleMarch2Six = new Vehicle();
LocalDateTime march1Twelve = LocalDateTime.of(2015, Month.MARCH, 1, 12, 0);
LocalDateTime march1Eighteen = LocalDateTime.of(2015, Month.MARCH, 1, 18, 0);
LocalDateTime march2Five = LocalDateTime.of(2015, Month.MARCH, 2, 5, 0);
LocalDateTime march2Six = LocalDateTime.of(2015, Month.MARCH, 2, 6, 0);
vehicleMarch1Twelve.setDateTime(march1Twelve);
vehicleMarch1Eighteen.setDateTime(march1Eighteen);
vehicleMarch2Five.setDateTime(march2Five);
vehicleMarch2Six.setDateTime(march2Six);
vehicleRepository.save(vehicleMarch1Twelve);
vehicleRepository.save(vehicleMarch1Eighteen);
vehicleRepository.save(vehicleMarch2Five);
vehicleRepository.save(vehicleMarch2Six);
vehicleRepository.flush();
// when
List<Vehicle> allVehicles = vehicleRepository.findByDateTimeBetween(
march1Twelve,
march2Six);
List<Vehicle> allVehicles2 = vehicleRepository.findByDateTimeBetween(
march1Twelve.minusMinutes(2),
march2Six.plusMinutes(2));
List<Vehicle> threeVehicles = vehicleRepository.findByDateTimeBetween(
march1Twelve.plusMinutes(2),
march2Six);
List<Vehicle> twoVehicles = vehicleRepository.findByDateTimeBetween(
march1Twelve.plusMinutes(2),
march2Six.minusMinutes(2));
List<Vehicle> oneVehicles = vehicleRepository.findByDateTimeBetween(
march1Twelve.plusMinutes(2),
march2Six.minusHours(3));
// then
Assert.assertTrue("size was " + allVehicles.size(), allVehicles.size() == 4);
Assert.assertTrue("size was " + allVehicles2.size(), allVehicles2.size() == 4);
Assert.assertTrue("size was " + threeVehicles.size(), threeVehicles.size() == 3);
Assert.assertTrue("size was " + twoVehicles.size(), twoVehicles.size() == 2);
Assert.assertTrue("size was " + oneVehicles.size(), oneVehicles.size() == 1);
Assert.assertTrue(oneVehicles.get(0).getDateTime().equals(march1Eighteen));
}
}
The first List contains 2 elements (should be 4). All other List contain 0 elements!Given the second request is for a greater timespan than the first.
Can someone tell me what I am doing wrong?
Edit
Thank you @Oliver Gierke for the quick answer.I was able to fix the issue by adding "org.springframework.data.jpa.convert.threeten" to the packagesToScan property. Now it seems to work properly.
As reference here is my working Database related (testing) configuration.
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="driverClassName" value="org.h2.Driver"/>
<property name="jdbcUrl" value="jdbc:h2:mem:testing"/>
<property name="username" value="interface"/>
<property name="password" value=""/>
<property name="connectionTestQuery" value="SELECT 1" />
</bean>
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<constructor-arg index="0" ref="hikariConfig"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven/>
<bean id="hibernateJpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter"/>
<property name="packagesToScan" value="com.company.project.domain,org.springframework.data.jpa.convert.threeten"/>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<jpa:repositories base-package="com.company.project.dao" transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactory"/>
UPDATE: The answer below is valid if you need to stay on a Hibernate version < 5.0. Hibernate 5.0 supports persisting JSR-310 date/time types out of the box. I.e. if you are on Hibernate 5.0 or newer, Adam's answer is the way to go. Everyone else, read on.
The root cause for this at none of the widely used JPA providers actually support JSR-310 types out of the box yet. However, as of Spring Data JPA 1.8.0 we ship JPA 2.0 converters that will translate non-time-zoned JSR-310 types into a legacy Date
so that they can be persisted as is.
To get this working, simply register org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters
as one of the managed JPA classes with your provider. There's a couple of way to do that: in a very standard JPA setup you list it in your persistence.xml
. In a LocalContainerEntityManagerFactoryBean
based setup you can just add the package of the class to the packagesToScan
property. If you're using Spring Boot adding the class to the @EntityScan
annotation does the trick.
The latter is described in a bit more detail in the blog post covering the new features the Spring Data release train named Fowler ships
这篇关于如何使用Spring Data JPA保留JSR-310类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!