Spring-Batch之HelloWorld程序
Springbatch的一些基础理论知识请参考其他的博客或者相关书籍。在这里我就不阐述了,我留一下实战经验分享给大家吧,在实战的过程中可能会引用一些基础知识。
目录
实战介绍
本实战案例如下图所示:
需要插入500万条假数据到 Oralce数据库中的Student表中。
思路介绍
Spring-Batch的处理分为三个阶段,第一个阶段为reader,第二阶段为processor,第三阶段为writer,这样的写法其实规定的比较死板,而且我试验过三者缺一不可,如果我们用自定义的tasklet就体会不到架构的好处了。所以在这里我的思路如下
1. reader阶段组装假数据并生成Student对象(使用自定义方法)
2. processor自定义方法
3. writer复写JdbcBatchItemWriter将数据插入数据库中(因为有可能insert语句会因为一些情况外界自动生成的,顺便说一下里面一些方法的执行顺序)
xml核心配置如下:
<job id="insertData">
<step id="dbWirterStep">
<tasklet transaction-manager="transactionManager">
<chunk reader="restarCustomStudentItemReader"
processor="partTranslateProcessor"
writer="jdbcSetterItemWriter"
commit-interval="4000"></chunk>
</tasklet>
</step>
</job>
<!-- 插入数据库中 这里需要注意的是我将JdbcBatchItemWriter进行了复写,这样可以更加灵活的控制SQL,不用将其写死在配置文件中 -->
<bean:bean id="jdbcSetterItemWriter"
class="springbatch.MyJdbcBatchItemWriter">
<bean:property name="dataSource" ref="dataSource"/>
<!-- <bean:property name="sql" ref=" insert into Student(NAME,SEX,BIRTH,AGE,ORIGIN,REMARKS) values(?,?,?,?,?,?)"/> -->
<bean:property name="itemPreparedStatementSetter">
<bean:bean class="springbatch.DestCreditBillItemPreparedStatementSetter"/>
</bean:property>
</bean:bean>
<!-- 自定义类用于生成假数据 -->
<bean:bean id="restarCustomStudentItemReader" class="springbatch.RestartableCustomStudentItemReader">
<bean:property name="currentLocation" value="5000000"></bean:property>
</bean:bean>
<!-- 处理程序 -->
<bean:bean id="partTranslateProcessor" class="springbatch.PartTranslateItemProcessor"/>
下来是Java程序如下RestartableCustomStudentItemReader.java
package springbatch;
import java.util.Date;
import java.util.Map;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
import pojo.Student;
import utils.RandomNameUtils;
/**
* @author xiaohunding
*/
public class RestartableCustomStudentItemReader implements ItemReader<Student>{
private int currentLocation = 0;
private int count = 0;
public RestartableCustomStudentItemReader() {
super();
System.out.println("我只插入一次");
}
/**
* 制作假数据
*/
public Student read() throws Exception, UnexpectedInputException,
ParseException, NonTransientResourceException {
Student student = new Student();
if(count<currentLocation) {//当count超过5000000时停止输出
Map<String,String> map = RandomNameUtils.getAddress();//随机生成测试数据(请参考源码)
student = new Student("id",map.get("name").toString(),Short.parseShort("1"),new Date(),Short.parseShort("24"),"lala",map.get("road").toString());
count++;
if (count%1000==1) {
System.out.println("我读了:"+count+":"+student);
}
return student;
}
System.out.println("我读了:"+count+":"+student);
return null;
}
public int getCurrentLocation() {
return currentLocation;
}
public void setCurrentLocation(int currentLocation) {
this.currentLocation = currentLocation;
}
}
PartTranslateItemProcessor.java处理如下:
package springbatch;
import org.springframework.batch.item.ItemProcessor;
import pojo.Student;
/**
*
* @author xiaohunding
*/
public class PartTranslateItemProcessor implements
ItemProcessor<Student, Student> {
//做一些转化数据,处理数据和验证数据等操作
public Student process(Student bill) throws Exception {
//bill.setAddress(bill.getAddress() + "," + bill.getName());
System.out.println("进入数据转化");
return bill;
}
}
MyJdbcBatchItemWriter.java处理如下:
package springbatch;
import java.util.List;
import org.springframework.batch.item.database.JdbcBatchItemWriter;
public class MyJdbcBatchItemWriter<T> extends JdbcBatchItemWriter<T>{
private String STUDENT_SQL = "insert into Student(NAME,SEX,BIRTH,AGE,ORIGIN,REMARKS) values(?,?,?,?,?,?)";
@Override
public void setSql(String sql) {
super.setSql(sql);
}
@Override
public void afterPropertiesSet() {
setSql(STUDENT_SQL);
super.afterPropertiesSet();
}
/**
* 动态获取
*/
@Override
public void write(List<? extends T> items) throws Exception {
super.write(items);
}
}
在这里有人会问,JdbcBatchItemWriter这个类已经很强大了为什么要复写它呢,原因是SQL语句在一些时候是会跟随业务的不一样而发生变化的,所以复写它就是想让SQL语句更加的灵活起来我可以这样写(如下)
@Override
public void setSql(String sql) {
Connection conn = null;
ResultSet rs = null;
try {
conn = getDataSource().getConnection();
Statement st = conn.createStatement();
rs = st.executeQuery(sql);//执行sql语句并返回结果集
StringBuffer sb = new StringBuffer();
while (rs.next()) {
String name=rs.getString("NAME");
sb.append(name+",");
}
sb.deleteCharAt(sb.length()-1);
sql = "select "+ sb.toString()+" FROM STUDENT";
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
rs.close();
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
super.setSql(sql);
}
这样SQL语句就是从数据库中动态组装的了。以此类推,其他的方法也可以这样重写一下即可达到效果但是请初始化在afterPropertiesSet()方法里面。
@Override
public void afterPropertiesSet() throws Exception {
//从数据库中将元数据获取到并动态生成SQL语句
final Map<String, Object> map = <<...从数据库通过元数据和各种业务方法生成的SQL语句...>>
//动态生成SQL语句
super.setSql(map.get("KINGBASE_SQL").toString());
//动态绑定参数
super.setRowMapper(new RowMapper(){
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Map<String, Object> dataMap = new ListOrderedMap();
List<String> list = (List<String>)map.get("FIELDNAME");
for (String fieldName : list) {
dataMap.put(fieldName,rs.getObject(fieldName));
}
return dataMap;
}
});
super.afterPropertiesSet();
}
“<<>>”此处为伪代码
核心代码就这些了。下一篇:我们来进一步实战,从一个数据库中将数据导入到另外一个数据库中.请关注。