创建 Executor接口及其实现类SimpleExecutor的接口
package com.lwl.sqlSession;
import com.lwl.pojo.Configuration;
import com.lwl.pojo.MappedStatement;
import java.util.List;
public interface Executor {
/**
* 执行JDBC代码
* @param configuration
* @param mappedStatement
* @param params
* @param <E>
* @return
*/
public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement,Object... params) throws Exception;
/**
* 执行新增 编辑
*
* @param configuration configuration
* @param statement statement
* @param param param
* @return int
* @throws Exception 异常
* @author bo.huang update
* @date 2019-12-30 20:23
*/
int update(Configuration configuration, MappedStatement statement, Object... param) throws Exception;
}
执行JDBC代码
参数封装 sql语句编译 结果集封装
package com.lwl.sqlSession;
import com.lwl.config.BoundSql;
import com.lwl.pojo.Configuration;
import com.lwl.pojo.MappedStatement;
import com.lwl.utils.GenericTokenParser;
import com.lwl.utils.ParameterMapping;
import com.lwl.utils.ParameterMappingTokenHandler;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class SimpleExecutor implements Executor {
@Override
public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception{
//1、注册驱动 获取链接
Connection connection = configuration.getDataSource().getConnection();
//2、获取sql语句
//转sql语句 将#{} 转换为 ? 并且对#{}的value解析并存储
String sql = mappedStatement.getSql();
//BoundSql 完成sql的转换,存放转换后的sql语句,以及解析过程中 对#{}里面的值进行存储
BoundSql boundSql = getBoundSql(sql);
//参数封装
PreparedStatement preparedStatement = getPreparedStatement(mappedStatement, connection, boundSql, params);
//5执行sql
ResultSet resultSet = preparedStatement.executeQuery();
String resultType = mappedStatement.getResultType();
Class<?> resultTypeClass = getClassType(resultType);
ArrayList<Object> objects = new ArrayList<>();
//6封装结果集 遍历resultSet
while (resultSet.next()){
Object o = resultTypeClass.newInstance();
//获取元数据
ResultSetMetaData metaData = resultSet.getMetaData();
//metaData.getColumnCount() 总列数
for (int i = 1; i <= metaData.getColumnCount() ; i++) {
//获取列名 字段名
String columnName = metaData.getColumnName(i);
Object value = resultSet.getObject(columnName);
//使用反射或者内省 根据数据库表和实体的对应关系 创建实体对象 完成结果封装
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName,resultTypeClass);
Method writeMethod = propertyDescriptor.getWriteMethod();
writeMethod.invoke(o,value);
}
objects.add(o);
}
return (List<E>) objects;
}
/**
* 执行新增 编辑
*
* @param configuration configuration
* @param statement statement
* @param param param
* @return int
* @throws Exception 异常
* @author bo.huang update
* @date 2019-12-30 20:23
*/
@Override
public int update(Configuration configuration, MappedStatement statement, Object... param) throws Exception {
//获取连接
//1、注册驱动 获取链接
Connection connection = configuration.getDataSource().getConnection();
//是否开启自动提交
connection.setAutoCommit(true);
//获取sql 框架定义的sql格式 #{}
String sql = statement.getSql();
//sql进行处理 #{} 转为 ?
BoundSql boundSql = getBoundSql(sql);
//封装参数
PreparedStatement preparedStatement = getPreparedStatement(statement, connection, boundSql, param);
//执行
int i = preparedStatement.executeUpdate();
preparedStatement.close();
connection.close();
return i;
}
/**
* 参数封装
* @param statement
* @param connection
* @param boundSql
* @param params
* @return
* @throws Exception
*/
private PreparedStatement getPreparedStatement(MappedStatement statement, Connection connection, BoundSql boundSql, Object[] params) throws Exception {
//3、获取预处理对象prepareStatement
PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqltext());
//4设置参数
//获取参数全路径
String parameterType = statement.getParameterType();
Class<?> parameterTypeClass = getClassType(parameterType);
List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
for (int i = 0; i < parameterMappingList.size(); i++) {
//取出parameterMappingList 所有的参数
ParameterMapping parameterMapping = parameterMappingList.get(i);
String content = parameterMapping.getContent();
//使用反射 根据参数名 获取实体对象的属性值, 在根据传递的参数 进行赋值
Field declaredField = parameterTypeClass.getDeclaredField(content);
//设置暴力访问
declaredField.setAccessible(true);
Object o = declaredField.get(params[0]);
preparedStatement.setObject(i+1,o);
}
return preparedStatement;
}
private Class<?> getClassType(String parameterType) throws ClassNotFoundException {
if (parameterType != null){
Class<?> aClass = Class.forName(parameterType);
return aClass;
}
return null;
}
/**
* 解析#{} :1、使用?进行占位 2、#{}的value解析并存储
* @param sql
* @return
*/
private BoundSql getBoundSql(String sql) {
//标记处理类,配置标记解析器来完成对占位符的解析处理工作
ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
//标记解析器
GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
//解析之后的sql
String parse = genericTokenParser.parse(sql);
//#{} 解析出来的参数名称
List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
BoundSql boundSql = new BoundSql(parse,parameterMappings);
return boundSql;
}
}
在此还需要使用标记处理类,配置标记解析器来完成对占位符的解析处理工作
package com.lwl.utils;
import java.util.ArrayList;
import java.util.List;
public class ParameterMappingTokenHandler implements TokenHandler {
private List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
// context是参数名称 #{id} #{username}
//
public String handleToken(String content) {
parameterMappings.add(buildParameterMapping(content));
return "?";
}
private ParameterMapping buildParameterMapping(String content) {
ParameterMapping parameterMapping = new ParameterMapping(content);
return parameterMapping;
}
public List<ParameterMapping> getParameterMappings() {
return parameterMappings;
}
public void setParameterMappings(List<ParameterMapping> parameterMappings) {
this.parameterMappings = parameterMappings;
}
}
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.lwl.utils;
/**
* @author Clinton Begin
*/
public interface TokenHandler {
String handleToken(String content);
}
package com.lwl.utils;
public class ParameterMapping {
//解析出来的参数名称
private String content;
public ParameterMapping(String content) {
this.content = content;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
GenericTokenParser 标记解析器
String parse(String text)该方法主要实现了配置文件、脚本等片段中占位符的解析、处理工作,并返回最终需要的数据。
其中,解析工作由该方法完成,处理工作是由处理器handler的handleToken()方法来实现
/**
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.lwl.utils;
/**
* @author Clinton Begin
*/
public class GenericTokenParser {
private final String openToken; //开始标记
private final String closeToken; //结束标记
private final TokenHandler handler; //标记处理器
public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
this.openToken = openToken;
this.closeToken = closeToken;
this.handler = handler;
}
/**
* 解析${}和#{}
* @param text
* @return
* 该方法主要实现了配置文件、脚本等片段中占位符的解析、处理工作,并返回最终需要的数据。
* 其中,解析工作由该方法完成,处理工作是由处理器handler的handleToken()方法来实现
*/
public String parse(String text) {
// 验证参数问题,如果是null,就返回空字符串。
if (text == null || text.isEmpty()) {
return "";
}
// 下面继续验证是否包含开始标签,如果不包含,默认不是占位符,直接原样返回即可,否则继续执行。
int start = text.indexOf(openToken, 0);
if (start == -1) {
return text;
}
// 把text转成字符数组src,并且定义默认偏移量offset=0、存储最终需要返回字符串的变量builder,
// text变量中占位符对应的变量名expression。判断start是否大于-1(即text中是否存在openToken),如果存在就执行下面代码
char[] src = text.toCharArray();
int offset = 0;
final StringBuilder builder = new StringBuilder();
StringBuilder expression = null;
while (start > -1) {
// 判断如果开始标记前如果有转义字符,就不作为openToken进行处理,否则继续处理
if (start > 0 && src[start - 1] == '\\') {
builder.append(src, offset, start - offset - 1).append(openToken);
offset = start + openToken.length();
} else {
//重置expression变量,避免空指针或者老数据干扰。
if (expression == null) {
expression = new StringBuilder();
} else {
expression.setLength(0);
}
builder.append(src, offset, start - offset);
offset = start + openToken.length();
int end = text.indexOf(closeToken, offset);
while (end > -1) {存在结束标记时
if (end > offset && src[end - 1] == '\\') {//如果结束标记前面有转义字符时
// this close token is escaped. remove the backslash and continue.
expression.append(src, offset, end - offset - 1).append(closeToken);
offset = end + closeToken.length();
end = text.indexOf(closeToken, offset);
} else {//不存在转义字符,即需要作为参数进行处理
expression.append(src, offset, end - offset);
offset = end + closeToken.length();
break;
}
}
if (end == -1) {
// close token was not found.
builder.append(src, start, src.length - start);
offset = src.length;
} else {
//首先根据参数的key(即expression)进行参数处理,返回?作为占位符
builder.append(handler.handleToken(expression.toString()));
offset = end + closeToken.length();
}
}
start = text.indexOf(openToken, offset);
}
if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
return builder.toString();
}
}