Mybatis學習之自定義持久層框架(六)創建 Executor接口及其實現類SimpleExecutor的接口 query() update()執行JDBC代碼

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章