【问题标题】:Spring Data JPA Receiving parameter value [2] did not match expected type [java.lang.String (n/a)]Spring Data JPA 接收参数值 [2] 与预期类型不匹配 [java.lang.String (n/a)]
【发布时间】:2016-07-16 23:51:01
【问题描述】:

我知道这里已经有人问过类似的问题,但相信我,我做了研究,尝试了很多不同的方法,但到目前为止还没有运气。

我的配置:Spring Data JPA(hibernate,H2 DB),上面的 REST 层并有一个 AngularJS 客户端发出请求。

REST 路径:

@RequestMapping("/search/{destinationId}/{arrivalId}/{departureTime}/{arrivalTime}/{numberOfPassengers}")
    public List<Flight> getFlightsWithCriteria(@PathVariable String destinationId,
                                               @PathVariable String arrivalId,
                                               @PathVariable String departureTime,
                                               @PathVariable String arrivalTime,
                                               @PathVariable Integer numberOfPassengers) { }

我调用的服务如下:

/search/2/1/2016-08-15T21:00:00.000Z/2016-08-15T21:00:00.000Z/2

顺便说一下,departureTime 对象是 DB 上的 TIMESTAMP,测试数据格式为 2016-05-16 17:30:00.0

然后将日期解析为与 DB 匹配的合适格式:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date departureDate = sdf.parse(departureTime);
String formattedTime = dateFormat.format(departureDate);

然后在我的查询中使用这个 formattedTime 参数如下:

flights = (List<Flight>) entityManager
                    .createQuery("SELECT f FROM Flight f WHERE f.departureTerminal = :destination AND f.arrivalTerminal = :arrival AND f.departureTime <= :departureDate AND f.numberOfSeatsAvailable <= :numberOfPassengers")
                    .setParameter("destination", destination)
                    .setParameter("arrival", arrival)
                    .setParameter("departureDate", newDate)
                    .setParameter("numberOfPassengers", numberOfPassengers).getResultList();

那我当然会收到:

java.lang.IllegalArgumentException: Parameter value [2] did not match expected type [java.lang.String (n/a)]

鉴于这些,您可能会说为什么我在查询中发送StringTIMESTAMP 对象,到目前为止我已经尝试过:

  • 发送String查询,
  • 发送Date查询,
  • 发送Date查询TemporalType.TIMESTAMP
  • @PathParam 中使用@DateFormat
  • 将日期类型的整个实体模型更改为String,并在上面一次又一次地尝试

..我已经为此摸索了好几个小时,但还没有解决方案。

你能看看我的代码并指出我做错了什么吗?

谢谢

