解析
1、创建一个ParameterMappingTokenHandler 处理器
2、创建一个GenericTokenParser 类,并初始化开始标记,结束标记,处理器
3、执行genericTokenParser.parse(sql);获取解析后的sql‘’,以及在parameterMappingTokenHandler 中存放了参数名称的集合。
4、将解析后的sql 和参数封装到BoundSql 实体类中。
/**
* 解析自定义占位符
* @param sql
* @return
*/
private BoundSql getBoundSql(String sql){
ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
GenericTokenParser genericTokenParser = new GenericTokenParser("#{","}",parameterMappingTokenHandler);
String parse = genericTokenParser.parse(sql);
return new BoundSql(parse,parameterMappingTokenHandler.getParameterMappings());
}
将参数注入到preparedStatement 中
上面的就完成了sql,的解析,但是我们知道上面得到的sql 还是包含 JDBC的 占位符,所以我们需要将参数注入到preparedStatement 中。
1、通过boundSql.getSqlText()获取带有占位符的sql.
2、接收参数名称集合 parameterMappingList
3、通过mapper.getParmType() 获取到参数的类。
4、通过getDeclaredField(content)方法获取到参数类的Field。
5、通过Field.get() 从参数类中获取对应的值
6、注入到preparedStatement 中
BoundSql boundSql=getBoundSql(mapper.getSql());
String sql=boundSql.getSqlText();
List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
//获取preparedStatement,并传递参数值
PreparedStatement preparedStatement=connection.prepareStatement(sql);
Class<?> parmType = mapper.getParmType();
for (int i = 0; i < parameterMappingList.size(); i++) {
ParameterMapping parameterMapping = parameterMappingList.get(i);
String content = parameterMapping.getContent();
Field declaredField = parmType.getDeclaredField(content);
declaredField.setAccessible(true);
Object o = declaredField.get(parm[0]);
preparedStatement.setObject(i+1,o);
}
System.out.println(sql);
return preparedStatement;
执行sql
其实还是调用JDBC 的executeQuery()方法或者execute()方法
//执行sql
ResultSet resultSet = preparedStatement.executeQuery();
通过反射将结果集封装成对象
在获取到resultSet 后,我们进行封装处理,和参数处理是类似的。
1、创建一个ArrayList
2、获取返回类型的类
3、循环从resultSet中取数据
4、获取属性名和属性值
5、创建属性生成器
6、为属性生成写方法,并将属性值写入到属性中
7、将这条记录添加到list 中
8、返回list
/**
* 封装结果集
* @param mapper
* @param resultSet
* @param <E>
* @return
* @throws Exception
*/
private <E> List<E> resultHandle(Mapper mapper,ResultSet resultSet) throws Exception{
ArrayList<E> list=new ArrayList<>();
//封装结果集
Class<?> resultType = mapper.getResultType();
while (resultSet.next()) {
ResultSetMetaData metaData = resultSet.getMetaData();
Object o = resultType.newInstance();
int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
//属性名
String columnName = metaData.getColumnName(i);
//属性值
Object value = resultSet.getObject(columnName);
//创建属性描述器,为属性生成读写方法
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName,resultType);
Method writeMethod = propertyDescriptor.getWriteMethod();
writeMethod.invoke(o,value);
}
list.add((E) o);
}
return list;
}
创建SqlSessionFactoryBuilder
我们现在来创建一个SqlSessionFactoryBuilder 类,来为使用端提供一个人口。
public class SqlSessionFactoryBuilder {
private Configuration configuration;
public SqlSessionFactoryBuilder(){
configuration=new Configuration();
}
public SqlSessionFactory build(InputStream in) throws DocumentException, PropertyVetoException, ClassNotFoundException {
XmlConfigBuilder xmlConfigBuilder = new XmlConfigBuilder(configuration);
configuration=xmlConfigBuilder.loadXmlConfig(in);
SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(configuration);
return sqlSessionFactory;
}
}
可以看到就一个build 方法,通过SqlMapConfig的文件流将信息解析到configuration,创建并返回一个sqlSessionFactory 。
到此,整个框架端已经搭建完成了,但是我们可以看到,只实现了select 的操作,update、inster、delete 的操作我们在我后面提供的源码中会有实现,这里只是将整体的设计思路和流程。
测试
终于到了测试的环节啦。我们前面写了自定义的持久层,我们现在来测试一下能不能正常的使用吧。
见证奇迹的时刻到啦