Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
303 views
in Technique[技术] by (71.8m points)

java 8 - How to persist JSR-310 types with Spring Data JPA?

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"/>
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...