深入原理,仿写自己的Mybatis(带缓存)

MyBatis是一个持久层框架,使用简单,学习成本较低,所以是我们学习源码的首选框架。

为什么Mybatis可以根据我们的xml去执行相应的sql并将结果封装成实体呢,其实归功于Java中两个很重要的东西:反射、动态代理,这可以使你不去修改程序也不用重新编译,只需要修改xml文件就可以实现sql语句的修改,下面我们深入了解一下Mybatis的启动流程并简单的仿写一个自己的Mybatis

Mybatis层次结构

  • SqlSession: ,它是 MyBatis 核心 API,主要用来执行命令,获取映射,管理事务。接收开发人员提供 Statement Id 和参数。并返回操作结果。
  • Executor :执行器,是 MyBatis 调度的核心,负责 SQL 语句的生成以及查询缓存的维护。
  • StatementHandler : 封装了JDBC Statement 操作,负责对 JDBC Statement 的操作,如设置参数、将Statement 结果集转换成 List 集合。
  • ParameterHandler : 负责对用户传递的参数转换成 JDBC Statement 所需要的参数。
  • ResultSetHandler : 负责将 JDBC 返回的 ResultSet 结果集对象转换成 List 类型的集合。
  • TypeHandler : 用于 Java 类型和 JDBC 类型之间的转换。
  • MappedStatement : 动态 SQL 的封装
  • SqlSource : 表示从 XML 文件或注释读取的映射语句的内容,它创建将从用户接收的输入参数传递给数据库的 SQL。
  • Configuration: MyBatis 所有的配置信息都维持在 Configuration 对象之中。

下面开始仿写我们的Mybatis

目录结构

├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       ├── entity
    │   │       │   └── User.java   测试实体类
    │   │       ├── mapper
    │   │       │   └── UserMapper.java   测试Mapper层接口
    │   │       └── system
    │   │           ├── Configuration.java   配置类,包含MappedStatement
    │   │           ├── MappedStatement.java   对应xml中的节点,其中包含方法的全限定名、sql、参数类型等
    │   │           ├── SimpleCatch.java   缓存,使用LRU算法实现,可定义初始化大小
    │   │           ├── XSqlSession.java   Sqlsession工厂,实现接口的实例化及方法的暴露
    │   │           ├── executor
    │   │           │   ├── BaseExecutor.java   基础执行器实现类
    │   │           │   └── Executor.java   执行器接口
    │   │           └── utils
    │   │               ├── UnderlineAndHumpUtil.java   驼峰与下划线互转
    │   │               └── XmlUtil.java   使用dom4j解析xml
    │   └── resources
    │       ├── mapper
    │       │   └── UserMapper.xml   测试Mapper
    │       └── xuwei-mybatis.xml   配置文件
    └── test
        └── java
            └── XWTest.java

相关代码

首先是我们的sqlsession工厂中的逻辑,在我们创建一个sqlsession对象的时候需要传入配置文件的目录地址,以此来初始化Configuration对象,之后重写代理方法来执行我们的相关业务,在代理方法中我们可以看到其实数据库操作只需要实现查和改,因为删和增都可以用改来实现

XSqlSession.java代码如下

package com.system;

import com.system.executor.BaseExecutor;
import com.system.executor.Executor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;

/**
 * @author xuwei
 */
public class XSqlSession implements InvocationHandler {
    private String className;
    private final Configuration configuration;

    public XSqlSession(String pathName) {
        this.configuration = Configuration.getConfiguration(pathName);
    }

    public Object getMapper(Class cls) {
        className = cls.getName();
        return Proxy.newProxyInstance(
                cls.getClassLoader(),
                new Class[]{cls},
                this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //到configuration中去获取方法对应的MappedStatement
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(className + "." + method.getName());
        //建立数据库连接
        Class.forName(configuration.getDriver());
        Connection connection = DriverManager.getConnection(configuration.getUrl(), configuration.getUsername(), configuration.getPassword());
        //建立执行器
        Executor executor = new BaseExecutor();
        System.out.println(executor);
        Object obj = null;
        if (mappedStatement.isQuery()) {
            if (method.getReturnType().isInstance(new ArrayList<>())) {
                obj = executor.queryList(connection, mappedStatement, args, configuration);
            } else {
                obj = executor.queryOne(connection, mappedStatement, args, configuration);
            }
        } else {
            obj = executor.update(connection, mappedStatement, args, configuration);
        }
        connection.close();
        return obj;
    }
}

