Pure handwriting Myatis framework

1, the interface layer - and interacts database

MyBatis and interactive database in two ways:

Using traditional API MyBatis provided;

Use Mapper interfaces;

2, using the interface Mapper

MyBatis configuration file each <mapper> Mapper abstracted as a node interfaces:

This method declared in the interface and <mapper> node <select | update | delete | insert> entry corresponding node, i.e. <select | update | delete | insert> id node is the name of a method Mapper interface,

It represents the value of parameter types parameterType Mapper corresponding method, and the value corresponding to the Mapper resultMap return type or return a result set element represented by the interface type.

According to the configuration specification MyBatis configured by SqlSession.getMapper (XXXMapper.class) method, a method based MyBatis information corresponding interface declaration, to generate a dynamic proxy mechanism Mapper example,

We used when a certain method Mapper interface, MyBatis method name and parameters based on this type of method, determining Statement Id, or through the bottom SqlSession.select ( "statementId", parameterObject) ; or SqlSession.update ( "statementId", parameterObject) ; and so on to achieve operation of the database, MyBatis references Mapper interface to call this way, simply to meet the needs of interface-oriented programming.

(In fact, there is a reason that the interface-oriented programming, so that the user can use the notes on the interface to configure SQL statements, so that you can out of the XML configuration file, and "0 configuration").

 

3, the data processing layer

Data processing layer can be said that the core of MyBatis, speaking from the large side, it is to complete two functions:

Build dynamic SQL statements by passing parameters;

Execute SQL statements and query results package of integrated List <E>;

4, the main components and their relationship

From the perspective of MyBatis code implementation point of view, the main core MyBatis has the following components:

SqlSession: as the main top-level API MyBatis work, presentation and session database interaction, to complete the necessary database CRUD functions;

Executor: MyBatis actuators, it is the core of MyBatis scheduling, responsible for generating SQL statements and query cache maintenance;

StatementHandler: JDBC Statement encapsulates the operation, the operation is responsible for the JDBC statement, such as setting parameters, converting the result set to a Statement List collection.

ParameterHandler: the user is responsible for passing parameters into parameters required JDBC Statement;

ResultSetHandler: responsible for converting JDBC ResultSet returned result set object into a set of List type;

TypeHandler: responsible for mapping and converting between data types and data type jdbc Java;

MappedStatement: MappedStatement maintains a <select | insert update | | delete> node package;

SqlSource: parameterObject The user is responsible for transmitting the dynamically generated SQL statements, the encapsulation information to BoundSql object, and return;

BoundSql: represents a dynamically generated SQL statements and the corresponding parameter information;

Configuration: All MyBatis configuration information is maintained in the Configuration object;

5, pure handwritten codes Mybatis

  Idea: custom annotation reflection + + dynamic proxy implementation

Add dependencies

<dependencies>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.37</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.8</version>
        </dependency>
    </dependencies>

 

定义JDBCUtils:

package com.yehui.utils;

import sun.security.krb5.Config;

import java.sql.*;
import java.util.List;

public class JDBCUtils {
    private static String connect;
    private static String driverClassName;
    private static String URL;
    private static String username;
    private static String password;
    private static boolean autoCommit;

