SpringBoot 集成SpringBatch 批处理框架
SpringBatch 核心组件简介:
1)JobRepository:用来注册Job容器,设置数据库相关属性。
2)JobLauncher:用来启动Job的接口
3)Job:我们要实际执行的任务,包含一个或多个
4)Step:即步骤,包括:ItemReader->ItemProcessor->ItemWriter
5)ItemReader:用来读取数据,做实体类与数据字段之间的映射。比如读取csv文件中的人员数据,之后对应实体AuthUser的字段做mapper
6)ItemProcessor:用来处理数据的接口,同时可以做数据校验(设置校验器,使用JSR-303(hibernate-validator)注解)
7)ItemWriter:用来输出数据的接口,设置数据库源。编写预处理SQL插入语句
使用说明:在自定义的SpringBatch配置对象中一一实列化七大组件,并且在程序入口开启@EnableBatchProcessing
package com.zzg.batch;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@EnableTransactionManagement
@MapperScan("com.zzg.batch.mapper")
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
System.out.println("============= SpringBoot gcxt-system-provider Service Start Success =============");
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
// 注意这里要指向原先用main方法执行的Application启动类
return builder.sources(Application.class);
}
}
SpringBatch 批处理流程图:
SpringBatch 实践
第一步:依赖核心jar文件:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zzg</groupId>
<artifactId>jreport</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<mybatis-spring-boot-starter.version>1.3.2</mybatis-spring-boot-starter.version>
<mysql-connector-java.version>8.0.11</mysql-connector-java.version>
<com.alibaba.druid.version>1.1.9</com.alibaba.druid.version>
<commons-lang.version>2.6</commons-lang.version>
<commons-codec.version>1.10</commons-codec.version>
<commons-lang3.version>3.8.1</commons-lang3.version>
<commons-net.version>3.6</commons-net.version>
<commons-io.version>2.6</commons-io.version>
<commons-collections.version>3.2.1</commons-collections.version>
<common-fileupload.version>1.3.1</common-fileupload.version>
<fastjson.version>1.2.48</fastjson.version>
<jasperreports.version>6.10.0</jasperreports.version>
</properties>
<dependencies>
<!-- SpringWeb模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--springboot 集成测试框架 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--lombok插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- mysql 连接 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${com.alibaba.druid.version}</version>
</dependency>
<!-- 分页控件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.6</version>
</dependency>
<!--common-lang 常用工具包 -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons-lang.version}</version>
</dependency>
<!--commons-lang3 工具包 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<!--commons-codec 加密工具包 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<!--commons-net 网络工具包 -->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>${commons-net.version}</version>
</dependency>
<!--common-io 工具包 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<!--common-collection 工具包 -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons-collections.version}</version>
</dependency>
<!--common-fileupload 工具包 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${common-fileupload.version}</version>
</dependency>
<!-- Swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- 集成springboot batch 批处理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
</dependencies>
</project>
第二步:application.properties 配置文件
# 指定服务端口
server.port=7090
# 指定服务 名称
# server.context-path=/jreport
#mybatis xml 文件配置
mybatis.mapper-locations=classpath*:mapper/batch/*Mapper.xml
mybatis.type-aliases-package=com.zzg.batch.domain
# MyBatis mysql8 配置
spring.datasource.url=jdbc:mysql://192.168.1.**:3306/boot-security?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true&allowMultiQueries=true
spring.datasource.username=**
spring.datasource.password=**
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# Druid 配置
# 初始化时建立物理连接的个数
spring.datasource.druid.initial-size=5
# 最大连接池数量
spring.datasource.druid.max-active=30
# 最小连接池数量
spring.datasource.druid.min-idle=5
# 获取连接时最大等待时间,单位毫秒
spring.datasource.druid.max-wait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.druid.time-between-eviction-runs-millis=60000
# 连接保持空闲而不被驱逐的最小时间
spring.datasource.druid.min-evictable-idle-time-millis=300000
# 用来检测连接是否有效的sql,要求是一个查询语句
spring.datasource.druid.validation-query=SELECT 1 FROM DUAL
# 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
spring.datasource.druid.test-while-idle=true
# 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
spring.datasource.druid.test-on-borrow=false
# 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
spring.datasource.druid.test-on-return=false
# 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
spring.datasource.druid.pool-prepared-statements=true
# 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。
spring.datasource.druid.max-pool-prepared-statement-per-connection-size=50
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计
#spring.datasource.druid.filters=stat,wall
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
# 合并多个DruidDataSource的监控数据
spring.datasource.druid.use-global-data-source-stat=true
# 配置sql 注入方式
spring.datasource.druid.filters=stat
# 日志文件配置
logging.config=classpath:logback.xml
# springboot 批处理配置
spring.batch.job.enabled=false
spring.batch.initialize-schema=always
第三步:配置类
package com.zzg.batch.config;
import javax.sql.DataSource;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider;
import org.springframework.batch.item.database.JdbcBatchItemWriter;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.PlatformTransactionManager;
import com.zzg.batch.domain.AuthUser;
import com.zzg.batch.processor.CVSItemProcessor;
import com.zzg.batch.processor.listener.CSVJobListener;
import com.zzg.batch.processor.validator.BeanValidator;
/**
* springboot batch 配置类
* @author zzg
*
*/
@Configuration
@EnableBatchProcessing // 开启批处理的支持
public class BatchConfig {
@Autowired
private DataSource dataSource;
@Autowired
private PlatformTransactionManager transactionManager;
/**
* ItemReader定义:读取文件数据+entirty映射
* @return
*/
@Bean
public ItemReader<AuthUser> reader(){
// 使用FlatFileItemReader去读cvs文件,一行即一条数据
FlatFileItemReader<AuthUser> reader = new FlatFileItemReader<>();
// 设置文件处在路径
reader.setResource(new ClassPathResource("user.csv"));
// entity与csv数据做映射
reader.setLineMapper(new DefaultLineMapper<AuthUser>() {
{
setLineTokenizer(new DelimitedLineTokenizer() {
{
setNames(new String[]{"id", "password"});
}
});
setFieldSetMapper(new BeanWrapperFieldSetMapper<AuthUser>() {
{
setTargetType(AuthUser.class);
}
});
}
});
return reader;
}
/**
* 注册ItemProcessor: 处理数据
* @return
*/
@Bean
public ItemProcessor<AuthUser, AuthUser> processor(){
CVSItemProcessor cvsItemProcessor = new CVSItemProcessor();
cvsItemProcessor.setValidator(new BeanValidator<AuthUser>());
return cvsItemProcessor;
}
/**
* ItemWriter定义:指定datasource,设置批量插入sql语句,写入数据库
* @param dataSource
* @return
*/
@Bean
public ItemWriter<AuthUser> writer(){
// 使用jdbcBcatchItemWrite写数据到数据库中
JdbcBatchItemWriter<AuthUser> writer = new JdbcBatchItemWriter<>();
// 设置有参数的sql语句
writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<AuthUser>());
String sql = "insert into auth_user (id, password)" + " values(:id, :password)";
writer.setSql(sql);
writer.setDataSource(dataSource);
return writer;
}
/**
* JobRepository定义:设置数据库,注册Job容器
* @param dataSource
* @param transactionManager
* @return
* @throws Exception
*/
@Bean
public JobRepository cvsJobRepository() throws Exception{
JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
jobRepositoryFactoryBean.setDatabaseType("mysql");
jobRepositoryFactoryBean.setTransactionManager(transactionManager);
jobRepositoryFactoryBean.setDataSource(dataSource);
return jobRepositoryFactoryBean.getObject();
}
/**
* jobLauncher定义:
* @param dataSource
* @param transactionManager
* @return
* @throws Exception
*/
@Bean
public SimpleJobLauncher csvJobLauncher() throws Exception{
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
// 设置jobRepository
jobLauncher.setJobRepository(cvsJobRepository());
return jobLauncher;
}
/**
* 定义job
* @param jobs
* @param step
* @return
*/
@Bean
public Job importJob(JobBuilderFactory jobs, Step step){
return jobs.get("importCsvJob")
.incrementer(new RunIdIncrementer())
.flow(step)
.end()
.listener(csvJobListener())
.build();
}
/**
* 注册job监听器
* @return
*/
@Bean
public CSVJobListener csvJobListener(){
return new CSVJobListener();
}
/**
* step定义:步骤包括ItemReader->ItemProcessor->ItemWriter 即读取数据->处理校验数据->写入数据
* @param stepBuilderFactory
* @param reader
* @param writer
* @param processor
* @return
*/
@Bean
public Step step(StepBuilderFactory stepBuilderFactory, ItemReader<AuthUser> reader,
ItemWriter<AuthUser> writer, ItemProcessor<AuthUser, AuthUser> processor){
return stepBuilderFactory
.get("step")
.<AuthUser, AuthUser>chunk(65000) // Chunk的机制(即每次读取一条数据,再处理一条数据,累积到一定数量后再一次性交给writer进行写入操作)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
}
package com.zzg.batch.config;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
/**
* druid 监控配置
* @author zzg
*
*/
@Configuration
public class DruidConfig {
@Bean
public ServletRegistrationBean druidServletRegistrationBean() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
servletRegistrationBean.setServlet(new StatViewServlet());
servletRegistrationBean.addUrlMappings("/druid/*");
servletRegistrationBean.addInitParameter("allow", "");
servletRegistrationBean.addInitParameter("deny", "");
servletRegistrationBean.addInitParameter("loginUsername", "admin");
servletRegistrationBean.addInitParameter("loginPassword", "admin");
return servletRegistrationBean;
}
/**
* 注册DruidFilter拦截
*
* @return
*/
@Bean
public FilterRegistrationBean duridFilterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
Map<String, String> initParams = new HashMap<String, String>();
//设置忽略请求
initParams.put("exclusions", "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*");
filterRegistrationBean.setInitParameters(initParams);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
package com.zzg.batch.config;
import java.util.Properties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.github.pagehelper.PageHelper;
/**
* mybatis 配置对象
* @author zzg
*
*/
@Configuration
public class MyBatisConfig {
/**
* 分页对象实列化
* @return
*/
@Bean
public PageHelper pageHelper() {
PageHelper pageHelper = new PageHelper();
Properties p = new Properties();
p.setProperty("offsetAsPageNum", "true");
p.setProperty("rowBoundsWithCount", "true");
p.setProperty("reasonable", "true");
p.setProperty("dialect", "mysql");
pageHelper.setProperties(p);
return pageHelper;
}
}
package com.zzg.batch.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.swagger.annotations.ApiOperation;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket buildDocket() {
ParameterBuilder tokenPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<Parameter>();
tokenPar.name("X-CSRF-TOKEN").description("令牌").modelRef(new ModelRef("string")).parameterType("header")
.required(false).build();
pars.add(tokenPar.build());
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)).paths(PathSelectors.any())
.build().globalOperationParameters(pars).apiInfo(buildApiInf());
}
private ApiInfo buildApiInf() {
return new ApiInfoBuilder().title("****").termsOfServiceUrl("http://www.baidu.cn/")
.description("API接口")
.contact(new Contact("baidu", "http://www.baidu.cn/", "zhouzhiwengang@163.com"))
.version("2.0").build();
}
}
业务逻辑层代码:model、service、mapper层的相关代码:
package com.zzg.batch.domain;
import java.util.Date;
public class AuthUser {
private Integer id;
private String password;
private Date lastLogin;
private String isSuperuser;
private String username;
private String firstName;
private String lastName;
private String email;
private String isStaff;
private String isActive;
private Date dateJoined;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password == null ? null : password.trim();
}
public Date getLastLogin() {
return lastLogin;
}
public void setLastLogin(Date lastLogin) {
this.lastLogin = lastLogin;
}
public String getIsSuperuser() {
return isSuperuser;
}
public void setIsSuperuser(String isSuperuser) {
this.isSuperuser = isSuperuser;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username == null ? null : username.trim();
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName == null ? null : firstName.trim();
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName == null ? null : lastName.trim();
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email == null ? null : email.trim();
}
public String getIsStaff() {
return isStaff;
}
public void setIsStaff(String isStaff) {
this.isStaff = isStaff;
}
public String getIsActive() {
return isActive;
}
public void setIsActive(String isActive) {
this.isActive = isActive;
}
public Date getDateJoined() {
return dateJoined;
}
public void setDateJoined(Date dateJoined) {
this.dateJoined = dateJoined;
}
}
package com.zzg.batch.mapper;
import java.util.List;
import java.util.Map;
import com.zzg.batch.domain.AuthUser;
public interface AuthUserMapper {
int deleteByPrimaryKey(Integer id);
int insert(AuthUser record);
int insertSelective(AuthUser record);
AuthUser selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(AuthUser record);
int updateByPrimaryKey(AuthUser record);
// 方法梳理
List<AuthUser> selectAll(Map<String, Object> paramter);
}
package com.zzg.batch.service;
import java.util.List;
import java.util.Map;
import com.zzg.batch.domain.AuthUser;
import com.zzg.jreport.common.BaseService;
import com.zzg.jreport.common.page.PageData;
import com.zzg.jreport.common.page.PageParam;
public interface AuthUserService extends BaseService<AuthUser> {
// 方法梳理
List<AuthUser> selectAll(Map<String, Object> paramter);
PageData<AuthUser> selectAllPage(Map<String,Object> parame, PageParam rb);
// 事务测试
void transaction();
}
package com.zzg.batch.service.impl;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.zzg.batch.domain.AuthUser;
import com.zzg.batch.mapper.AuthUserMapper;
import com.zzg.batch.service.AuthUserService;
import com.zzg.jreport.common.convert.SimpleTypeConverterUtil;
import com.zzg.jreport.common.page.PageData;
import com.zzg.jreport.common.page.PageParam;
@Service
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout=36000,rollbackFor=Exception.class)
public class AuthUserServiceImpl implements AuthUserService {
@Autowired
private AuthUserMapper mapper;
@Override
public Long insert(AuthUser entity) {
// TODO Auto-generated method stub
Integer num = mapper.insertSelective(entity);
return Long.valueOf(num);
}
@Override
public void updateByPrimaryKeySelective(AuthUser entity) {
// TODO Auto-generated method stub
mapper.updateByPrimaryKeySelective(entity);
}
@Override
public AuthUser selectByPrimaryKey(String sid) {
// TODO Auto-generated method stub
return mapper.selectByPrimaryKey(SimpleTypeConverterUtil.convertIfNecessary(sid, int.class));
}
@Override
public void deleteByPrimaryKey(String sid) {
// TODO Auto-generated method stub
mapper.deleteByPrimaryKey(SimpleTypeConverterUtil.convertIfNecessary(sid, int.class));
}
@Override
public List<AuthUser> selectAll(Map<String, Object> paramter) {
// TODO Auto-generated method stub
return mapper.selectAll(paramter);
}
@Override
public PageData<AuthUser> selectAllPage(Map<String, Object> parame, PageParam rb) {
// TODO Auto-generated method stub
PageData<AuthUser> pageData = new PageData<AuthUser>();
PageHelper.startPage(rb.getPageNo(), rb.getLimit());
List<AuthUser> rs = mapper.selectAll(parame);
PageInfo<AuthUser> pageInfo = new PageInfo<AuthUser>(rs);
pageData.setData(pageInfo.getList());
pageData.setPageNum(pageInfo.getPageNum());
pageData.setPageSize(pageInfo.getPageSize());
pageData.setTotalCount(pageInfo.getTotal());
return pageData;
}
@Override
public void transaction() {
// TODO Auto-generated method stub
AuthUser admin = new AuthUser();
admin.setId(1);
admin.setUsername("admin");
mapper.updateByPrimaryKeySelective(admin);
int a = 1 / 0;
AuthUser superMan = new AuthUser();
superMan.setId(2);
superMan.setUsername("superMan");
mapper.updateByPrimaryKeySelective(superMan);
}
}
package com.zzg.batch.controller;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSONObject;
import com.zzg.batch.domain.AuthUser;
import com.zzg.batch.service.AuthUserService;
import com.zzg.jreport.common.controller.AbstractController;
import com.zzg.jreport.common.page.PageData;
import com.zzg.jreport.common.page.PageParam;
import com.zzg.jreport.response.JreportResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@Controller
@RequestMapping("/api/auth/user")
@Api(value = "用户Controlle", tags = "用户操作服务")
public class AuthUserController extends AbstractController {
@Autowired
private AuthUserService service;
@ApiOperation(httpMethod = "POST", value = "用户对象保存")
@RequestMapping(value = "/insert", method = { RequestMethod.POST }, produces = "application/json;charset=UTF-8")
@ResponseBody
public JreportResponse insert(
@RequestBody @ApiParam(name = "用户对象", value = "json格式对象", required = true) AuthUser entity) {
Long engSid = service.insert(entity);
return JreportResponse.ok(engSid);
}
@ApiOperation(httpMethod = "POST", value = "用户对象更新")
@RequestMapping(value = "/update", method = { RequestMethod.POST }, produces = "application/json;charset=UTF-8")
@ResponseBody
public JreportResponse update(
@RequestBody @ApiParam(name = "用户对象", value = "json格式对象", required = true) AuthUser entity) {
service.updateByPrimaryKeySelective(entity);
return JreportResponse.ok();
}
@RequestMapping(value="/find", method={RequestMethod.POST}, produces = "application/json;charset=UTF-8")
@ResponseBody
@ApiOperation(httpMethod = "POST", value = "用户查询")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "主键", required = false, dataType = "Integer", paramType = "query"),
@ApiImplicitParam(name = "username", value = "用户名称", required = false, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "email", value = "用户邮箱", required = false, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "isStaff", value = "是否在职:1 在职、2:离职", required = false, dataType = "Integer", paramType = "query"),
@ApiImplicitParam(name = "isActive", value = "激活状态:1 已激活、2:未激活", required = false, dataType = "Integer", paramType = "query")
})
public JreportResponse find(@RequestBody @ApiParam(name = "用户对象", value = "json格式对象", required = true) JSONObject entity) {
Map<String, Object> param = JSONObject.toJavaObject(entity, Map.class);
List<AuthUser> list = service.selectAll(param);
return JreportResponse.ok(list);
}
@RequestMapping(value="/findByPage", method={RequestMethod.POST}, produces = "application/json;charset=UTF-8")
@ResponseBody
@ApiOperation(httpMethod = "POST", value = "用户分页查询")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "主键", required = false, dataType = "Integer", paramType = "query"),
@ApiImplicitParam(name = "username", value = "用户名称", required = false, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "email", value = "用户邮箱", required = false, dataType = "String", paramType = "query"),
@ApiImplicitParam(name = "isStaff", value = "是否在职:1 在职、2:离职", required = false, dataType = "Integer", paramType = "query"),
@ApiImplicitParam(name = "isActive", value = "激活状态:1 已激活、2:未激活", required = false, dataType = "Integer", paramType = "query")
})
public JreportResponse findByPage(@RequestBody @ApiParam(name = "用户对象", value = "json格式对象", required = true) JSONObject entity) {
Map<String, Object> param = JSONObject.toJavaObject(entity, Map.class);
PageParam rb = super.initPageBounds(param);
PageData<AuthUser> pageList = service.selectAllPage(param, rb);
return JreportResponse.ok(pageList);
}
}
package com.zzg.batch.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.zzg.batch.processor.listener.CSVJobListener;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@Controller
@RequestMapping("/api/batch")
@Api(value = "批处理Controlle", tags = "批处理操作服务")
public class BatchController {
// 日志服务处理
private Logger logger = LoggerFactory.getLogger(BatchController.class);
@Autowired
private SimpleJobLauncher launcher;
@Autowired
private Job job;
@ApiOperation(httpMethod = "POST", value = "用户对象保存")
@RequestMapping(value = "/init", method = { RequestMethod.POST }, produces = "application/json;charset=UTF-8")
@ResponseBody
public void insert() {
// 后置参数:使用JobParameters中绑定参数
try{
JobParameters jobParameters = new JobParametersBuilder().addLong("time", System.currentTimeMillis())
.toJobParameters();
launcher.run(job, jobParameters);
} catch(Exception e){
e.printStackTrace();
logger.error(e.getMessage());
}
}
}
package com.zzg.batch.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.zzg.batch.service.AuthUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@Controller
@RequestMapping("/api/trans")
@Api(value = "事务处理Controlle", tags = "事务处理操作服务")
public class TransactionController {
@Autowired
private AuthUserService service;
@ApiOperation(httpMethod = "POST", value = "事务功能测试")
@RequestMapping(value = "/init", method = { RequestMethod.POST }, produces = "application/json;charset=UTF-8")
@ResponseBody
public void init() {
service.transaction();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zzg.batch.mapper.AuthUserMapper">
<resultMap id="BaseResultMap" type="com.zzg.batch.domain.AuthUser">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="password" jdbcType="VARCHAR" property="password" />
<result column="last_login" jdbcType="TIMESTAMP" property="lastLogin" />
<result column="is_superuser" jdbcType="VARCHAR" property="isSuperuser" />
<result column="username" jdbcType="VARCHAR" property="username" />
<result column="first_name" jdbcType="VARCHAR" property="firstName" />
<result column="last_name" jdbcType="VARCHAR" property="lastName" />
<result column="email" jdbcType="VARCHAR" property="email" />
<result column="is_staff" jdbcType="VARCHAR" property="isStaff" />
<result column="is_active" jdbcType="VARCHAR" property="isActive" />
<result column="date_joined" jdbcType="TIMESTAMP" property="dateJoined" />
</resultMap>
<sql id="Base_Column_List">
id, password, last_login, is_superuser, username,
first_name, last_name,
email, is_staff,
is_active, date_joined
</sql>
<sql id="condition">
<if test="id != null">
and id = #{id}
</if>
<if test="username != null and username != ''">
and username = #{username}
</if>
<if test="email != null and email !=''">
and email = #{email}
</if>
<if test="isStaff != null">
and is_staff = #{isStaff}
</if>
<if test="isActive != null">
and is_active = #{isActive}
</if>
</sql>
<!-- 方法梳理 -->
<select id="selectAll" parameterType="java.util.Map" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from auth_user
where 1 = 1
<include refid="condition"></include>
</select>
<select id="selectByPrimaryKey" parameterType="java.lang.Integer"
resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from auth_user
where id = #{id,jdbcType=INTEGER}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from
auth_user
where id = #{id,jdbcType=INTEGER}
</delete>
<insert id="insert" parameterType="com.zzg.batch.domain.AuthUser">
insert into auth_user (id,
password, last_login,
is_superuser, username, first_name,
last_name,
email, is_staff,
is_active, date_joined)
values (#{id,jdbcType=INTEGER},
#{password,jdbcType=VARCHAR},
#{lastLogin,jdbcType=TIMESTAMP},
#{isSuperuser,jdbcType=VARCHAR}, #{username,jdbcType=VARCHAR},
#{firstName,jdbcType=VARCHAR},
#{lastName,jdbcType=VARCHAR},
#{email,jdbcType=VARCHAR},
#{isStaff,jdbcType=VARCHAR},
#{isActive,jdbcType=VARCHAR}, #{dateJoined,jdbcType=TIMESTAMP})
</insert>
<insert id="insertSelective" parameterType="com.zzg.batch.domain.AuthUser">
insert into auth_user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="password != null">
password,
</if>
<if test="lastLogin != null">
last_login,
</if>
<if test="isSuperuser != null">
is_superuser,
</if>
<if test="username != null">
username,
</if>
<if test="firstName != null">
first_name,
</if>
<if test="lastName != null">
last_name,
</if>
<if test="email != null">
email,
</if>
<if test="isStaff != null">
is_staff,
</if>
<if test="isActive != null">
is_active,
</if>
<if test="dateJoined != null">
date_joined,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=INTEGER},
</if>
<if test="password != null">
#{password,jdbcType=VARCHAR},
</if>
<if test="lastLogin != null">
#{lastLogin,jdbcType=TIMESTAMP},
</if>
<if test="isSuperuser != null">
#{isSuperuser,jdbcType=VARCHAR},
</if>
<if test="username != null">
#{username,jdbcType=VARCHAR},
</if>
<if test="firstName != null">
#{firstName,jdbcType=VARCHAR},
</if>
<if test="lastName != null">
#{lastName,jdbcType=VARCHAR},
</if>
<if test="email != null">
#{email,jdbcType=VARCHAR},
</if>
<if test="isStaff != null">
#{isStaff,jdbcType=VARCHAR},
</if>
<if test="isActive != null">
#{isActive,jdbcType=VARCHAR},
</if>
<if test="dateJoined != null">
#{dateJoined,jdbcType=TIMESTAMP},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.zzg.batch.domain.AuthUser">
update auth_user
<set>
<if test="password != null">
password = #{password,jdbcType=VARCHAR},
</if>
<if test="lastLogin != null">
last_login = #{lastLogin,jdbcType=TIMESTAMP},
</if>
<if test="isSuperuser != null">
is_superuser = #{isSuperuser,jdbcType=VARCHAR},
</if>
<if test="username != null">
username = #{username,jdbcType=VARCHAR},
</if>
<if test="firstName != null">
first_name = #{firstName,jdbcType=VARCHAR},
</if>
<if test="lastName != null">
last_name = #{lastName,jdbcType=VARCHAR},
</if>
<if test="email != null">
email = #{email,jdbcType=VARCHAR},
</if>
<if test="isStaff != null">
is_staff = #{isStaff,jdbcType=VARCHAR},
</if>
<if test="isActive != null">
is_active = #{isActive,jdbcType=VARCHAR},
</if>
<if test="dateJoined != null">
date_joined = #{dateJoined,jdbcType=TIMESTAMP},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
<update id="updateByPrimaryKey" parameterType="com.zzg.batch.domain.AuthUser">
update auth_user
set password = #{password,jdbcType=VARCHAR},
last_login =
#{lastLogin,jdbcType=TIMESTAMP},
is_superuser =
#{isSuperuser,jdbcType=VARCHAR},
username = #{username,jdbcType=VARCHAR},
first_name = #{firstName,jdbcType=VARCHAR},
last_name =
#{lastName,jdbcType=VARCHAR},
email = #{email,jdbcType=VARCHAR},
is_staff = #{isStaff,jdbcType=VARCHAR},
is_active =
#{isActive,jdbcType=VARCHAR},
date_joined =
#{dateJoined,jdbcType=TIMESTAMP}
where id = #{id,jdbcType=INTEGER}
</update>
</mapper>
SpringBatch批处理涉及:批处理类、批处理监听器、批处理校验器
package com.zzg.batch.processor;
import org.springframework.batch.item.validator.ValidatingItemProcessor;
import org.springframework.batch.item.validator.ValidationException;
import com.zzg.batch.domain.AuthUser;
public class CVSItemProcessor extends ValidatingItemProcessor<AuthUser> {
// 局部常量
// public static final String YES = "1";
// public static final String NO = "2";
@Override
public AuthUser process(AuthUser item) throws ValidationException {
// TODO Auto-generated method stub
super.process(item);
// if("是".equalsIgnoreCase(item.getIsSuperuser())){
// item.setIsSuperuser(YES);
// } else {
// item.setIsSuperuser(NO);
// }
//
// if("是".equalsIgnoreCase(item.getIsActive())){
// item.setIsActive(YES);
// } else {
// item.setIsActive(NO);
// }
//
// if("是".equalsIgnoreCase(item.getIsStaff())){
// item.setIsStaff(YES);
// } else {
// item.setIsStaff(NO);
// }
return item;
}
}
package com.zzg.batch.processor.listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
public class CSVJobListener implements JobExecutionListener {
private Logger logger = LoggerFactory.getLogger(CSVJobListener.class);
private long startTime;
private long endTime;
@Override
public void afterJob(JobExecution arg0) {
// TODO Auto-generated method stub
endTime = System.currentTimeMillis();
logger.info("job process end...");
logger.info("spend time: " + (endTime - startTime) + "ms");
}
@Override
public void beforeJob(JobExecution arg0) {
// TODO Auto-generated method stub
startTime = System.currentTimeMillis();
logger.info("job process start...");
}
}
package com.zzg.batch.processor.validator;
import org.springframework.batch.item.validator.ValidationException;
import org.springframework.batch.item.validator.Validator;
import org.springframework.beans.factory.InitializingBean;
public class BeanValidator<T> implements Validator<T>, InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
}
@Override
public void validate(T value) throws ValidationException {
}
}
项目整体结构截图:
补全日志配置文件、数据库脚本和测试数据:
<?xml version="1.0" encoding="UTF-8"?>
<!-- 从高到地低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL -->
<!-- 日志输出规则 根据当前ROOT 级别,日志输出时,级别高于root默认的级别时 会输出 -->
<!-- 以下 每个配置的 filter 是过滤掉输出文件里面,会出现高级别文件,依然出现低级别的日志信息,通过filter 过滤只记录本级别的日志-->
<!-- 属性描述 scan:性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,
默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 定义日志文件 输入位置 -->
<property name="log_dir" value="/logs/system-provider" />
<!-- 日志最大的历史 30天 -->
<property name="maxHistory" value="30"/>
<!-- ConsoleAppender 控制台输出日志 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- 对日志进行格式化 -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n</pattern>
</encoder>
</appender>
<!-- ERROR级别日志 -->
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 RollingFileAppender-->
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤器,只记录WARN级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 最常用的滚动策略,它根据时间来制定滚动策略.既负责滚动也负责出发滚动 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志输出位置 可相对、和绝对路径 -->
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/neo4j-error-log.log</fileNamePattern>
<!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件假设设置每个月滚动,且<maxHistory>是6,
则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除-->
<maxHistory>${maxHistory}</maxHistory>
</rollingPolicy>
<!-- 按照固定窗口模式生成日志文件,当文件大于20MB时,生成新的日志文件。窗口大小是1到3,当保存了3个归档文件后,将覆盖最早的日志。
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy> -->
<!-- 查看当前活动文件的大小,如果超过指定大小会告知RollingFileAppender 触发当前活动文件滚动
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy> -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<!-- WARN级别日志 appender -->
<appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤器,只记录WARN级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/neo4j-warn-log.log
</fileNamePattern>
<maxHistory>${maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<!-- INFO级别日志 appender -->
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤器,只记录INFO级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/neo4j-info-log.log
</fileNamePattern>
<maxHistory>${maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<!-- DEBUG级别日志 appender -->
<appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/neo4j-debug-log.log
</fileNamePattern>
<maxHistory>${maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<logger name="java.sql.PreparedStatement" value="DEBUG" />
<logger name="java.sql.Connection" value="DEBUG" />
<logger name="java.sql.Statement" value="DEBUG" />
<logger name="com.ibatis" value="DEBUG" />
<logger name="com.ibatis.common.jdbc.SimpleDataSource" value="DEBUG" />
<logger name="com.ibatis.common.jdbc.ScriptRunner" level="DEBUG"/>
<logger name="com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate" value="DEBUG" />
<logger name="org.springframework.web" level="DEBUG"/>
<logger name="com.zzg.jreport" level="DEBUG"/>
<!-- root级别 DEBUG -->
<root level="ERROR">
<!-- 控制台输出 -->
<appender-ref ref="STDOUT" />
<!-- 文件输出 -->
<appender-ref ref="ERROR" />
<appender-ref ref="INFO" />
<appender-ref ref="WARN" />
<appender-ref ref="DEBUG" />
</root>
</configuration>
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。