 Configuration.java

Configuration类中包含数据库连接的基本属性以及是否启用下划线和驼峰互转

还包含了我们所有xml中的业务sql,调用了我们的XMLUtil来解析并封装

package com.system;

import com.system.utils.XmlUtil;

import java.io.File;
import java.util.List;
import java.util.Map;

/**
 * @author xuwei
 */
public class Configuration {
    private static volatile Configuration configuration;
    private String driver = null;
    private String url = null;
    private String username = null;
    private String password = null;
    private boolean openTheCatch = false;
    private SimpleCatch simpleCatch = null;
    private boolean underlineAndHump = false;
    private Map<String, MappedStatement> mappedStatementMap;

    public static Configuration getConfiguration(String pathName) {
        if (configuration == null) {
            synchronized (Configuration.class) {
                if (configuration == null) {
                    configuration = new Configuration(pathName);
                }
            }
        }
        return configuration;
    }

    private Configuration(String pathName) {
        File file = new File(pathName);
        if (file.exists()) {
            XmlUtil xmlUtil = new XmlUtil();
            Map<String, Object> config = xmlUtil.analysisConfig(file);
            this.driver = (String) config.get("driver");
            this.url = (String) config.get("url");
            this.username = (String) config.get("username");
            this.password = (String) config.get("password");
            this.openTheCatch = "true".equals(config.get("openTheCatch"));
            this.underlineAndHump = "true".equals(config.get("underlineAndHump"));
            this.mappedStatementMap = xmlUtil.getMappedStatements((List<String>) config.get("mappers"));
            if (openTheCatch) {
                if (config.get("catchSize") != null) {
                    if (Integer.parseInt((String) config.get("catchSize")) > 0) {
                        simpleCatch = new SimpleCatch(Integer.parseInt((String) config.get("catchSize")));
                    }
                } else {
                    simpleCatch = new SimpleCatch();
                }
            }
        } else {
            System.out.println("配置文件不存在");
        }
    }

    public String getDriver() {
        return driver;
    }

    public String getUrl() {
        return url;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    public boolean isUnderlineAndHump() {
        return underlineAndHump;
    }

    public Map<String, MappedStatement> getMappedStatementMap() {
        return mappedStatementMap;
    }

    public boolean isOpenTheCatch() {
        return openTheCatch;
    }

    public SimpleCatch getSimpleCatch() {
        return simpleCatch;
    }

    @Override
    public String toString() {
        return "Configuration{" +
                "driver='" + driver + '\'' +
                ", url='" + url + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", underlineAndHump=" + underlineAndHump +
                ", mappedStatementMap=" + mappedStatementMap +
                '}';
    }
}

 MappedStatement.java

id为对应接口的全限定名

resultType为返回类型

paramterType为sql的返回值类型

query为标记sql是否为查询语句

sql就是我们具体的业务了

package com.system;

/**
 * @author xuwei
 */
public class MappedStatement {
    /**
     * sql节点的id,对应接口方法名
     */
    private final String id;
    /**
     * sql的返回类型参数
     */
    private final String resultType;
    /**
     * sql的入参类型
     */
    private final String parameterType;
    /**
     * sql是否为查询
     */
    private final boolean query;
    /**
     * sql语句
     */
    private final String sql;

    public MappedStatement(String id, String resultType, String parameterType, boolean query, String sql) {
        this.id = id;
        this.resultType = resultType;
        this.parameterType = parameterType;
        this.query = query;
        this.sql = sql;
    }

    public String getId() {
        return id;
    }

    public String getResultType() {
        return resultType;
    }

    public String getParameterType() {
        return parameterType;
    }

