在spring batch中如何使用rowmapper
概述
在spring batch框架中提供了三个核心的概念,分别是reader和processor和writer,分别用于读取,处理和写数据。关于这部分更详细的内容可以参考博客:批处理框架spring batch介绍及使用。这里不再展开。本篇文章主要想记录的问题是使用spring batch时,对于读取到的数据我们应该怎样去处理的问题。
怎样去处理读取到的数据,这个问题本质上是由需求决定的。如果读取到的数据不需要做任何处理,只需要插入到目标数据库即可的话,这意味着procesor这个模块不需要任何逻辑。而如果我们读取到的数据是需要经过处理才能写入的,那这必然导致procesor需要写逻辑代码,且我们必须将读到的数据序列化成一个能操作的对象,当然这也是根据需求决定的。
什么是rowmapper
在spring batch的reader读取到数据之后,也都是将数据放到ResultSet对象里面的,这与直接使用JdbcTemplate无异。rowmapper是spring jdbc里面定义的接口,其左右就是用于将从数据库读到的一行记录,映射到另一个对象里面去。该接口定义如下:
@FunctionalInterface
public interface RowMapper<T> {
/**
* Implementations must implement this method to map each row of data
* in the ResultSet. This method should not call {@code next()} on
* the ResultSet; it is only supposed to map values of the current row.
* @param rs the ResultSet to map (pre-initialized for the current row)
* @param rowNum the number of the current row
* @return the result object for the current row (may be {@code null})
* @throws SQLException if a SQLException is encountered getting
* column values (that is, there's no need to catch SQLException)
*/
@Nullable
T mapRow(ResultSet rs, int rowNum) throws SQLException;
}
要使用rowmapper将数据映射为一个对象,只需要实现该mapRow方法即可。
实现Rowmapper
我们可以实现Rowmapper接口,例如下面的这个例子:
public class BPRowMapper implements RowMapper<BP> {
@Nullable
@Override
public Student mapRow(ResultSet resultSet, int rowNum) throws SQLException {
Student student = new Student();
student.setPkId(resultSet.getString("pkId"));
student.setChangedAt(resultSet.getDate("changedAt"));
student.setCreatedAt(resultSet.getDate("createdAt"));
student.setChangedBy(resultSet.getString("changedBy"));
student.setCreatedBy(resultSet.getString("createdBy"));
student.setCompany(resultSet.getString("company"));
student.setMax_deletion_date(resultSet.getDate("max_deletion_date"));
student.setFirstName(resultSet.getString("firstName"));
student.setLastName(resultSet.getString("lastName"));
student.setName(resultSet.getString("name"));
student.setVersion(resultSet.getInt("version"));
student.setIs_blocked(resultSet.getBoolean("is_blocked"));
return student;
}
}
上述代码就是将读到的数据映射到一个student的对象当中,后续操作就基于Student对象即可,这样的好处是类型安全,同时操作方便,但是类型安全带来的代价是失去了灵活性
使用ColumnMapRowMapper
使用ColumnMapRowMapper的作用就是将读回来的数据存在一个map对象里面,map的key就是字段的名字,map的value就是字段的值,这样的坏处是失去了一定的类型安全,同时操作数据比较不方便,但是它的核心好处在于灵活性。
在上述例子当中我们定义了一个Student对象用于持久化,当读回来的数据有多种类型时,那么就需要定义相应的对象才可以。需要处理的数据越多时,则需要定义的对象越多,需要操作的对象也就越多。
这个时候map的优势就体现出来了,map是一个灵活的结构,并不需要指定字段名字。它的使用方法非常简单:
private JdbcCursorItemReader generateJdbcCursorItemReader() {
JdbcCursorItemReader<Map<String, Object>> itemReader = new JdbcCursorItemReader<>();
itemReader.setDataSource(dataSource);
itemReader.setSql("sql");
itemReader.setRowMapper(new ColumnMapRowMapper());
return itemReader;
}
ColumnMapRowMapper是spring实现的一个类,它已经自己实现了mapRow方法,我们只需直接使用即可。它的maprow方法实现如下:
@Override
public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
Map<String, Object> mapOfColValues = createColumnMap(columnCount);
for (int i = 1; i <= columnCount; i++) {
String key = getColumnKey(JdbcUtils.lookupColumnName(rsmd, i));
Object obj = getColumnValue(rs, i);
mapOfColValues.put(key, obj);
}
return mapOfColValues;
}
其逻辑和我们描述的一样,就是根据读到的放在ResultSet里面的数据,然后将其按照key-value的方式放在map里。map的key就是column的名字,由方法getColumnKey获得,map的value就是column的值,由方法getColumnValue获得。
但是值得注意的是这两种方式获取到的数据在写数据时的逻辑也是有所区别的,需要做相应调整。
因此,在spring batch迁移数据时,我们可以灵活的根据自己的需求选择恰当的rowmapper,若我们只需要迁移数据而不需要做任何数据操作时,则我们可以考虑使用ColumnMapRowMapper,这将会大大减少我们需要写的代码,其灵活性也更强。
还没有评论,来说两句吧...