手写MyBatis框架

1.执行流程

MyBatis 的执行流程:

  • 加载配置文件:MyBatis 首先会加载配置文件(例如 mybatis-config.xml),其中包含了数据库连接信息、映射文件的位置以及其他配置信息。
  • 创建 SqlSessionFactory:MyBatis 根据加载的配置文件创建一个 SqlSessionFactory 对象,SqlSessionFactory 是 MyBatis 的核心接口之一,它负责创建 SqlSession 对象。
  • 创建 SqlSession:应用程序通过 SqlSessionFactory 创建 SqlSession 对象。SqlSession 提供了执行 SQL 命令所需的所有方法。
  • 定义 Executor(执行器):MyBatis 底层定义了一个 Executor 来操作数据库。它根据 SqlSession 传递的参数,动态地生成需要执行的 SQL 语句,并负责查询缓存的维护。
  • 创建 MappedStatement:在 Executor 的执行方法中,包含一个 MappedStatement 的参数。该参数是对映射信息的封装,用于存储要映射的 SQL 语句的参数等。Mapper.xml 中的一个 SQL 语句对应一个 MappedStatement,SQL 语句的 id 属性即为 MappedStatement 的 id。
  • Executor 通过 MappedStatement 在执行 SQL 语句前,将输入的 Java 对象映射到 SQL 语句中。或将输出结果映射到 Java 对象中。

如图:

回忆一下mybatis的用法

package com.dreams;

import com.dreams.mapper.BookMapper;
import com.dreams.pojo.Book;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * @author PoemsAndDreams
 * @description 简单使用mybatis
 */