    public boolean isQuery() {
        return query;
    }

    public String getSql() {
        return sql;
    }

    @Override
    public String toString() {
        return "MappedStatement{" +
                "id='" + id + '\'' +
                ", resultType='" + resultType + '\'' +
                ", parameterType='" + parameterType + '\'' +
                ", query=" + query +
                ", sql='" + sql + '\'' +
                '}';
    }
}

ExeCutor.java

执行器接口,我们只实现基础执行器,目前不考虑带缓存的高级执行器,里面暴露三个接口:查询多个,对应返回的参数为多个对象的list;查询单个,对应查询单个对象;更新接口,用于增删改

package com.system.executor;

import com.system.Configuration;
import com.system.MappedStatement;

import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

/**
 * @author xuwei
 */
public interface Executor {
    /**
     * 查询列表接口
     *
     * @param connection       connection
     * @param mappedStatement  mappedStatement
     * @param args             args
     * @param underlineAndHump underlineAndHump
     * @return 查询结果结合
     * @throws Exception 异常信息
     */
    List<Object> queryList(Connection connection, MappedStatement mappedStatement, Object[] args, Configuration configuration) throws Exception;

    /**
     * 查询单个接口
     *
     * @param connection       connection
     * @param mappedStatement  mappedStatement
     * @param args             args
     * @param underlineAndHump underlineAndHump
     * @return 查询结果
     * @throws SQLException              SQLException
     * @throws ClassNotFoundException    ClassNotFoundException
     * @throws InvocationTargetException InvocationTargetException
     * @throws NoSuchMethodException     NoSuchMethodException
     * @throws IllegalAccessException    IllegalAccessException
     * @throws InstantiationException    InstantiationException
     */
    Object queryOne(Connection connection, MappedStatement mappedStatement, Object[] args, Configuration configuration) throws SQLException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException;

    /**
     * 修改接口
     *
     * @param connection      connection
     * @param mappedStatement mappedStatement
     * @param args            args
     * @return 影响行数
     * @throws SQLException              SQLException
     * @throws ClassNotFoundException    ClassNotFoundException
     * @throws InvocationTargetException InvocationTargetException
     * @throws NoSuchMethodException     NoSuchMethodException
     * @throws IllegalAccessException    IllegalAccessException
     */
    int update(Connection connection, MappedStatement mappedStatement, Object[] args, Configuration configuration) throws SQLException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException;
}

 BaseExecutor.java

封装JDBC,对查询的全过程全权负责

package com.system.executor;

import com.system.Configuration;
import com.system.MappedStatement;
import com.system.SimpleCatch;
import com.system.utils.UnderlineAndHumpUtil;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author xuwei
 */
public class BaseExecutor implements Executor {

    @Override
    public List<Object> queryList(Connection connection, MappedStatement mappedStatement, Object[] args, Configuration configuration) throws ClassNotFoundException, SQLException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException {
        if (mappedStatement.getResultType() == null) {
            System.out.println("未定义resultType!");
            return null;
        }
        PreparedStatement preparedStatement = initPreparedStatement(connection, mappedStatement, args);
        if (configuration.isOpenTheCatch()){
            SimpleCatch simpleCatch = configuration.getSimpleCatch();
            if (simpleCatch.containsKey(preparedStatement.toString())){
                return resultToBean(mappedStatement.getResultType(),simpleCatch.get(preparedStatement.toString()),configuration.isUnderlineAndHump());
            }else {
                ResultSet resultSet = preparedStatement.executeQuery();
                simpleCatch.put(preparedStatement.toString(),resultSet);
                return resultToBean(mappedStatement.getResultType(), resultSet, configuration.isUnderlineAndHump());
            }
        }
        return resultToBean(mappedStatement.getResultType(), preparedStatement.executeQuery(), configuration.isUnderlineAndHump());
    }

