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&useUnicode=true&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>
如果我们需要对其读取,可以借助工具类
只需要导入依赖:
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>使用方法可以参考我的博客
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);
}
}
}可以看到都成功了



