完整代码示例:对比两种查询方式
// Employee.java 实体类(包含命名查询)
@Entity
@NamedQuery(name = "Employee.findAllNamedQuery", query = "SELECT e FROM Employee e ORDER BY e.id") // 定义命名查询
public class Employee {@Idprivate Long id;private String name;private Double salary;// getters/setters
}// JobConfig.java 配置类
@Configuration
@EnableBatchProcessing
public class JobConfig {@Autowiredprivate JobBuilderFactory jobBuilderFactory;@Autowiredprivate StepBuilderFactory stepBuilderFactory;@Autowiredprivate EntityManagerFactory entityManagerFactory;@Beanpublic Job employeeJob() {return jobBuilderFactory.get("employeeJob").start(employeeStep1()) // 使用命名查询的步骤.next(employeeStep2()) // 使用原生查询的步骤.build();}// 使用JpaNamedQueryProvider的步骤@Beanpublic Step employeeStep1(JpaPagingItemReader<Employee> namedQueryReader) {return stepBuilderFactory.get("step1").<Employee, Employee>chunk(10).reader(namedQueryReader).writer(items -> items.forEach(System.out::println)).build();}// 使用JpaNativeQueryProvider的步骤@Beanpublic Step employeeStep2(JpaPagingItemReader<Employee> nativeQueryReader) {return stepBuilderFactory.get("step2").<Employee, Employee>chunk(10).reader(nativeQueryReader).writer(items -> items.forEach(System.out::println)).build();}// 命名查询配置@Beanpublic JpaPagingItemReader<Employee> namedQueryReader() {return new JpaPagingItemReaderBuilder<Employee>().name("namedQueryReader").entityManagerFactory(entityManagerFactory).pageSize(50).queryProvider(new JpaNamedQueryProvider("Employee.findAllNamedQuery")).build();}// 原生查询配置@Beanpublic JpaPagingItemReader<Employee> nativeQueryReader() {return new JpaPagingItemReaderBuilder<Employee>().name("nativeQueryReader").entityManagerFactory(entityManagerFactory).pageSize(50).queryProvider(new JpaNativeQueryProvider<>("SELECT e.id, e.name, e.salary FROM EMPLOYEE e ORDER BY e.id", // 原生SQLEmployee.class, // 映射实体new String[]{"id", "name", "salary"} // 列名到属性的映射)).build();}
}// SpringBatchApplication.java 启动类
@SpringBootApplication
@EnableBatchProcessing
public class SpringBatchApplication {public static void main(String[] args) {SpringApplication.run(SpringBatchApplication.class, args);}
}
关键代码说明:
-
JpaNamedQueryProvider
- 通过实体类上的
@NamedQuery
定义查询名称和JPQL语句 - 配置时直接传入命名查询名称
- 优势:符合JPA规范,编译时检查查询语法
- 通过实体类上的
-
JpaNativeQueryProvider
- 处理原生SQL查询,需手动指定:
- SQL语句(注意表名与数据库实际表名一致)
- 目标实体类型
- 列名与实体属性的映射关系
- 适用场景:需要复杂SQL优化或使用数据库特定语法
- 处理原生SQL查询,需手动指定:
-
JpaPagingItemReader
- 核心分页读取器,自动处理分页逻辑:
- 使用
entityManagerFactory
连接数据库 - 通过
pageSize
控制每批数据量 - 支持事务管理(由Spring Batch自动处理)
- 使用
- 核心分页读取器,自动处理分页逻辑:
-
JpaPagingItemReaderBuilder
-
通过链式调用简化配置:
new JpaPagingItemReaderBuilder<>().name("readerName") .entityManagerFactory(...) .pageSize(50) .queryProvider(...) .build();
-
必须配置:
entityManagerFactory
、queryProvider
、pageSize
-
功能对比表格
类名称 | 功能描述 | 使用场景 | 关键配置参数 | 注意事项 |
---|---|---|---|---|
JpaNamedQueryProvider | 基于实体类的命名查询管理 | 需要复用JPQL查询 | 查询名称(String) | 需与实体类的@NamedQuery一致 |
JpaNativeQueryProvider | 管理原生SQL查询,支持列名到实体属性的映射 | 需要执行原生SQL查询 | SQL语句、实体类型、属性列名映射数组 | 需确保列名与实体属性严格对应 |
JpaPagingItemReader | 分页读取数据的核心组件,支持数据库连接和分页逻辑 | 所有需要分页读取数据的场景 | entityManagerFactory、pageSize、查询提供者 | 需配合Spring Batch事务管理 |
JpaPagingItemReaderBuilder | 构建JpaPagingItemReader的辅助类,提供链式配置方式 | 需要简化配置流程 | name、entityManagerFactory、pageSize、查询提供者 | 所有参数必须显式配置 |
使用要点总结:
- 命名查询:需在实体类上通过
@NamedQuery
定义,保证查询名称一致性 - 原生查询:
- 表名需与数据库实际表名一致(区分大小写)
- 列名必须与实体属性严格对应(如:
id
对应Employee.id
)
- 分页性能:
pageSize
需根据数据库性能调整,建议50-1000之间- 分页使用
OFFSET
时需注意数据库性能(如MySQL InnoDB的优化)
- 事务管理:Spring Batch会自动管理事务,但需确保
EntityManagerFactory
配置正确 - 数据映射:原生查询需手动指定列名映射,命名查询自动通过实体属性映射