    @Override
    public Object queryOne(Connection connection, MappedStatement mappedStatement, Object[] args, Configuration configuration) throws SQLException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException {
        if (mappedStatement.getResultType() == null) {
            System.out.println("未定义resultType!");
            return null;
        }
        PreparedStatement preparedStatement = initPreparedStatement(connection, mappedStatement, args);
        if (configuration.isOpenTheCatch()){
            SimpleCatch simpleCatch = configuration.getSimpleCatch();
            if (simpleCatch.containsKey(preparedStatement.toString())){
                List<Object> objectList = resultToBean(mappedStatement.getResultType(), simpleCatch.get(preparedStatement.toString()), configuration.isUnderlineAndHump());
                return objectList.size() == 0 ? null : objectList.get(0);
            }else {
                ResultSet resultSet = preparedStatement.executeQuery();
                simpleCatch.put(preparedStatement.toString(),resultSet);
                List<Object> objectList = resultToBean(mappedStatement.getResultType(), preparedStatement.executeQuery(), configuration.isUnderlineAndHump());
                return objectList.size() == 0 ? null : objectList.get(0);
            }
        }
        List<Object> objectList = resultToBean(mappedStatement.getResultType(), preparedStatement.executeQuery(), configuration.isUnderlineAndHump());
        return objectList.size() == 0 ? null : objectList.get(0);
    }

    @Override
    public int update(Connection connection, MappedStatement mappedStatement, Object[] args, Configuration configuration) throws SQLException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        if (configuration.isOpenTheCatch()){
            configuration.getSimpleCatch().clear();
        }
        return initPreparedStatement(connection, mappedStatement, args).executeUpdate();
    }

    public PreparedStatement initPreparedStatement(Connection connection, MappedStatement mappedStatement, Object[] args) throws SQLException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        String regex = "\\{([^}]*)}";
        Pattern pattern = Pattern.compile(regex);
        String sql = mappedStatement.getSql();
        Matcher matcher = pattern.matcher(sql);
        List<String> params = new ArrayList<>();
        PreparedStatement preparedStatement = null;
        //处理sql为预编译形式
        if (mappedStatement.getSql().contains("#{")) {
            if (mappedStatement.getParameterType() == null) {
                //如果采用预编译形式却未定义parameterType则报错
                System.out.println("未定义parameterType!");
                return null;
            }
            while (matcher.find()) {
                String param = matcher.group(1);
                params.add(param);
                sql = sql.replace("#{" + param + "}", "?");
            }
            preparedStatement = connection.prepareStatement(sql);
            switch (mappedStatement.getParameterType()) {
                case "int":
                case "java.lang.Integer":
                    int num = Integer.parseInt(args[0].toString());
                    preparedStatement.setInt(1, num);
                    break;
                case "java.lang.String":
                    String str = args[0].toString();
                    preparedStatement.setString(1, str);
                    break;
                default:
                    Class clazz = Class.forName(mappedStatement.getParameterType());
                    Object obj = args[0];
                    String name = "";
                    for (int i = 0; i < params.size(); i++) {
                        name = params.get(i).substring(0, 1).toUpperCase() + params.get(i).substring(1);
                        String methodName = "get" + name;
                        Method methodObj = clazz.getMethod(methodName);
                        //调用getter方法完成赋值
                        String value = methodObj.invoke(obj).toString();
                        preparedStatement.setString(i + 1, value);
                    }
                    break;
            }
        } else {
            preparedStatement = connection.prepareStatement(sql);
        }
        return preparedStatement;
    }