    private static Connection conn;
    private static PreparedStatement pre;
    static {
        Config();
    }
    private static void Config(){
        /*
         * 获取驱动
         */
        driverClassName = "com.mysql.jdbc.Driver";
        /*
         * 获取URL
         */
        URL = "jdbc:mysql://localhost:3306/study?useUnicode=true&characterEncoding=utf8";
        /*
         * 获取用户名
         */
        username = "root";
        /*
         * 获取密码
         */
        password = "root";
        /*
         * 设置是否自动提交,一般为false不用改
         */
        autoCommit = false;
    }
    /**
     * 载入数据库驱动类
     */
    private static boolean load(){
        try {
            Class.forName(driverClassName);
            return true;
        } catch (ClassNotFoundException e) {
            System.out.println("驱动类 " + driverClassName + " 加载失败");
            e.printStackTrace();
        }
        return false;
    }
    /**
     * 专门检查缓存的连接是否不可以被使用 ,不可以被使用的话,就返回 true
     */
    private static boolean invalid() {
        if (conn != null) {
            try {
                if (conn.isClosed() || !conn.isValid(3)) {
                    return true;
                    /*
                     * isValid方法是判断Connection是否有效,如果连接尚未关闭并且仍然有效,则返回 true
                     */
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            /*
             * conn 既不是 null 且也没有关闭 ,且 isValid 返回 true,说明是可以使用的 ( 返回 false )
             */
            return false;
        } else {
            return true;
        }
    }
    /**
     * 建立数据库连接
     */
    public static Connection connect() {
        if (invalid()) { /* invalid为true时,说明连接是失败的 */
            /* 加载驱动 */
            load();
            try {
                /* 建立连接 */
                conn = DriverManager.getConnection(URL, username, password);
            } catch (SQLException e) {
                System.out.println("建立 " + connect + " 数据库连接失败 , " + e.getMessage());
            }
        }
        return conn;
    }
    /**
     * 设置是否自动提交事务
     **/
    public static void transaction() {

        try {
            conn.setAutoCommit(autoCommit);
        } catch (SQLException e) {
            System.out.println("设置事务的提交方式为 : " + (autoCommit ? "自动提交" : "手动提交") + " 时失败: " + e.getMessage());
        }
    }

    /** 提交事务 */
    private static void commit(Connection c) {
        if (c != null && !autoCommit) {
            try {
                c.commit();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /** 回滚事务 */
    private static void rollback(Connection c) {
        if (c != null && !autoCommit) {
            try {
                c.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 释放资源
     **/
    public static void release(Object cloaseable) {

        if (cloaseable != null) {

            if (cloaseable instanceof ResultSet) {
                ResultSet rs = (ResultSet) cloaseable;
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            if (cloaseable instanceof Statement) {
                Statement st = (Statement) cloaseable;
                try {
                    st.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            if (cloaseable instanceof Connection) {
                Connection c = (Connection) cloaseable;
                try {
                    c.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

        }

    }
    /**
     * 根据给定的带参数占位符的SQL语句,创建 PreparedStatement 对象
     *
     * @param SQL
     *            带参数占位符的SQL语句
     * @return 返回相应的 PreparedStatement 对象
     */
    private static PreparedStatement prepare(String SQL, boolean autoGeneratedKeys) {

        PreparedStatement ps = null;
        connect();
        /* 如果连接是无效的就重新连接 */
        transaction();
        /* 设置事务的提交方式 */
        try {
            if (autoGeneratedKeys) {
                ps = conn.prepareStatement(SQL, Statement.RETURN_GENERATED_KEYS);
            } else {
                ps = conn.prepareStatement(SQL);
            }
        } catch (SQLException e) {
            System.out.println("创建 PreparedStatement 对象失败: " + e.getMessage());
        }

        return ps;

    }
    /*
     *
     * @param SQL 需要执行的 INSERT 语句
     *
     * @param autoGeneratedKeys 指示是否需要返回由数据库产生的键
     *
     * @param params 将要执行的SQL语句中包含的参数占位符的 参数值
     *
     * @return 如果指定 autoGeneratedKeys 为 true 则返回由数据库产生的键; 如果指定 autoGeneratedKeys
     * 为 false 则返回受当前SQL影响的记录数目
     */
    public static Object insert(String SQL, List<?> params,boolean autoGeneratedKeys) {
        //获得连接
       conn = connect();
       int var = 0;
        try {
            if (SQL == null || SQL.trim().isEmpty()) {
                throw new RuntimeException("你没有指定SQL语句,请检查是否指定了需要执行的SQL语句");
            }
            // 如果不是 insert 开头开头的语句
            if (!SQL.trim().toLowerCase().startsWith("insert")) {
                System.out.println(SQL.toLowerCase());
                throw new RuntimeException("你指定的SQL语句不是插入语句,请检查你的SQL语句");
            }
            if (params!=null&&params.size()>0) {//有参数的情况
                //创建连接对象
                pre =  prepare(SQL,autoGeneratedKeys);
                for (int i = 0; i <params.size(); i++) {
                    pre.setObject(i+1,typeof(params.get(i)));
                }
                int count = pre.executeUpdate();
                if (autoGeneratedKeys) {// 如果希望获得数据库产生的键
                    ResultSet rs = pre.getGeneratedKeys(); // 获得数据库产生的键集
                    if (rs.next()) { // 因为是保存的是单条记录,因此至多返回一个键
                        var = rs.getInt(1); // 获得值并赋值给 var 变量
                    }
                 }else{
                    var = count;// 如果不需要获得,则将受SQL影像的记录数赋值给 var 变量
                }
                commit(conn);
            }else{// 说明没有参数
                Statement st = statement();
                Connection connection = st.getConnection();
                int count = st.executeUpdate(SQL);
                if (autoGeneratedKeys) { // 如果企望获得数据库产生的键
                    ResultSet rs = st.getGeneratedKeys(); // 获得数据库产生的键集
                    if (rs.next()) { // 因为是保存的是单条记录,因此至多返回一个键
                        var = rs.getInt(1); // 获得值并赋值给 var 变量
                    }
                } else {
                    var = count; // 如果不需要获得,则将受SQL影像的记录数赋值给 var 变量
                }
                commit(conn); // 提交事务
            }
        } catch (Exception e) {
            System.out.println("数据保存失败: " + e.getMessage());
            rollback(conn);
        }
        return var;
    }

    /**
     * 创建 Statement 对象
     */
    public static Statement statement() {
        Statement st = null;
        connect();
        /* 如果连接是无效的就重新连接 */
        transaction();
        /* 设置事务的提交方式 */
        try {
            st = conn.createStatement();
        } catch (SQLException e) {
            System.out.println("创建 Statement 对象失败: " + e.getMessage());
        }

        return st;
    }

    /**
     * 类型转换
     * @param o
     * @return
     */
    private static Object typeof(Object o) {
        Object r = o;

        if (o instanceof java.sql.Timestamp) {
            return r;
        }
        // 将 java.util.Date 转成 java.sql.Date
        if (o instanceof java.util.Date) {
            java.util.Date d = (java.util.Date) o;
            r = new java.sql.Date(d.getTime());
            return r;
        }
        // 将 Character 或 char 变成 String
        if (o instanceof Character || o.getClass() == char.class) {
            r = String.valueOf(o);
            return r;
        }
        return r;
    }

    /**
     * 得到查询结
     * @param newSql
     * @param parameValues
     * @return
     */
    public static ResultSet select(String newSql, List<Object> parameValues) {
        ResultSet rs = null;
        //获得连接
        conn = connect();
        if (newSql == null || newSql.trim().isEmpty() || !newSql.trim().toLowerCase().startsWith("select")) {
            throw new RuntimeException("你的SQL语句为空或不是查询语句");
        }
        try {
            if (parameValues!=null&&parameValues.size()>0) {//这是有参数的情况下
                //获取连接对象
                pre = conn.prepareStatement(newSql);
                for (int i = 0; i < parameValues.size(); i++) {
                    pre.setObject(i+1,parameValues.get(i));
                }
                rs = pre.executeQuery();
            }else{
                /* 说明没有传入任何参数 */
                Statement st = statement();
                rs = st.executeQuery(newSql); // 直接执行不带参数的 SQL 语句
            }
        } catch (Exception e) {
            System.out.println("执行SQL失败: " + e.getMessage());
        }
        return rs;
    }
}

 

 定义SQLUtils

package com.yehui.utils;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class SQLUtils {

    /**
     * 得到插入sql需要传递的参数
     * @param insertSql
     */
    public static List<String> getInsertSqlParams(String insertSql){
        Integer startIndex = insertSql.indexOf("values");
        insertSql = insertSql.substring(startIndex+"values".length())
                .replace("#{","")
                .replace("}","")
                .replace("(","")
                .replace(")","");
       return Arrays.asList(insertSql.split(","));
    }
    /**
     * 得到查询sql需要传递的参数
     * @param selectSql
     */
    public static List<String> getSelectSqlParams(String selectSql){
        Integer startIndex = selectSql.indexOf("where");
        selectSql = selectSql.substring(startIndex+"where".length());
        String[] ands = selectSql.split("and");
        List<String> list = new ArrayList<>();
        for (String and : ands) {
            String[] split = and.split("=");
            String replace = split[1].replace("#{", "").replace("}", "").trim();
            list.add(replace);
        }
        return list;
    }
    /**
     * 将sql参数转换为?
     * @param sql
     * @param params
     * @return
     */
    public static String covertSql(String sql,List<String> params){
        for (String param : params) {
           sql =  sql.replace("#{"+param+"}","?");
        }
       return sql;
    }

    public static void main(String[] args) {
            //insert into account(username,money) values(#{username},#{money})
        //select * from account where username=#{username} and money=#{money}
        List<String> p = new ArrayList<>();
        p.add("username");
        p.add("money");
        getSelectSqlParams("select * from account where username=#{username} and money=#{money}");
    }
}

 

 定义一个mapper接口

package com.yehui.mapper;

import com.yehui.annotation.ExtInsert;
import com.yehui.annotation.ExtParam;
import com.yehui.annotation.ExtSelect;
import com.yehui.entity.User;

public interface UserMapper {
    // 1.接口既然不能被实例化?那么我们是怎么实现能够调用的?
    // 2.参数如何和sql参数绑定
    // 3.返回结果
    @ExtSelect("select * from account where username=#{username} and money=#{money} ")
    User selectUser(@ExtParam("username") String username, @ExtParam("money") Integer money);

    @ExtInsert("insert into account(username,money) values(#{username},#{money})")
    int insertUser(@ExtParam("username") String username, @ExtParam("money") Integer money);
}

 

定义注解

@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtInsert {

    String value();
}

// Mapper映射注解
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtMapper {

}

@Target(value = { ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtParam {

    String value();

}

// 查询注解
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtSelect {

    String value();

}

定义动态代理类

package com.yehui;

import com.sun.xml.internal.ws.api.model.MEP;
import com.yehui.annotation.ExtInsert;
import com.yehui.annotation.ExtParam;
import com.yehui.annotation.ExtSelect;
import com.yehui.utils.JDBCUtils;
import com.yehui.utils.SQLUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class MyInvocationHandlerMbatis implements InvocationHandler {
    /**
     * 这个就是我们要代理的真实对象
     */
    private Object target;

    /**
     * 构造方法,给我们要代理的真实对象赋初值
     *
     * @param target
     */
    public MyInvocationHandlerMbatis(Object target) {
        this.target = target;
    }
    /**
     * 该方法负责集中处理动态代理类上的所有方法调用。 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
     *
     * @param proxy
     *            代理类实例
     * @param method
     *            被调用的方法对象
     * @param args
     *            调用参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //获取注解
        if(method.isAnnotationPresent(ExtInsert.class)){
            ExtInsert extInsert = method.getAnnotation(ExtInsert.class);
            //得到注解上面的sql
            String insertSql = extInsert.value();
            //得到参数列表
            ConcurrentHashMap<String,Object> paramMap =  getExtParams(method,args);
            //得到注解上面的参数
            List<String> params = SQLUtils.getInsertSqlParams(insertSql);
            //转换sql 替换为?
            String newSql = SQLUtils.covertSql(insertSql, params);
            //得到参数值
            List<Object> parameValues = new ArrayList<>();
            for (String parame : params) {
                parameValues.add(paramMap.get(parame));
            }
            //执行sql语句
            return JDBCUtils.insert(newSql,parameValues,false);
        }
        //得到查询注解
        if(method.isAnnotationPresent(ExtSelect.class)){
            //得到sql上面的注解
            ExtSelect extSelect = method.getAnnotation(ExtSelect.class);
            //得到注解上的值
            String selectSql = extSelect.value();
            //得到参数列表
            ConcurrentHashMap<String,Object> paramMap =  getExtParams(method,args);
            //得到注解上面的参数
            List<String> params = SQLUtils.getSelectSqlParams(selectSql);
            //转换sql 替换为?
            String newSql = SQLUtils.covertSql(selectSql, params);
            //得到参数值
            List<Object> parameValues = new ArrayList<>();
            for (String parame : params) {
                parameValues.add(paramMap.get(parame));
            }
            //执行sql得到查询结果
            ResultSet  rs = JDBCUtils.select(newSql,parameValues);
            if (!rs.next()) {
                // 没有查找数据
                return null;
            }
            //得到返回值类型
            Class<?> returnType = method.getReturnType();
            // 向上移动
            rs.previous();
            //得到实例化对象
            Object obj = returnType.newInstance();
            while (rs.next()){
                for(String parm:params){
                    //得到对应的属性
                    Field declaredField = returnType.getDeclaredField(parm);
                    declaredField.setAccessible(true);
                    // 赋值参数
                    declaredField.set(obj,rs.getObject(parm));
                }
            }
            return obj;
        }
        return null;
    }

    /**
     * 将参数放入map中
     */
    public static ConcurrentHashMap<String,Object> getExtParams(Method method,Object[] args){
        Parameter[] parameters = method.getParameters();
        ConcurrentHashMap<String,Object> paramMap = new ConcurrentHashMap<>();
        for (int i = 0; i < parameters.length; i++) {
            // 参数信息
            Parameter parameter = parameters[i];
            if(parameter.isAnnotationPresent(ExtParam.class)){
                ExtParam extParam = parameter.getAnnotation(ExtParam.class);
                // 参数名称
                String value = extParam.value();
                //args[i] 参数值
                paramMap.put(value,args[i]);
            }
        }
        return paramMap;
    }
}

 

 创建SqlSession

public class SqlSession {

    public static <T> T getMapper(Class<T> tClass){
       return (T) Proxy.newProxyInstance(tClass.getClassLoader(),new Class[]{tClass},
                new MyInvocationHandlerMbatis(tClass));
    }
}

 

创建一个实体

package com.yehui.entity;

public class User {
    private String username;
    private Integer money;
    private Integer id;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getMoney() {
        return money;
    }

    public void setMoney(Integer money) {
        this.money = money;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "User{" + "username='" + username + '\'' + ", money=" + money + ", id=" + id + '}';
    }
}

 测试查询:

 UserMapper mapper = SqlSession.getMapper(UserMapper.class);
        User jack = mapper.selectUser("jack", 6000);
        System.out.println(jack);

 

测试新增:

 UserMapper mapper = SqlSession.getMapper(UserMapper.class);
        int count = mapper.insertUser("user111", 1000);
        System.out.println(count);

 

 网站:https://blog.csdn.net/kuailebuzhidao/article/details/88355236

 

Guess you like

Origin www.cnblogs.com/cxyyh/p/10960229.html