public class Main {
    public static void main(String[] args) {
        InputStream is = null;
        SqlSession sqlSession = null;

        try {
            is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
            sqlSession = sqlSessionFactory.openSession();

            BookMapper mapper = sqlSession.getMapper(BookMapper.class);
            Book book = new Book();
            book.setBookId("5");
            // 调用方法进行查询
            List<Book> books = mapper.selectByBook(book);

            // 输出查询结果
            for (Book b : books) {
                System.out.println(b);
            }
        } catch (IOException e) {
            throw new RuntimeException("Error loading mybatis-config.xml", e);
        } finally {
            // 关闭 SqlSession 和 InputStream
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

通过加载 MyBatis 的配置文件 mybatis-config.xml创建SqlSessionFactory 对象,然后使用 SqlSessionFactory 创建 SqlSession 对象。最后使用 SqlSession 执行 SQL 查询。

所以第一步就是创建相关的类。

新建一个maven项目,叫mybatis

将代码复制过来

package com.dreams;
import java.io.InputStream;

/**
 * @author PoemsAndDreams
 * @description //测试
 */
public class Main {
    public static void main(String[] args) {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession();
    }
}

这些就是需要我们实现的类

 

 

2.返回字节流

我们解决第一个爆红处

创建io/Resources类,添加静态方法getResourceAsStream,我们使用使用当前类的 ClassLoader 来加载资源文件,加载位于类路径下的资源文件,并返回对应的字节流。如果资源文件不存在,则会抛出 IOException。

代码如下:

package com.dreams.io;

import java.io.IOException;
import java.io.InputStream;

/**
 * @author PoemsAndDreams
 * @description Resources类
 */
public class Resources {

    /**
     * 将配置文件加载为字节流
     * @param resource
     * @return
     * @throws IOException
     */
    public static InputStream getResourceAsStream(String resource) throws IOException {
        // 使用当前类的 ClassLoader 来加载资源文件
        ClassLoader classLoader = Resources.class.getClassLoader();
        InputStream inputStream = classLoader.getResourceAsStream(resource);

        if (inputStream == null) {
            // 如果资源文件不存在,则抛出 IOException
            throw new IOException("Resource not found: " + resource);
        }

        return inputStream;
    }
}

 

3.SqlSessionFactory

接下来就是SqlSessionFactory ,它负责创建 SqlSession 对象。

创建/session/SqlSessionFactory接口

package com.dreams.session;

/**
 * @author PoemsAndDreams
 * @description
 */
public interface SqlSessionFactory {
}

 

再创建/session/SqlSessionFactoryBuilder类

package com.dreams.session;

import java.io.InputStream;

/**
 * @author PoemsAndDreams
 * @description
 */
public class SqlSessionFactoryBuilder {

    public SqlSessionFactory build(InputStream is) {
        return null;
    }
}

这个方法里应该调用XMLConfigBuilder,解析mybatis-config.xml和mapper.xml,但是这里先暂时返回null,后面再完善。

 

通过调用build方法返回SqlSessionFactory

现在就只剩下SqlSession对象了

 

4.读取xml

要解析mybatis-config.xml和mapper.xml,就是要读取xml文件。

回顾mybatis和spring的配置文件

可以看到上面的链接

这些就是格式校验链接,引入可以对xml格式校验

我们自己手写一个mybatis框架就不加入校验了

不过最上面的是指定版本,和规范,最好加上

在创建resources/mappers/UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.dreams.mappers.UserMapper">
    <!--int insertUser();-->
    <insert id="insertUser">
        insert into user values(1,'admin','123456')
    </insert>
</mapper>

这就是mybatis的xml文件

mybatis框架就是对其的读取

同时创建resources/mybatis-config.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <!--设置连接数据库的环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!--引入映射文件-->
    <mappers>
        <mapper resource="mappers/UserMapper.xml"/>
        <!-- <package name="com.dreams.mappers"/>-->
    </mappers>
</configuration>

 

如果我们需要对其读取,可以借助工具类

https://dom4j.github.io/

只需要导入依赖:

<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.3</version>
</dependency>

使用方法可以参考我的博客

读取xml文件

 

 

5.Mapper文件映射信息封装对象

MappedStatement是对映射信息的封装,用于存储要映射的 SQL 语句的参数等。Mapper.xml 中的一个 SQL 语句对应一个 MappedStatement,SQL 语句的 id 属性即为 MappedStatement 的 id。

为了方便,我们使用lombok,加入依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.32</version>
</dependency>

创建com/dreams/mapping/MappedStatement类

package com.dreams.mapping;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author PoemsAndDreams
 * @description 映射信息的封装类
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MappedStatement {

    //id唯一标识
    private String id;

    //返回类型
    private String resultType;


    //传入类型
    private String parameterType;

    //sql语句
    private String sql;
}

Mapper.xml 中的一个 SQL 语句对应一个 MappedStatement,而映射后的MappedStatement对象,存储在Configuration对象中

 

6.Configuration对象

MyBatis的Configuration对象是MyBatis框架中的核心对象之一,负责管理MyBatis的所有配置信息。它包含了对数据库连接、映射文件、缓存、参数映射、插件等各方面的配置。这里我们只需要实现数据库连接、映射文件即可。

每个Mapper.xml 会存在多条sql,所以在Configuration中我们使用map存储。

Configuration对象的主要作用不仅包括初始化全局配置加载和解析映射文件(Mapper文件)并将其注册到MyBatis中,同时还要加载和解析mybatis-config.xml配置文件,加载数据源信息

如下存储在mappedStatements中,key为(命名空间.id)(如com.dreams.mappers.UserMapper.insertUser)

代码如下:

package com.dreams.session;

import com.dreams.mapping.MappedStatement;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.sql.DataSource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author PoemsAndDreams
 * @description SQL配置封装对象
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Configuration {

    //读取mybatis-config.xml,存储数据源信息
    private DataSource dataSource;

    //Mapper.xml 中的一个 SQL 语句对应一个 MappedStatement,这里使用Map存储
    Map<String, MappedStatement> mappedStatements = new ConcurrentHashMap<>();
}

参考mybatis源码

 

 

7.读取mapper.xml文件

XMLMapperBuilder类通常用于构建和配置用于处理XML的ObjectMapper实例,用于读取mapper.xml文件。

创建com/dreams/xml/XMLMapperBuilder类

package com.dreams.xml;

import com.dreams.mapping.MappedStatement;
import com.dreams.session.Configuration;
import org.dom4j.*;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.List;

/**
 * @author PoemsAndDreams
 * @description 解析mapper.xml
 */
public class XMLMapperBuilder {

    private InputStream resource;

    private Configuration configuration;

    public XMLMapperBuilder(InputStream resource, Configuration configuration) {
        this.resource = resource;
        this.configuration = configuration;
        parse();
    }

    public void parse() {
        //创建一个解析器
        SAXReader saxReader = new SAXReader();
        //读取xml文件
        Document document = null;
        try {
            document = saxReader.read(resource);
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }

        //获取根节点
        Element rootElement = document.getRootElement();

        // mapper.xml
        Attribute namespace = rootElement.attribute("namespace");

        //获取根节点下的子节点
        List<Element> elements = rootElement.elements();
        for (Element element : elements) {
            //获取id
            String id = element.attributeValue("id");
            //获取返回值resultType
            String resultType = element.attributeValue("resultType");

            String parameterType = element.attributeValue("parameterType");

            //去除空格
            String sql = element.getTextTrim();


            //封装MappedStatement对象
            MappedStatement mappedStatement = new MappedStatement();
            mappedStatement.setId(id);
            mappedStatement.setResultType(resultType);
            mappedStatement.setParameterType(parameterType);
            mappedStatement.setSql(sql);
            String value = namespace.getValue();
            String key = value+ "." + id;

            configuration.getMappedStatements().put(key,mappedStatement);
        }


    }

}

 

8.读取mybatis-config.xml

mybatis-config.xml是MyBatis框架中的配置文件,用于配置MyBatis的全局设置和属性。我们这里主要读取数据库连接信息,所以先把数据库连接依赖加入,我们使用druid连接池。

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.16</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.20</version>
</dependency>

同样创建com/dreams/xml/XMLConfigBuilder文件

package com.dreams.xml;

import com.alibaba.druid.pool.DruidDataSource;
import com.dreams.io.Resources;
import com.dreams.session.Configuration;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * @author PoemsAndDreams
 * @description 解析mybatis-config.xml
 */
public class XMLConfigBuilder {
    private Configuration configuration = new Configuration();

    //接收字节流处理,赋值给configuration对象
    public XMLConfigBuilder(InputStream is) {
        //创建一个解析器
        SAXReader saxReader = new SAXReader();
        //读取xml文件
        Document document = null;
        try {
            document = saxReader.read(is);
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }

        //获取根节点configuration
        Element rootElement = document.getRootElement();
        Element environments = rootElement.element("environments");
        Element environment = environments.element("environment");
        Element dataSource = environment.element("dataSource");
        List<Element> elements = dataSource.elements();

        DruidDataSource druidDataSource = new DruidDataSource();

        for (Element element : elements) {
            String name = element.attributeValue("name");
            String value = element.attributeValue("value");
            if (name.equals("driver")){
                druidDataSource.setDriverClassName(value);
            }else if (name.equals("url")){
                druidDataSource.setUrl(value);
            }else if (name.equals("username")){
                druidDataSource.setUsername(value);
            }else if (name.equals("password")){
                druidDataSource.setPassword(value);
            }
        }

        configuration.setDataSource(druidDataSource);
        

        //处理每一个mapper.xml文件
        Element mappers = rootElement.element("mappers");
        List<Element> mapper = mappers.elements("mapper");
        for (Element element : mapper) {
            String value = element.attributeValue("resource");
            try {
                InputStream inputStream = Resources.getResourceAsStream(value);
                XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(inputStream, configuration);

            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public Configuration parse() {
        return configuration;
    }
}

从 XML 文件中提取配置信息,包括数据源配置和映射器配置。创建 DruidDataSource 对象,并根据 XML 中的配置信息设置数据源的相关属性。将配置的数据源设置到 Configuration 对象中。处理每个映射器文件(mapper.xml),创建 XMLMapperBuilder 对象,并将映射器配置加载到 Configuration 对象中。

 

9.SqlSessionFactory对象

接着我们完善上面的

package com.dreams.session;

import com.dreams.xml.XMLConfigBuilder;
import java.io.InputStream;

/**
 * @author PoemsAndDreams
 * @description
 */
public class SqlSessionFactoryBuilder {

    public SqlSessionFactory build(InputStream is) {
        Configuration configuration = null;
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(is);
            configuration = parser.parse();
        } catch (Exception e) {
            throw new RuntimeException("Error building SqlSession : ", e);
        }

        return this.build(configuration);
    }

    public SqlSessionFactory build(Configuration configuration) {
        return null;
    }

}

这里我们最终需要返回一个SqlSessionFactory工厂类,所以我们再调用一个build方法,完成返回SqlSessionFactory类的逻辑,这里先返回null。

回忆mybatis配置时openSession方法(为true时开启事务,这里不实现事务,但是依旧使用该方法),它返回一个sqlSession对象。

仿照我们添加openSession方法

在com/dreams/session/SqlSessionFactory中添加

package com.dreams.session;

/**
 * @author PoemsAndDreams
 * @description SqlSessionFactory工厂类
 */
public interface SqlSessionFactory {
    SqlSession openSession(boolean bool);
    SqlSession openSession();
}

返回一个接口

package com.dreams.session;

/**
 * @author PoemsAndDreams
 * @description SqlSession接口
 */
public interface SqlSession {

}

这个接口里仿照源码,这里提供一些方法

package com.dreams.session;


import java.util.List;

/**
 * @author PoemsAndDreams
 * @description SqlSession接口
 */
public interface SqlSession {
    /**
     * 搜索
     * @param statement 唯一标识
     * @param parameter 参数
     * @return
     * @param <T>
     */
    <T> T selectOne(String statement, Object ...parameter);
    
    <E> List<E> selectList(String statement, Object ...parameter);
    
    int insert(String statement, Object ...parameter);

    int update(String statement, Object ...parameter);

    int delete(String statement, Object ...parameter);

    <T> T getMapper(Class<T> mapperClass);

}

 

创建一个DefaultSqlSessionFactory实现SqlSessionFactory

package com.dreams.session.defaults;

import com.dreams.session.Configuration;
import com.dreams.session.SqlSession;
import com.dreams.session.SqlSessionFactory;

/**
 * @author PoemsAndDreams
 * @date 2024-06-09 02:02
 * @description //TODO
 */
public class DefaultSqlSessionFactory implements SqlSessionFactory {
    private Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSession(boolean bool) {
        return this.openSession();
    }

    @Override
    public SqlSession openSession() {
        return new DefaultSqlSession(configuration);
    }
}

这里openSession()返回sqlSession的默认实现DefaultSqlSession

DefaultSqlSession里面会用到Executor执行器来执行操作,所以我们先完成Executor执行器的代码编写。

 

10.Executor执行器

Executor是一个执行器,它负责管理SQL语句的执行过程。

定义接口

update方法用来处理增加,更新,删除

query处理搜索相关的操作

package com.dreams.executor;

import com.dreams.mapping.MappedStatement;

import java.sql.SQLException;
import java.util.List;

/**
 * @author PoemsAndDreams
 * @description
 */
public interface Executor {

    int update(MappedStatement ms, Object ...parameter) throws SQLException;

    <E> List<E> query(MappedStatement ms, Object ...parameter) throws SQLException;

}

 

具体的逻辑如下:

package com.dreams.executor;

import com.dreams.mapping.MappedStatement;
import com.dreams.session.Configuration;

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

/**
 * @author PoemsAndDreams
 * @description
 */
public class SimpleExecutor implements Executor {

    private Configuration configuration;

    public SimpleExecutor(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public int update(MappedStatement mappedStatement, Object... parameter) throws SQLException {
        //获取数据库连接
        Connection connection = configuration.getDataSource().getConnection();
        String sql = mappedStatement.getSql();
        String parameterType = mappedStatement.getParameterType();
        String boundSql = replacePlaceholders(sql, parameter);
        PreparedStatement prepareStatement = connection.prepareStatement(boundSql);

        int result = prepareStatement.executeUpdate();
        return result;
    }

    @Override
    public <E> List<E> query(MappedStatement mappedStatement, Object... parameter) throws SQLException {
        //获取数据库连接
        Connection connection = configuration.getDataSource().getConnection();
        //获取sql
        String sql = mappedStatement.getSql();

        //替换成执行sql
        String boundSql = replacePlaceholders(sql, parameter);
        //sql执行
        PreparedStatement prepareStatement = connection.prepareStatement(boundSql);
        ResultSet resultSet = prepareStatement.executeQuery();


        //封装结果集
        String resultType = mappedStatement.getResultType();
        Class<?> resultClass = null;
        try {
            resultClass = Class.forName(resultType);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }

        ArrayList<Object> resultList = new ArrayList<>();
        while (resultSet.next()) {
            try {
                // 创建 resultType 对应类的实例
                Object instance = resultClass.newInstance();

                ResultSetMetaData metaData = resultSet.getMetaData();

                int columnCount = metaData.getColumnCount();

                for (int i = 1; i <= columnCount; i++) {
                    String columnName = metaData.getColumnName(i);
                    Object columnValue = resultSet.getObject(i);

                    // 使用反射设置实例的属性
                    Field field = resultClass.getDeclaredField(columnName); // 假设属性名与列名相同
                    field.setAccessible(true);
                    field.set(instance, columnValue);
                }
                // 将实例添加到 resultList 中
                resultList.add(instance);
            } catch (Exception e) {
                // 处理异常
                e.printStackTrace();
            }
        }

        return (List<E>) resultList;
    }


    private static String replacePlaceholders(String sql, Object... parameters) {
        // 如果参数为空,则返回原始的SQL语句
        if (parameters == null) {
            return sql;
        }

        StringBuffer newSql = new StringBuffer(sql);
        StringBuffer sb = null;

        // 遍历SQL语句中的所有占位符
        // 这里假设占位符的格式为#{0}、#{1}、#{2}等
        for (int i = 0; i < parameters.length; i++) {
            Object parameter = parameters[i];

            // 获取参数的类
            Class<?> parameterClass = parameter.getClass();

            // 遍历SQL语句中的所有占位符
            // 这里假设占位符的格式为#{propertyName},例如#{id}、#{name}
            Pattern pattern = Pattern.compile("#\\{([^{}]*)\\}");
            Matcher matcher = pattern.matcher(newSql);
            sb = new StringBuffer();
            while (matcher.find()) {
                String placeholder = matcher.group(1);

                // 获取参数对象中与占位符对应的属性值
                try {
                    // 使用反射获取属性值
                    if (parameterClass.isPrimitive() ||parameterClass.getName().startsWith("java.lang")){
                        // 替换占位符为属性值
                        matcher.appendReplacement(sb, Matcher.quoteReplacement(parameter.toString()));
                        continue;
                    }

                    Field field = parameterClass.getDeclaredField(placeholder);
                    field.setAccessible(true);
                    Object value = field.get(parameter);

                    // 如果参数类型为字符串,加上双引号
                    String replacement = value != null ? (field.getType().equals(String.class) ? "'" + value + "'" : value.toString()) : "null";

                    // 替换占位符为属性值
                    matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement));
                } catch (NoSuchFieldException | IllegalAccessException e) {
                    // 如果找不到属性或者无法访问属性,可以选择忽略或者抛出异常
                    // 这里简单地将占位符替换为null
//                    matcher.appendReplacement(sb, "null");
                    if (i == parameters.length - 1) {
                        matcher.appendReplacement(sb, "null");
                    }
                }
            }
            matcher.appendTail(sb);
            newSql = new StringBuffer(sb);

        }

        return newSql.toString();
    }

}

replacePlaceholders函数就是将sql替换成可执行sql

例如:update user set password = #{password} where id = #{id}中的值使用反射将传入的对象赋值,这样出来的sql就是update user set password = ‘123456’ where id = ‘4’

出来的可执行sql只要放到update或query执行即可

 

11.DefaultSqlSession逻辑

完善后的DefaultSqlSession如下:

package com.dreams.session.defaults;

import com.dreams.executor.SimpleExecutor;
import com.dreams.mapping.MappedStatement;
import com.dreams.session.Configuration;
import com.dreams.session.SqlSession;

import java.lang.reflect.*;
import java.sql.SQLException;
import java.util.List;

/**
 * @author PoemsAndDreams
 * @description
 */
public class DefaultSqlSession implements SqlSession {
    private Configuration configuration;

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public <T> T selectOne(String statement, Object... parameter) {
        List<Object> query = this.selectList(statement, parameter);
        if (query.isEmpty()){
            return null;
        }
        return (T)query.get(0);
    }

    @Override
    public <E> List<E> selectList(String statement, Object... parameter) {
        SimpleExecutor executor = new SimpleExecutor(configuration);
        MappedStatement mappedStatement = this.configuration.getMappedStatements().get(statement);
        try {
            List<Object> query = executor.query(mappedStatement, parameter);
            return (List<E>) query;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int insert(String statement, Object... parameter) {
        SimpleExecutor executor = new SimpleExecutor(configuration);
        MappedStatement mappedStatement = this.configuration.getMappedStatements().get(statement);
        try {
            int insert = executor.update(mappedStatement, parameter);
            return insert;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    @Override
    public int update(String statement, Object... parameter) {
        SimpleExecutor executor = new SimpleExecutor(configuration);
        MappedStatement mappedStatement = this.configuration.getMappedStatements().get(statement);
        try {
            int update = executor.update(mappedStatement, parameter);
            return update;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int delete(String statement, Object... parameter) {
        SimpleExecutor executor = new SimpleExecutor(configuration);
        MappedStatement mappedStatement = this.configuration.getMappedStatements().get(statement);
        try {
            int delete = executor.update(mappedStatement, parameter);
            return delete;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }


    @Override
    public <T> T getMapper(Class<T> mapperClass) {
        Object proxyInstance = Proxy.newProxyInstance(mapperClass.getClassLoader(), new Class<?>[]{mapperClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 获取方法名
                String methodName = method.getName();
                // 获取接口的全类名
                String className = method.getDeclaringClass().getName();
                // 拼接SQL的唯一标识
                String statement = className + "." + methodName;
                // 获取方法被调用之后的返回值类型
                Type returnType = method.getGenericReturnType();

                MappedStatement mappedStatement = configuration.getMappedStatements().get(statement);

                String sql = mappedStatement.getSql();

                // 匹配对应方法
                if (sql.contains("select")) {
                    if (returnType instanceof ParameterizedType){
                        return selectList(statement, args);
                    }
                    return selectOne(statement,args);
                } else if (sql.contains("insert")) {
                    return insert(statement, args);
                } else if (sql.contains("update")) {
                    return update(statement, args);
                } else if (sql.contains("delete")) {
                    return delete(statement, args);
                } else {
                    throw new UnsupportedOperationException("方法不存在: " + methodName);
                }
            }
        });
        return (T) proxyInstance;
    }
}

getMapper方法会去根据方法名和sql类型调用对应执行方法。而执行方法只要调用executor方法执行对应sql语句即可

 

12.测试代码

最后,我们在使用手写的Mybatis框架来测试一下。

userMapper代码

package com.dreams.mappers;

import com.dreams.pojo.User;
import java.util.List;

/**
 * @author PoemsAndDreams
 * @description
 */
public interface UserMapper {
    List<User> list();

    List<User> select(User user);

    int update(User user);

    int deleteById(Integer id);

    int insertUser(User user);

}

 

userMapper.xml代码:

<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.dreams.mappers.UserMapper">

    <select id="list" resultType="com.dreams.pojo.User">
        select * from user
    </select>

    <select id="select" resultType="com.dreams.pojo.User">
        select * from user where username= #{username}
    </select>

    <!--int insertUser();-->
    <insert id="insertUser" parameterType="com.dreams.pojo.User">
        insert into user(id, username, password) values(#{id},#{username},#{password})
    </insert>

    <delete id="deleteById" parameterType="java.lang.Integet">
        delete from user where id = #{id}
    </delete>

    <update id="update" parameterType="com.dreams.pojo.User">
        update user set password = #{password} where id = #{id}
    </update>


</mapper>

 

user类

package com.dreams.pojo;

import lombok.Data;

/**
 * @author PoemsAndDreams
 * @description
 */
@Data
public class User {
    private String id;
    private String username;
    private String password;
}

 

最后测试一下即可

package com.dreams;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import com.dreams.io.Resources;
import com.dreams.mappers.UserMapper;
import com.dreams.pojo.User;
import com.dreams.session.SqlSession;
import com.dreams.session.SqlSessionFactory;
import com.dreams.session.SqlSessionFactoryBuilder;

/**
 * @author PoemsAndDreams
 * @description //测试
 */
public class Main {
    public static void main(String[] args) {
        InputStream is = null;
        try {
            is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
            System.out.println(sqlSessionFactory);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List<User> list = mapper.list();
            for (User user : list) {
                System.out.println(user);
            }

            User user = new User();
            user.setId("41");
            user.setUsername("dreams");
            user.setPassword("123456");
            int i = mapper.insertUser(user);
            System.out.println("新增成功!");

            List<User> select = mapper.select(user);
            for (User u : select) {
                System.out.println(u);
            }


            int de = mapper.deleteById(41);
            System.out.println(de);

            User user1 = new User();
            user1.setId("4");
            user1.setUsername("dreams1");
            user1.setPassword("1234561");
            int update = mapper.update(user1);
            System.out.println(update);


        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

可以看到都成功了

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