    public List<Object> resultToBean(String resultType, ResultSet resultSet, boolean underlineAndHump) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, SQLException, InstantiationException, ClassNotFoundException {
        Object obj = null;
        List<Object> objectList = new ArrayList<>();
        while (resultSet.next()) {
            //反射创建对象
            Class clazz = Class.forName(resultType);
            obj = clazz.newInstance();
            //获取ResultSet数据
            ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
            //遍历实体类属性集合,依次将结果集中的值赋给属性
            Field[] fields = clazz.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                Object value = setFieldValueByResultSet(fields[i], resultSetMetaData, resultSet, underlineAndHump);
                //通过属性名找到对应的setter方法
                String name = fields[i].getName();
                name = name.substring(0, 1).toUpperCase() + name.substring(1);
                String methodName = "set" + name;
                Method methodObj = clazz.getMethod(methodName, fields[i].getType());
                //调用setter方法完成赋值
                methodObj.invoke(obj, value);
            }
            objectList.add(obj);
        }
        return objectList;
    }


    public Object setFieldValueByResultSet(Field field, ResultSetMetaData rsmd, ResultSet rs, boolean underlineAndHump) {
        Object result = null;
        try {
            int count = rsmd.getColumnCount();
            for (int i = 1; i <= count; i++) {
                if (field.getName().equals(underlineAndHump ? UnderlineAndHumpUtil.underlineToHump(rsmd.getColumnName(i)) : rsmd.getColumnName(i))) {
                    String type = field.getType().getName();
                    switch (type) {
                        case "int":
                        case "java.lang.Integer":
                            result = rs.getInt(underlineAndHump ? UnderlineAndHumpUtil.humpToUnderline(field.getName()) : field.getName());
                            break;
                        case "java.lang.String":
                            result = rs.getString(underlineAndHump ? UnderlineAndHumpUtil.humpToUnderline(field.getName()) : field.getName());
                            break;
                        default:
                            break;
                    }
                }
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return result;
    }
}

 SimpleCatch.java

缓存使用lru算法实现,定义内部类存储key和使用次数,HashMap存储数据

package com.system;

import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Map;


/**
 * @ClassName SimpleCatch
 * @Author xuwei
 * @DATE 2022/4/9
 */
public class SimpleCatch {
    /**
     * 保存key和使用次数
     */
    static class KeyNum {
        private String key;
        private Integer num;

        public KeyNum() {

        }

        public KeyNum(String key, Integer num) {
            this.key = key;
            this.num = num;
        }

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public int getNum() {
            return num;
        }

        public void setNum(Integer num) {
            this.num = num;
        }
    }

    private final KeyNum[] keyNums;
    private final Map<String, ResultSet> simpleCatch;
    private Integer initialCapacity = 16;
    private Integer size = 0;

    public SimpleCatch() {
        this.keyNums = new KeyNum[initialCapacity];
        this.simpleCatch = new HashMap<>(initialCapacity);
    }

    public SimpleCatch(Integer catchSize) {
        this.initialCapacity = catchSize;
        this.keyNums = new KeyNum[initialCapacity];
        this.simpleCatch = new HashMap<>(initialCapacity);
    }

    /**
     * 判断是否包含key
     *
     * @param key
     * @return
     */
    public boolean containsKey(String key) {
        return simpleCatch.containsKey(key);
    }

    /**
     * 返回当前缓存中数据数量
     *
     * @return
     */
    public Integer size() {
        return this.size;
    }

    /**
     * 向缓存中添加数据
     * 先判断缓存是否满,如果未满,直接添加,如果满了的话则判断哪个使用次数最少,移除并覆盖
     *
     * @param key
     * @param resultSet
     */
    public void put(String key, ResultSet resultSet) {
        if (size < initialCapacity) {
            keyNums[size++] = new KeyNum(key, 0);
            simpleCatch.put(key, resultSet);
        } else {
            int index = 0;
            int minUsedNum = keyNums[0].num;
            for (int i = 0; i < size; ++i) {
                if (keyNums[i].num < minUsedNum) {
                    index = i;
                    minUsedNum = keyNums[i].num;
                }
            }
            simpleCatch.remove(keyNums[index].key);
            keyNums[index] = new KeyNum(key, 0);
            simpleCatch.put(key, resultSet);
        }
    }

    /**
     * 获取缓存中的数据
     *
     * @param key
     * @return
     */
    public ResultSet get(String key) {
        for (int i = 0; i < size; ++i) {
            if (key.equals(keyNums[i].key)) {
                keyNums[i].num++;
                return simpleCatch.get(key);
            }
        }
        return null;
    }

    /**
     * 清空缓存
     */
    public void clear() {
        for (int i = 0; i < initialCapacity; ++i) {
            keyNums[i] = null;
        }
        simpleCatch.clear();
        size = 0;
    }
}

UnderlineAndHumpUtil.java

package com.system.utils;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author xuwei
 */
public class UnderlineAndHumpUtil {
    private static final Pattern LINE_PATTERN = Pattern.compile("_(\\w)");
    private static final Pattern HUMP_PATTERN = Pattern.compile("[A-Z]");

    /**
     * 下划线转驼峰
     *
     * @param str str
     * @return str
     */
    public static String underlineToHump(String str) {
        str = str.toLowerCase();
        Matcher matcher = LINE_PATTERN.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * 驼峰转下划线
     *
     * @param str str
     * @return str
     */
    public static String humpToUnderline(String str) {
        Matcher matcher = HUMP_PATTERN.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
}

 XmlUtil.java

用来解析xml配置文件和xml业务代码

package com.system.utils;

import com.system.MappedStatement;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author xuwei
 */
public class XmlUtil {
    /**
     * 解析Configuration
     *
     * @param file 配置文件
     * @return 配置Map
     */
    public Map<String, Object> analysisConfig(File file) {
        Map<String, Object> config = new HashMap<>();
        SAXReader reader = new SAXReader();
        List<String> mappers = new ArrayList<>();
        Document document = null;
        try {
            document = reader.read(file);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        assert document != null;
        Element root = document.getRootElement();
        List<Element> childElements = root.elements();
        for (Element child : childElements) {
            if (!child.elements().isEmpty()) {
                for (Element c : child.elements()) {
                    if ("driver".equals(c.attributeValue("name"))) {
                        config.put("driver", c.attributeValue("value"));
                    }
                    if ("url".equals(c.attributeValue("name"))) {
                        config.put("url", c.attributeValue("value"));
                    }
                    if ("username".equals(c.attributeValue("name"))) {
                        config.put("username", c.attributeValue("value"));
                    }
                    if ("password".equals(c.attributeValue("name"))) {
                        config.put("password", c.attributeValue("value"));
                    }
                    if ("underlineAndHump".equals(c.attributeValue("name"))) {
                        config.put("underlineAndHump", c.attributeValue("value"));
                    }
                    if ("openTheCatch".equals(c.attributeValue("name"))) {
                        config.put("openTheCatch", c.attributeValue("value"));
                    }
                    if ("catchSize".equals(c.attributeValue("name"))) {
                        config.put("catchSize", c.attributeValue("value"));
                    }
                    if (!"".equals(c.attributeValue("resource")) && c.attributeValue("resource") != null) {
                        mappers.add(c.attributeValue("resource"));
                    }
                }
            }
        }
        config.put("mappers", mappers);
        return config;
    }

    /**
     * 获取mapper
     *
     * @param className 类名称
     * @param mappers   mappers
     * @return str
     */
    public String getMapperFile(String className, List<String> mappers) {
        for (String mapper : mappers) {
            SAXReader reader = new SAXReader();
            Document document = null;
            try {
                document = reader.read(new File(mapper));
            } catch (DocumentException e) {
                e.printStackTrace();
            }
            assert document != null;
            if (className.equals(document.getRootElement().attributeValue("namespace"))) {
                return mapper;
            }
        }
        return null;
    }

    /**
     * 获取mapper
     *
     * @param mappers mappers
     * @return map
     */
    public Map<String, MappedStatement> getMappedStatements(List<String> mappers) {
        Map<String, MappedStatement> mappedStatementMap = new HashMap<>();
        for (String mapper : mappers) {
            SAXReader reader = new SAXReader();
            Document document = null;
            try {
                document = reader.read(new File(mapper));
            } catch (DocumentException e) {
                e.printStackTrace();
            }
            assert document != null;
            Element root = document.getRootElement();
            List<Element> childElements = root.elements();
            for (Element child : childElements) {
                MappedStatement mappedStatement = new MappedStatement(child.attributeValue("id"),
                        child.attributeValue("resultType"),
                        child.attributeValue("parameterType"),
                        "select".equals(child.getName()),
                        child.getTextTrim());
                mappedStatementMap.put(root.attributeValue("namespace") + "." + child.attributeValue("id"), mappedStatement);
            }
        }
        return mappedStatementMap;
    }
}

xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <dataSource>
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/user?serverTimezone=GMT&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;allowMultiQueries=true&amp;useAffectedRows=true&amp;useSSL=false&amp;zeroDateTimeBehavior=convertToNull"/>
        <property name="username" value="root"/>
        <property name="password" value="Gyj1113.."/>
    </dataSource>
    <setting>
        <property name="underlineAndHump" value="true"/>
        <property name="openTheCatch" value="true"/>
        <property name="catchSize" value="20"/>
    </setting>
    <mappers>
        <mapper resource="src/main/resources/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

 到此我们的所有部分就已经结束了,接下来我们测试一下

首先定义实体

package com.entity;

/**
 * @author xuwei
 */
public class User {
    private Integer id;
    private String realName;
    private String sex;
    private String address;

    public String getRealName() {
        return realName;
    }

    public void setRealName(String name) {
        this.realName = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Integer getId() {
        return id;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", realName='" + realName + '\'' +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

方法接口

package com.mapper;

import com.entity.User;

import java.util.List;

/**
 * @author xuwei
 */
public interface UserMapper {
    /**
     * 查询所有
     *
     * @return List
     */
    List<User> findAll();

    /**
     * 查询单个
     *
     * @param id
     * @return User
     */
    User findById(Integer id);

    /**
     * 更新操作
     *
     * @param user
     * @return int
     */
    int updateUser(User user);

    /**
     * 添加操作
     *
     * @param user
     * @return int
     */
    int addUser(User user);

    /**
     * 删除操作
     *
     * @param id
     * @return int
     */
    int deleteUser(Integer id);
}

 mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<mapper namespace="com.mapper.UserMapper">
    <select id="findAll" resultType="com.entity.User">
        select * from user;
    </select>
    <select id="findById" parameterType="int" resultType="com.entity.User">
        select * from user where id = #{id};
    </select>
    <update id="updateUser" parameterType="com.entity.User">
        update user set real_name = #{realName} , sex = #{sex} , address = #{address} where id = #{id};
    </update>
    <insert id="addUser" parameterType="com.entity.User">
        insert into user (real_name,sex,address) values (#{realName},#{sex},#{address});
    </insert>
    <delete id="deleteUser" parameterType="int">
        delete from user where id = #{id};
    </delete>
    <select id="find">

    </select>
</mapper>

测试

import com.entity.User;
import com.mapper.UserMapper;
import com.system.XSqlSession;
import org.junit.Test;

public class XWTest {
    @Test
    public void test() {
        XSqlSession xSqlSession = new XSqlSession("src/main/resources/xuwei-mybatis.xml");
        UserMapper userMapper = (UserMapper) xSqlSession.getMapper(UserMapper.class);
        //先查询一下数据库
        System.out.println(userMapper.findAll());
        //创建几个对象,保存到数据库中
        User user1 = new User();
        User user2 = new User();
        User user3 = new User();
        user1.setRealName("一号");
        user1.setSex("男");
        user1.setAddress("河北");
        user2.setRealName("二号");
        user2.setSex("女");
        user2.setAddress("广东");
        user3.setRealName("三号");
        user3.setSex("保密");
        user3.setAddress("北京");
        userMapper.addUser(user1);
        userMapper.addUser(user2);
        userMapper.addUser(user3);
        //再查询一下
        System.out.println(userMapper.findAll());
        //查询一个具体的,然后修改
        User queryOne = userMapper.findById(1);
        System.out.println(queryOne);
        queryOne.setSex("保密");
        userMapper.updateUser(queryOne);
        //看看是否修改成功了
        System.out.println(userMapper.findById(1));
        //删除一个
        userMapper.deleteUser(1);
        //查询
        System.out.println(userMapper.findAll());
    }
}

 测试结果

源码:

qihua99/xuwei-mybatis (github.com)https://github.com/qihua99/xuwei-mybatis

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeXu_cyber

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值