一. 概述
在使用原生的 jdbc 时,常规开发需要将 ResultSet 对象遍历访问并创建一个对象来承载这个结果。代码如下:
while (resultSet.next()) {
long id = resultSet.getLong(1);
String name = resultSet.getString(2);
int age = resultSet.getInt(3);
Sex sex = Sex.valueOf(resultSet.getString(4));
Student student = new Student();
student.setId(id);
student.setName(name);
student.setAge(age);
student.setSex(sex);
.....
}
复制代码
在后面的章节中,通过罗列各种场景,逐步对代码进行调整,以满足各种场景的使用;在过程中会运用到抽象思维以及设计模式来进行。
二. ResultSet 抽象处理
遍历 ResultSet 获取数据进行封装成 java 对象,具体如何封装,可以进行抽象化。这时就可以设计一个接口来抽象化,由子类实现不同类型的封装处理,代码如下:
public interface ResultTransformer<T> {
List<T> transform(ResultSet rs);
}
复制代码
三. ColumnTypeHandler 抽象处理
在遍历获取表字段值时都需要根据不同类型从而调用 ResultSet 对象中不同的方法,例如 getString()、getInt()等;因此可以抽象成一个接口 ColumnTypeHandler;
public interface ColumnTypeHandler<T> {
Class<T> getJavaType();
T getValue(ResultSet rs, String labelName) throws SQLException;
}
复制代码
3.1 TypeMapping 门户
在遍历过程,需要匹配类型从而找到对应的 ColumnTypeHandler,如果使用 if 语句,代码将及其难看,因此采用 Map 集合形式来替换。如果 Map 集合以及匹配动作放到 ResultSet 子类,有很强的耦合。因此增加一个中间类来专门处理这个 Map 集合,其大体提供注册 Type 处理器,查找 Type 处理器等功能。代码如下:
public final class TypeMapping {
private static final Map<Class<?>, ColumnTypeHandler<?>> defaultTypes = new HashMap<Class<?>,ColumnTypeHandler<?>>();
private final Map<Class<?>,ColumnTypeHandler<?>> typeByClass = new HashMap<Class<?>,ColumnTypeHandler<?>>();
static {
registerDefault(new IntegerColumnTypeHandler());
registerDefault(new StringColumnTypeHandler());
registerDefault(new LongColumnTypeHandler());
}
private static void registerDefault(ColumnTypeHandler<?> type) {
defaultTypes.put(type.getJavaType(), type);
}
private <T> ColumnTypeHandler<T> getDefault(Class<T> clazz){
return (ColumnTypeHandler<T>) defaultTypes.get(clazz);
}
public <T> ColumnTypeHandler<?> getType(Class<T> clazz) {
ColumnTypeHandler<?> handler = typeByClass.get(clazz);
if (handler == null) {
return getDefault(clazz);
}
return (ColumnTypeHandler<T>) handler;
}
public void register(ColumnTypeHandler<?> type) {
typeByClass.put(type.getJavaType(), type);
}
}
复制代码
四. 配置类
TypeMapping 对象具有配置特性,也就是需要开发者注册定制化的处理器。后续会有很多诸如此类的功能需要配置的,如果分别去设置的化,会增加开发者的记忆负担。因此增加一个中间类,将这些类全部收拢起来,统一提供配置方法。
public final class Configuration {
private TypeMapping typeMapping = new TypeMapping();
public void register(ColumnTypeHandler<?> type) {
typeMapping.register(type);
}
public TypeMapping getTypeMapping() {
return typeMapping;
}
}
复制代码
五. 总结
经过上面的抽象化,相关实现类被隐藏,感兴趣的可以自行补充(相对比较简单),待后续完整实现 SQL-DSL 后开源出来;
上面的测试用例的经过简化后,测试代码如下:
Configuration configuration = new Configuration();
//注册Sex类型的处理器
configuration.register(new EnumColumnTypeHandler<Sex>(Sex.class));
BeanResultTransformer<Student> transformer = new BeanResultTransformer<>(Student.class, configuration);
List<Student> students = transformer.transform(rs);
复制代码
评论