創建 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();
}
}