Spring Batch教程实践遇BadSqlGrammarException:PEOPLE表不存在或权限不足
问题描述
我正在跟着Spring官方的《创建批处理服务》入门指南实操,指南用的是HyperSQL数据库。我完全按步骤来,却碰到了BadSqlGrammarException错误,提示“user lacks privilege or object not found: PEOPLE”,对应的SQL语句是INSERT INTO people(first_name,last_name) VALUES (?, ?)。下面是完整的错误栈和我的BatchConfiguration配置类代码,作为新手我搜了相关问题但没找到解决办法。
错误栈信息
org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; 错误的SQL语法 [INSERT INTO people(first_name,last_name) VALUES (?, ?)] at org.springframework.jdbc.support.SQLExceptionSubclassTranslator.doTranslate(SQLExceptionSubclassTranslator.java:101) ~[spring-jdbc-6.0.4.jar:6.0.4] at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:70) ~[spring-jdbc-6.0.4.jar:6.0.4] at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1538) ~[spring-jdbc-6.0.4.jar:6.0.4] at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:667) ~[spring-jdbc-6.0.4.jar:6.0.4] at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:691) ~[spring-jdbc-6.0.4.jar:6.0.4] at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:1034) ~[spring-jdbc-6.0.4.jar:6.0.4] at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.batchUpdate(NamedParameterJdbcTemplate.java:373) ~[spring-jdbc-6.0.4.jar:6.0.4] at org.springframework.batch.item.database.JdbcBatchItemWriter.write(JdbcBatchItemWriter.java:190) ~[spring-batch-infrastructure-5.0.0.jar:5.0.0] at org.springframework.batch.core.step.item.SimpleChunkProcessor.writeItems(SimpleChunkProcessor.java:203) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.step.item.SimpleChunkProcessor.doWrite(SimpleChunkProcessor.java:170) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.step.item.SimpleChunkProcessor.write(SimpleChunkProcessor.java:297) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:227) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:75) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:389) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:313) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-6.0.4.jar:6.0.4] at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:256) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:362) ~[spring-batch-infrastructure-5.0.0.jar:5.0.0] at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:206) ~[spring-batch-infrastructure-5.0.0.jar:5.0.0] at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:139) ~[spring-batch-infrastructure-5.0.0.jar:5.0.0] at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:241) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:227) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:153) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:68) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:68) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:167) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:142) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:137) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:316) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:157) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-6.0.4.jar:6.0.4] at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:148) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.batch.core.launch.support.TaskExecutorJobLauncher.run(TaskExecutorJobLauncher.java:70) ~[spring-batch-core-5.0.0.jar:5.0.0] at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.execute(JobLauncherApplicationRunner.java:199) ~[spring-boot-autoconfigure-3.0.2.jar:3.0.2] at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.executeLocalJobs(JobLauncherApplicationRunner.java:178) ~[spring-boot-autoconfigure-3.0.2.jar:3.0.2] at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.launchJobFromProperties(JobLauncherApplicationRunner.java:166) ~[spring-boot-autoconfigure-3.0.2.jar:3.0.2] at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:161) ~[spring-boot-autoconfigure-3.0.2.jar:3.0.2] at org.springframework.boot.autoconfigure.batch.JobLauncherApplicationRunner.run(JobLauncherApplicationRunner.java:156) ~[spring-boot-autoconfigure-3.0.2.jar:3.0.2] at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:758) ~[spring-boot-3.0.2.jar:3.0.2] at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:748) ~[spring-boot-3.0.2.jar:3.0.2] at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-3.0.2.jar:3.0.2] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) ~[spring-boot-3.0.2.jar:3.0.2] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) ~[spring-boot-3.0.2.jar:3.0.2] at com.example.batchprocessing.DemoApplication.main(DemoApplication.java:10) ~[classes/:na] Caused by: java.sql.SQLSyntaxErrorException: 用户权限不足或对象不存在:PEOPLE,语句为 [INSERT INTO people(first_name,last_name) VALUES (?, ?)] at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source) ~[hsqldb-2.7.1.jar:2.7.1] at org.hsqldb.jdbc.JDBCUtil.sqlException(Unknown Source) ~[hsqldb-2.7.1.jar:2.7.1] at org.hsqldb.jdbc.JDBCPreparedStatement.<init>(Unknown Source) ~[hsqldb-2.7.1.jar:2.7.1] at org.hsqldb.jdbc.JDBCConnection.prepareStatement(Unknown Source) ~[hsqldb-2.7.1.jar:2.7.1] at com.zaxxer.hikari.pool.ProxyConnection.prepareStatement(ProxyConnection.java:327) ~[HikariCP-5.0.1.jar:na] at com.zaxxer.hikari.pool.HikariProxyConnection.prepareStatement(HikariProxyConnection.java) ~[HikariCP-5.0.1.jar:na] at org.springframework.jdbc.core.JdbcTemplate$SimplePreparedStatementCreator.createPreparedStatement(JdbcTemplate.java:1637) ~[spring-jdbc-6.0.4.jar:6.0.4] at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:649) ~[spring-jdbc-6.0.4.jar:6.0.4] ... 41 common frames omitted Caused by: org.hsqldb.HsqlException: 用户权限不足或对象不存在:PEOPLE at org.hsqldb.error.Error.error(Unknown Source) ~[hsqldb-2.7.1.jar:2.7.1] at org.hsqldb.error.Error.error(Unknown Source) ~[hsqldb-2.7.1.jar:2.7.1] at org.hsqldb.ParserDQL.readTableName(Unknown Source) ~[hsqldb-2.7.1.jar:2.7.1] at org.hsqldb.ParserDQL.readRangeVariableForDataChange(Unknown Source) ~[hsqldb-2.7.1.jar:2.7.1] at org.hsqldb.ParserDML.compileInsertStatement(Unknown Source) ~[hsqldb-2.7.1.jar:2.7.1] at org.hsqldb.ParserCommand.compilePart(Unknown Source) ~[hsqldb-2.7.1.jar:2.7.1] at org.hsqldb.ParserCommand.compileStatement(Unknown Source) ~[hsqldb-2.7.1.jar:2.7.1] at org.hsqldb.Session.compileStatement(Unknown Source) ~[hsqldb-2.7.1.jar:2.7.1] at org.hsqldb.StatementManager.compile(Unknown Source) ~[hsqldb-2.7.1.jar:2.7.1] at org.hsqldb.Session.execute(Unknown Source) ~[hsqldb-2.7.1.jar:2.7.1] ... 47 common frames omitted
BatchConfiguration配置类代码
package com.example.batchprocessing; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.job.builder.JobBuilder; import org.springframework.batch.core.launch.support.RunIdIncrementer; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.step.builder.StepBuilder; import org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider; import org.springframework.batch.item.database.JdbcBatchItemWriter; import org.springframework.batch.item.database.builder.JdbcBatchItemWriterBuilder; import org.springframework.batch.item.file.FlatFileItemReader; import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder; import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; @Configuration public class BatchConfiguration { @Bean public FlatFileItemReader<Person> reader() { return new FlatFileItemReaderBuilder<Person>() .name("personItemReader") .resource(new ClassPathResource("sample-data.csv")) .delimited() .names(new String[]{"firstName", "lastName"}) .fieldSetMapper(new BeanWrapperFieldSetMapper<Person>() {{ setTargetType(Person.class); }}) .build(); } @Bean public PersonItemProcessor processor() { return new PersonItemProcessor(); } @Bean public JdbcBatchItemWriter<Person> writer(DataSource dataSource) { return new JdbcBatchItemWriterBuilder<Person>() .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>()) .sql("INSERT INTO people(first_name,last_name) VALUES (:firstName, :lastName)")//此处报错 .dataSource(dataSource) .build(); } @Bean public Job importUserJob(JobRepository jobRepository, JobCompletionNotificationListener listener, Step step1) { return new JobBuilder("importUserJob", jobRepository) .incrementer(new RunIdIncrementer()) .listener(listener) .flow(step1) .end() .build(); } @Bean public Step step1(JobRepository jobRepository, PlatformTransactionManager transactionManager, JdbcBatchItemWriter<Person> writer) { return new StepBuilder("step1", jobRepository) .<Person, Person> chunk(10, transactionManager) .reader(reader()) .processor(processor()) .writer(writer) .build(); } }
解决办法
核心原因
错误提示明确指向PEOPLE表不存在或权限不足,本质是数据库中没有提前创建people表,Spring Batch写入数据时找不到目标表。
具体步骤
添加建表脚本
在src/main/resources目录下创建schema.sql文件,写入以下建表语句:CREATE TABLE people ( id BIGINT IDENTITY PRIMARY KEY, first_name VARCHAR(255), last_name VARCHAR(255) );Spring Boot启动时会自动执行该脚本,初始化HyperSQL数据库的表结构。
确认数据库配置
检查application.properties(或application.yml)中的数据库配置是否正确,确保使用的是内存模式的HyperSQL:spring.datasource.url=jdbc:hsqldb:mem:testdb spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver spring.datasource.username=sa spring.datasource.password= spring.jpa.hibernate.ddl-auto=none其中
spring.jpa.hibernate.ddl-auto=none用于禁用Hibernate自动生成表,确保使用我们手动编写的schema.sql。验证表是否创建成功
可以通过添加日志或数据库客户端连接HyperSQL内存库查看表状态。连接信息:- URL:
jdbc:hsqldb:mem:testdb - 用户名:
sa - 密码: 空
- URL:
补充说明
官方指南默认会通过schema.sql初始化表结构,可能你在实操时遗漏了这个步骤。Spring Batch的写入操作依赖目标表已存在,必须确保表创建完成后再执行批处理任务。
内容的提问来源于stack exchange,提问作者arihant raj singh