【问题讨论】:

    标签: javascript java hibernate jpa spring-data


    【解决方案1】:

    通过在适当的地方添加括号来帮助您进行逻辑和调试,例如,尝试清楚查询操作数的分组。下面

    flights = (List<Flight>) entityManager
                        .createQuery("SELECT f FROM Flight f WHERE f.departureTerminal = :destination AND f.arrivalTerminal = :arrival AND (f.departureTime <= :departureDate)  AND (f.numberOfSeatsAvailable <= :numberOfPassengers)")
                        .setParameter("destination", destination)
                        .setParameter("arrival", arrival)
                        .setParameter("departureDate", newDate)
                        .setParameter("numberOfPassengers", numberOfPassengers).getResultList();
    

    再想一想,尝试传递 1 个参数,然后调整您的请求以提供 1 个参数,然后继续执行第 2 个参数,等等。这将准确指出哪个参数有问题。

    一旦你发现了那个罪魁祸首,现在通过作为唯一的请求参数传递来将你的问题简化为那个参数来修复它。

    更新(我的解决方案):

    我对您的代码进行了另一次破解,并且查询很好。我怀疑这是您的输入,就像您报告的异常一样。如果这对您的测试有帮助,我会执行以下操作来测试与您的查询类似的内容。

    FlightTest.java

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = { TestH2DatabaseConfiguration.class }, loader = AnnotationConfigContextLoader.class)
    public class FlightTest extends AbstractTransactionalJUnit4SpringContextTests {
    
        final static Logger log = LoggerFactory.getLogger(FlightTest.class);
    
        @Autowired
        @Qualifier("entityManagerFactory")
        EntityManagerFactory entityManagerFactory;
    
        @Test
        @SuppressWarnings("unchecked")
        public void test() throws ParseException {
    
            assertNotNull(entityManagerFactory);
    
            EntityManager entityManager = entityManagerFactory.createEntityManager();
            assertNotNull(entityManager);
    
            List<Flight> flights = (List<Flight>) entityManager.createQuery("FROM Flight").getResultList();
    
            assertEquals(2, flights.size());
    
    
            String departureTimeRequestInput = "2016-05-16T21:00:00.000Z";
    
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");        
            Date departureDate = sdf.parse(departureTimeRequestInput);
            Timestamp departureTimestamp = new Timestamp(departureDate.getTime());
    
    
            flights = (List<Flight>) entityManager.createQuery(
                      "SELECT f FROM Flight f "
                    + "WHERE "
                    + "f.destinationId = :destinationId "
                    + "and "
                    + "f.arrivalId = :arrivalId "
                    + "and "
                    + "f.departureTime <= :departureDate "
                    + "and "
                    + "f.numberOfPassengers <= :numberOfPassengers")
                    .setParameter("destinationId", 1L)
                    .setParameter("arrivalId", 3L)
                    .setParameter("departureDate", departureTimestamp)          
                    .setParameter("numberOfPassengers", 400L)
                     .getResultList();              
    
            assertEquals(1, flights.size());
    
            log.debug("**H2**SIZE: " + flights.size());     
        }
    
    }
    

    TestH2DatabaseConfiguration.java

    @Configuration
    @EnableJpaRepositories("com.mycompany.h2.jpa")
    @EnableTransactionManagement
    public class TestH2DatabaseConfiguration {
    
        final static Logger log = LoggerFactory.getLogger(TestH2DatabaseConfiguration.class);
    
        @Bean
        @Qualifier("dataSource")
        public DataSource h2DataSource() {
            return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).addScript("classpath:flight.sql").build();
        }
    
        @Bean
        public EntityManagerFactory entityManagerFactory() {
    
            HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
            jpaVendorAdapter.setGenerateDdl(true);
    
            LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
    
            factoryBean.setDataSource(h2DataSource());
            factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
            factoryBean.setPackagesToScan("com.mycompany.h2.jpa");
            factoryBean.setPersistenceUnitName("flight_table");
    
            Properties ps = new Properties();
            ps.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
            ps.put("hibernate.show_sql", "true");
            ps.put("hibernate.hbm2ddl.auto", "none");
            factoryBean.setJpaProperties(ps);       
    
            factoryBean.afterPropertiesSet();
    
    
            return factoryBean.getObject();
        }
    
        @Bean
        public PlatformTransactionManager transactionManager() {
            JpaTransactionManager txManager = new JpaTransactionManager();
            txManager.setEntityManagerFactory(entityManagerFactory());
            return txManager;
        }   
    }
    

    Flight.java

    public class Flight implements Serializable {
    
        private static final long serialVersionUID = 2608721697556397731L;
    
        @Id
        private Long destinationId;
    
        private Long arrivalId;
        private Timestamp departureTime;
        private Timestamp arrivalTime;
        private Long numberOfPassengers;
        ...
    }
    

    flight.sql

    CREATE TABLE flight (
      destinationId      INTEGER PRIMARY KEY,
      arrivalId          INTEGER,
      departureTime      TIMESTAMP,
      arrivalTime        TIMESTAMP,
      numberOfPassengers INTEGER
    );
    
    INSERT INTO flight VALUES (1, 3, '2016-05-16 17:30:00.0', '2016-05-18 17:30:00.0', 400);
    INSERT INTO flight VALUES (2, 2, '2016-05-17 17:30:00.0', '2016-05-19 17:30:00.0', 200);
    

    关键点: 这里的关键点是将您的用户输入时间传递到 java.sql.Timestamp 对象中,如 FlightTest.java 中所示,例如

            String departureTimeRequestInput = "2016-05-16T21:00:00.000Z";
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");        
            Date departureDate = sdf.parse(departureTimeRequestInput);
            Timestamp departureTimestamp = new Timestamp(departureDate.getTime());
    

    ...
    .setParameter("departureDate", departureTimestamp)
    ...
    

    更新 2: 在下面的事务中插入示例 Flight 对象

    EntityManager entityManager = null;
    try {
        entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        entityManager.persist(flightObject);
        entityManager.flush();
        transaction.commit();           
        return flightObject;
    }
    finally {
        entityManager.close();
    }
    

    【讨论】:

    • 我刚刚重构了整个项目(当然包括单元测试),将日期类型字段切换为时间戳类型。当我运行该应用程序并保留一些初始数据时,它工作正常并按我的预期插入了所有内容(映射到 H2 DB 中的 TIMESTAMP)。但是我正在尝试使用您共享的示例代码查询数据(发送时间戳作为参数)但没有运气......我真的没有选择,我该怎么办?
    • 我认为您现在可以做的是通过执行一般查询(例如实体 id 而不是时间戳)来列出您插入的记录是否可见,然后使用其他条件参数缩小查询结果过滤范围。最后,使用时间戳过滤。为确保您的实体确实被插入并且其 id 可用,请在事务中执行它们并刷新。
    • 请检查上面的示例代码我在事务中的意思。当然这不是最理想的,因为它不是服务类中的 Spring 托管事务,用于测试就足够了。
    • 只是让您知道,如果您希望 Spring 为您管理事务,您可以按照我也在此回答的 stackoverflow.com/questions/38307395/… 链接中的示例进行操作。我之所以这么说是因为我不想在这里重新创建这些示例。
    猜你喜欢
    • 2022-11-10
    • 2015-08-17
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 2020-06-22
    • 2019-09-03
    • 2019-10-22
    • 1970-01-01
    相关资源
    最近更新 更多