Spring Data JPA 接收参数值 [2] 与预期类型 [java.lang.String (n/a)] 不匹配

Spring Data JPA Receiving parameter value [2] did not match expected type [java.lang.String (n/a)]

本文关键字:String lang java 不匹配 类型 JPA Data 参数 Spring      更新时间:2023-09-26

我知道这里已经提出了类似的问题,但相信我,我做了研究,尝试了很多不同的方法,但还没有运气。

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

休息路径:

@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对象是数据库上的TIMESTAMP,测试数据的格式为 2016-05-16 17:30:00.0

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

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

然后在我的查询中使用此格式化时间参数,如下所示:

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)]

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

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

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

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

谢谢

尝试通过在适当的位置添加括号来明确查询操作数的分组,以帮助您进行逻辑和调试,例如下面

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 个参数,然后继续第二个参数,依此类推。这将准确指出哪个参数有问题。

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

更新日期(我的解决方案):

我对你的代码又有一次破解,查询很好。我怀疑这是您的输入是问题,例如您报告的异常。这是我为测试所做的,如果这有助于您的测试,则与您的查询类似。

飞行测试.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;
    }   
}

飞行.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;
    ...
}

飞行.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:在下面的事务中插入飞行对象示例

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