这段时间小编工作较忙,然后好久没更新博客了,上次小编在博客精通Mybatis之Jdbc处理器StatementHandler还落下了对结果集封装处理以及MetaObject反射工具的详细讲解。结果集的封装映射比较复杂,一次肯定讲解不完,小编打算分三次讲完。首先我们来回顾一下查询后结果集处理流程,再次查看其源码中的步骤,然后讲解一下MetaObject和一部分映射体系。
通过StatementHandler执行sql查询到结果集后,如下图处理:
resultSetHandler由一条或多行数据库的行记录,然后一条一条转换成对应的Object(根据映射),放入ResultContext,最后根据ResultHandler处理放入到其list中。那为什么不直接放入ResultHandler的list中,原因是ResultContext这个结果集上下文组件可以控制解析数量以及状态,也就是是否需要继续解析前面的结果集行。
小编用代码来解释一下ResultContext的作用:
@Test
public void ResultContextTest(){
final List<Object> resultList = new ArrayList<Object>();
ResultHandler resultHandler = new ResultHandler() {
public void handleResult(ResultContext resultContext) {
int resultCount = resultContext.getResultCount();
if(resultCount>2){
resultContext.stop();
}
Object resultObject = resultContext.getResultObject();
resultList.add(resultObject);
}
};
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.REUSE);
sqlSession.select("mapper.EmployeeMapper.selectAll",resultHandler);
System.out.println(resultList.size());
}
结果:
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@47d90b9e]
==> Preparing: select * from employee
==> Parameters:
<== Columns: id, name
<== Row: 1, wangwu
<== Row: 2, zhaoliu
<== Row: 3, tom
3
当执行器执行完sql拿出结果集后需要经历一系列过程才能将数据库表中的行数据变成我们需要的java对象。小编用图给大家说明一下:
上图所示,可能有点杂乱,其实所有的操作都在DefaultResultSetHandler类,这基本是mybatis框架中代码最多的类,大家如果把这个类理顺了,基本上就对结果集处理流程全部了若指掌。小编稍微理了一下顺序,必要时源码解读。
上面的流程还没涉及嵌套查询和结果集映射(一对多,多对多等等),那小编接着往下讲解。
结果集映射之前,先了解一下MetaObject反射工具,这个比较重要,这个工具在封装属性值和封装结果值的时候都需要用到,小编觉得大家在封装相应的工具类的时候都可以借鉴,而且这个类相对是底层的,不依赖其他的类,有时候可以直接使用。废话不多说,进入正题。
当结果集中的列填充至JAVA Bean属性。这就必须用到反射,而Bean的属性多种多样,有普通属性、对象、集合、Map都有可能。为了更加方便的操作Bean的属性,MyBatis提供了MeataObject 工具类,其简化了对象属性的操作。其具体功能如下:
代码示例:
三个类:分别为公司,部门和员工
@Data
public class Company {
private Long id;
private String companyName;
private List<Department> departmentList;
}
@Data
public class Department {
private Long id;
private String departmentName;
private List<Employee> employeeList;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
private Long id;
private String name;
private Department department;
private Company company;
}
测试代码:
public class MetaObjectTest {
@Test
public void reflectTest(){
Company company = new Company();
Configuration configuration = new Configuration();
MetaObject metaObject = configuration.newMetaObject(company);
//设置公司名称值
metaObject.setValue("companyName","I'm your Boss");
Object companyName = metaObject.getValue("companyName");
//获取属性
System.out.println(companyName);
}
@Test
public void reflectTest1(){
Employee employee = new Employee();
Configuration configuration = new Configuration();
MetaObject metaObject = configuration.newMetaObject(employee);
//employee的department本身是空的可以直接赋值
metaObject.setValue("department.departmentName","finance department ");
Object departmentName = metaObject.getValue("department.departmentName");
//获取属性以及本身的空对象
System.out.println(departmentName);
System.out.println(employee.getDepartment());
}
@Test
public void reflectTest2(){
Employee employee = new Employee();
Configuration configuration = new Configuration();
MetaObject metaObject = configuration.newMetaObject(employee);
//employee的department本身是空的可以直接赋值以及驼峰命名
String property = metaObject.findProperty("department.department_name", true);
//查找驼峰命名的属性名称
System.out.println(property);
metaObject.setValue(property,"finance department ");
Object departmentName = metaObject.getValue(property);
//获取属性以及本身的空对象
System.out.println(departmentName);
System.out.println(employee.getDepartment());
}
@Test
public void reflectTest3(){
Company company = new Company();
Configuration configuration = new Configuration();
MetaObject metaObject = configuration.newMetaObject(company);
//因为结合metaObject不能直接生成
Department department = new Department();
department.setDepartmentName("technical division");
List<Department> departments = Collections.singletonList(department);
metaObject.setValue("departmentList",departments);
//获取属性
System.out.println(metaObject.getValue("departmentList[0].departmentName"));
//还可以继续设置属性,不停的拿出属性
Employee employee = new Employee();
employee.setName("Bob");
department.setEmployeeList(Collections.singletonList(employee));
System.out.println(metaObject.getValue("departmentList[0].employeeList[0].name"));
}
}
大家有空可以根据代码直接去调试一下。接下来看下MetaObject的结构
小编以上面示例代码为例讲一下MetaObject获取值的流程。
如:"employeeList[0].department.name"这个取值比较麻烦的就这个吧。
代码如下:
@Test
public void reflectTest4(){
Department department = new Department();
department.setDepartmentName("technical division");
//还可以继续设置属性,不停的拿出属性
Employee employee = new Employee();
employee.setName("Bob");
department.setEmployeeList(Collections.singletonList(employee));
employee.setDepartment(department);
Configuration configuration = new Configuration();
MetaObject metaObject = configuration.newMetaObject(department);
System.out.println(metaObject.getValue("employeeList[0].department.departmentName"));
}
流程中方法说明:
MeataObject.getValue()
获取值的入口,首先根据属性名"employeeList[0].department.name" 解析成PropertyTokenizer,并基于属性中的“.” 来判断是否为子属性值,如果是就递归调用getValue()获取子属性对象。然后在递归调用getValue()获取子属性下的属性。直到最后的name属性获取成功。上图并不完整啊。留给大家自己探索。
MeataObject.setValue()
流程与getValue()类似,不同在于如果子属性不存在,则会尝试创建子属性。这个留给大家自己调试和阅读代码。
getValue方法
public Object getValue(String name) {
//分割字符串名称
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
//有子属性 然后先取出最上面的一个employeeList[0]
//如果是employeeList先取出结果集然后再根据索引取出对应的值
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
return null;
} else {
//继续取子属性
return metaValue.getValue(prop.getChildren());
}
} else {
//通过反射取值
//第一次是取
return objectWrapper.get(prop);
}
}
PropertyTokenizer 分割名称
public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
private String name;
private final String indexedName;
private String index;
private final String children;
public PropertyTokenizer(String fullname) {
//以.为分割并取到最近的一个下标
int delim = fullname.indexOf('.');
if (delim > -1) {
//不为空则分割第一个名称上面则为employeeList[0]
name = fullname.substring(0, delim);
//children为department.name
children = fullname.substring(delim + 1);
} else {
//没有则直接取出,children为null
name = fullname;
children = null;
}
//indexName 与名称相同 employeeList[0]
indexedName = name;
//如果是数组列表
delim = name.indexOf('[');
if (delim > -1) {
//取出index的值上面就是0
index = name.substring(delim + 1, name.length() - 1);
//获取名称employeeList
name = name.substring(0, delim);
}
}
@Override
public boolean hasNext() {
return children != null;
}
@Override
public PropertyTokenizer next() {
return new PropertyTokenizer(children);
}
metaObjectForProperty方法
public MetaObject metaObjectForProperty(String name) {
Object value = getValue(name);
return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
}
BaseWrapper类
@Override
public Object get(PropertyTokenizer prop) {
if (prop.getIndex() != null) {
//获取集合的值
Object collection = resolveCollection(prop, object);
//获取集合对应的索引值
return getCollectionValue(prop, collection);
} else {
//不是集合的话直接根据反射取值
return getBeanProperty(prop, object);
}
}
protected Object resolveCollection(PropertyTokenizer prop, Object object) {
if ("".equals(prop.getName())) {
return object;
} else {
return metaObject.getValue(prop.getName());
}
}
//集合类取值方法
protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {
if (collection instanceof Map) {
return ((Map) collection).get(prop.getIndex());
} else {
int i = Integer.parseInt(prop.getIndex());
if (collection instanceof List) {
return ((List) collection).get(i);
} else if (collection instanceof Object[]) {
return ((Object[]) collection)[i];
} else if (collection instanceof char[]) {
return ((char[]) collection)[i];
} else if (collection instanceof boolean[]) {
return ((boolean[]) collection)[i];
} else if (collection instanceof byte[]) {
return ((byte[]) collection)[i];
} else if (collection instanceof double[]) {
return ((double[]) collection)[i];
} else if (collection instanceof float[]) {
return ((float[]) collection)[i];
} else if (collection instanceof int[]) {
return ((int[]) collection)[i];
} else if (collection instanceof long[]) {
return ((long[]) collection)[i];
} else if (collection instanceof short[]) {
return ((short[]) collection)[i];
} else {
throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array.");
}
}
}
//根据属性名称取值
private Object getBeanProperty(PropertyTokenizer prop, Object object) {
try {
Invoker method = metaClass.getGetInvoker(prop.getName());
try {
return method.invoke(object, NO_ARGUMENTS);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} catch (RuntimeException e) {
throw e;
} catch (Throwable t) {
throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ". Cause: " + t.toString(), t);
}
}
小伙伴有没有被绕晕呢,反射的代码大家自己看下也不难。看完流程后再看代码其实很容易了
小编就先到这儿,今天主要是说明了结果集处理的大致流程大家可以自行debug查看DefaultResultSetHandler类的方法,里面的细节还是很多的,再就是MetaObject的使用以及他是如何获取属性值的流程,设置值的流程留给大家了。希望继续关注小编,小编接下来将嵌套查询,懒加载延迟加载的细节。