Easy-mybatis version 0.0.1
-
配置加载(SqlSessionFactoryBuilder + Resources) 业务代码调用
SqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml")):-
Resources加载类路径下的mybatis-config.xml,返回输入流; -
建造者解析 XML,初始化
EasyMybatisUNPOOLEDDataSource(数据源); -
基于数据源创建
EasyMybatisJDBCTransaction(事务管理器); -
解析 Mapper XML(如
UserMapper.xml),生成EasyMybatisMappedStatement并放入 Map; -
组装
SqlSessionFactory(包含事务管理器 + SQL 映射 Map)。
-
-
获取操作入口(SqlSessionFactory) 调用
SqlSessionFactory.openSession(),创建DefaultSqlSession实例(注入事务管理器和 SQL 映射 Map)。 -
执行 SQL(DefaultSqlSession) 调用
defaultSqlSession.selectOne("com.xxx.UserMapper.selectById", 1):-
通过 SQL id 从
mappedStatements中获取对应的EasyMybatisMappedStatement; -
从
TransactionManager中获取Connection; -
预编译 SQL(
PreparedStatement ps = conn.prepareStatement(ms.getSql())); -
设置参数(
ps.setInt(1, 1)); -
执行 SQL(
ResultSet rs = ps.executeQuery()); -
将
ResultSet封装为User对象并返回。
-
-
事务控制(TransactionManager)
-
若操作成功:调用
defaultSqlSession.commit()→ 代理调用TransactionManager.commit(); -
若操作失败:调用
defaultSqlSession.rollback()→ 代理调用TransactionManager.rollback(); -
最终调用
close()关闭连接,释放资源。
-

源码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.rongx.core;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
public class EasyMybatisJDBCTransaction implements TransactionManager {
private Connection conn;
private DataSource dataSource;
private boolean autoCommit;
public EasyMybatisJDBCTransaction(DataSource dataSource, boolean autoCommit) {
this.dataSource = dataSource;
this.autoCommit = autoCommit;
}
public void commit() {
try {
this.conn.commit();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void rollback() {
try {
this.conn.rollback();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void close() {
try {
this.conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void openConnection() {
try {
this.conn = this.dataSource.getConnection();
this.conn.setAutoCommit(this.autoCommit);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public Connection getConnection() {
return this.conn;
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.rongx.core;
public class EasyMybatisMappedStatement {
private String sqlId;
private String resultType;
private String sql;
private String parameterType;
private String sqlType;
public String toString() {
return "GodMappedStatement{sqlId='" + this.sqlId + "', resultType='" + this.resultType + "', sql='" + this.sql + "', parameterType='" + this.parameterType + "', sqlType='" + this.sqlType + "'}";
}
public String getSqlId() {
return this.sqlId;
}
public void setSqlId(String sqlId) {
this.sqlId = sqlId;
}
public String getResultType() {
return this.resultType;
}
public void setResultType(String resultType) {
this.resultType = resultType;
}
public String getSql() {
return this.sql;
}
public void setSql(String sql) {
this.sql = sql;
}
public String getParameterType() {
return this.parameterType;
}
public void setParameterType(String parameterType) {
this.parameterType = parameterType;
}
public String getSqlType() {
return this.sqlType;
}
public void setSqlType(String sqlType) {
this.sqlType = sqlType;
}
public EasyMybatisMappedStatement(String sqlId, String resultType, String sql, String parameterType, String sqlType) {
this.sqlId = sqlId;
this.resultType = resultType;
this.sql = sql;
this.parameterType = parameterType;
this.sqlType = sqlType;
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.rongx.core;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
import javax.sql.DataSource;
public class EasyMybatisUNPOOLEDDataSource implements DataSource {
private String url;
private String username;
private String password;
public EasyMybatisUNPOOLEDDataSource(String driver, String url, String username, String password) {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
this.url = url;
this.username = username;
this.password = password;
}
public Connection getConnection() throws SQLException {
return DriverManager.getConnection(this.url, this.username, this.password);
}
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
public PrintWriter getLogWriter() throws SQLException {
return null;
}
public void setLogWriter(PrintWriter out) throws SQLException {
}
public void setLoginTimeout(int seconds) throws SQLException {
}
public int getLoginTimeout() throws SQLException {
return 0;
}
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.rongx.core;
import java.io.InputStream;
public class Resources {
public static InputStream getResourcesAsStream(String config) {
return Thread.currentThread().getContextClassLoader().getResourceAsStream(config);
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.rongx.core;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SqlSession {
private TransactionManager transactionManager;
private Map<String, EasyMybatisMappedStatement> mappedStatements;
public SqlSession(TransactionManager transactionManager, Map<String, EasyMybatisMappedStatement> mappedStatements) {
this.transactionManager = transactionManager;
this.mappedStatements = mappedStatements;
}
public void commit() {
try {
this.transactionManager.getConnection().commit();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void rollback() {
try {
this.transactionManager.getConnection().rollback();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void close() {
try {
this.transactionManager.getConnection().close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public int insert(String sqlId, Object obj) {
EasyMybatisMappedStatement easyMybatisMappedStatement = (EasyMybatisMappedStatement)this.mappedStatements.get(sqlId);
Connection connection = this.transactionManager.getConnection();
String easyMybatisbatisSql = easyMybatisMappedStatement.getSql();
String sql = easyMybatisbatisSql.replaceAll("#\\{[a-zA-Z0-9_\\$]*}", "?");
Map<Integer, String> map = new HashMap();
int endIndex;
for(int index = 1; easyMybatisbatisSql.indexOf("#") >= 0; easyMybatisbatisSql = easyMybatisbatisSql.substring(endIndex + 1)) {
int beginIndex = easyMybatisbatisSql.indexOf("#") + 2;
endIndex = easyMybatisbatisSql.indexOf("}");
map.put(index++, easyMybatisbatisSql.substring(beginIndex, endIndex).trim());
}
try {
PreparedStatement ps = connection.prepareStatement(sql);
map.forEach((k, v) -> {
try {
char var10000 = v.toUpperCase().charAt(0);
String getMethodName = "get" + var10000 + v.substring(1);
Method getMethod = obj.getClass().getDeclaredMethod(getMethodName);
ps.setString(k, getMethod.invoke(obj).toString());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
endIndex = ps.executeUpdate();
ps.close();
return endIndex;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public int update(String sqlId, Object obj) {
EasyMybatisMappedStatement easyMybatisMappedStatement = (EasyMybatisMappedStatement)this.mappedStatements.get(sqlId);
Connection connection = this.transactionManager.getConnection();
String easyMybatisbatisSql = easyMybatisMappedStatement.getSql();
String sql = easyMybatisbatisSql.replaceAll("#\\{[a-zA-Z0-9_\\$]*}", "?");
Map<Integer, String> map = new HashMap();
int endIndex;
for(int index = 1; easyMybatisbatisSql.indexOf("#") >= 0; easyMybatisbatisSql = easyMybatisbatisSql.substring(endIndex + 1)) {
int beginIndex = easyMybatisbatisSql.indexOf("#") + 2;
endIndex = easyMybatisbatisSql.indexOf("}");
map.put(index++, easyMybatisbatisSql.substring(beginIndex, endIndex).trim());
}
try {
PreparedStatement ps = connection.prepareStatement(sql);
map.forEach((k, v) -> {
try {
char var10000 = v.toUpperCase().charAt(0);
String getMethodName = "get" + var10000 + v.substring(1);
Method getMethod = obj.getClass().getDeclaredMethod(getMethodName);
ps.setString(k, getMethod.invoke(obj).toString());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
endIndex = ps.executeUpdate();
ps.close();
return endIndex;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public int delete(String sqlId, Object parameterObj) {
EasyMybatisMappedStatement easyMybatisMappedStatement = (EasyMybatisMappedStatement)this.mappedStatements.get(sqlId);
Connection connection = this.transactionManager.getConnection();
String easyMybatisbatisSql = easyMybatisMappedStatement.getSql();
String sql = easyMybatisbatisSql.replaceAll("#\\{[a-zA-Z0-9_\\$]*}", "?");
Map<Integer, String> map = new HashMap();
int endIndex;
for(int index = 1; easyMybatisbatisSql.indexOf("#") >= 0; easyMybatisbatisSql = easyMybatisbatisSql.substring(endIndex + 1)) {
int beginIndex = easyMybatisbatisSql.indexOf("#") + 2;
endIndex = easyMybatisbatisSql.indexOf("}");
map.put(index++, easyMybatisbatisSql.substring(beginIndex, endIndex).trim());
}
try {
PreparedStatement ps = connection.prepareStatement(sql);
if (map.size() == 1 && parameterObj != null) {
ps.setString(1, parameterObj.toString());
} else if (parameterObj != null) {
map.forEach((k, v) -> {
try {
char var10000 = v.toUpperCase().charAt(0);
String getMethodName = "get" + var10000 + v.substring(1);
Method getMethod = parameterObj.getClass().getDeclaredMethod(getMethodName);
ps.setString(k, getMethod.invoke(parameterObj).toString());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
endIndex = ps.executeUpdate();
ps.close();
return endIndex;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public Object selectOne(String sqlId, Object parameterObj) {
EasyMybatisMappedStatement easyMybatisMappedStatement = (EasyMybatisMappedStatement)this.mappedStatements.get(sqlId);
Connection connection = this.transactionManager.getConnection();
String easyMybatisbatisSql = easyMybatisMappedStatement.getSql();
String sql = easyMybatisbatisSql.replaceAll("#\\{[a-zA-Z0-9_\\$]*}", "?");
PreparedStatement ps = null;
ResultSet rs = null;
Object obj = null;
try {
ps = connection.prepareStatement(sql);
ps.setString(1, parameterObj.toString());
rs = ps.executeQuery();
if (rs.next()) {
String resultType = easyMybatisMappedStatement.getResultType();
Class<?> aClass = Class.forName(resultType);
Constructor<?> con = aClass.getDeclaredConstructor();
obj = con.newInstance();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
for(int i = 1; i <= columnCount; ++i) {
String columnName = rsmd.getColumnName(i);
char var10000 = columnName.toUpperCase().charAt(0);
String setMethodName = "set" + var10000 + columnName.substring(1);
Method setMethod = aClass.getDeclaredMethod(setMethodName, aClass.getDeclaredField(columnName).getType());
setMethod.invoke(obj, rs.getString(columnName));
}
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
try {
ps.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
return obj;
}
public List<Object> selectList(String sqlId, Object parameterObj) {
EasyMybatisMappedStatement easyMybatisMappedStatement = (EasyMybatisMappedStatement)this.mappedStatements.get(sqlId);
Connection connection = this.transactionManager.getConnection();
String easyMybatisbatisSql = easyMybatisMappedStatement.getSql();
String sql = easyMybatisbatisSql.replaceAll("#\\{[a-zA-Z0-9_\\$]*}", "?");
PreparedStatement ps = null;
ResultSet rs = null;
List<Object> list = new ArrayList();
try {
ps = connection.prepareStatement(sql);
if (parameterObj != null) {
ps.setString(1, parameterObj.toString());
}
rs = ps.executeQuery();
String resultType = easyMybatisMappedStatement.getResultType();
Class<?> aClass = Class.forName(resultType);
Constructor<?> con = aClass.getDeclaredConstructor();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
Map<String, Class<?>> fieldTypeMap = new HashMap();
for(int i = 1; i <= columnCount; ++i) {
String columnName = rsmd.getColumnName(i);
try {
fieldTypeMap.put(columnName, aClass.getDeclaredField(columnName).getType());
} catch (Exception var35) {
}
}
while(rs.next()) {
Object obj = con.newInstance();
for(int i = 1; i <= columnCount; ++i) {
String columnName = rsmd.getColumnName(i);
char var10000 = columnName.toUpperCase().charAt(0);
String setMethodName = "set" + var10000 + columnName.substring(1);
try {
Class<?> fieldType = (Class)fieldTypeMap.get(columnName);
Method setMethod = aClass.getDeclaredMethod(setMethodName, fieldType);
Object value = rs.getObject(columnName);
if (fieldType == String.class) {
setMethod.invoke(obj, rs.getString(columnName));
} else if (fieldType != Integer.class && fieldType != Integer.TYPE) {
if (fieldType != Long.class && fieldType != Long.TYPE) {
setMethod.invoke(obj, value);
} else {
setMethod.invoke(obj, rs.getLong(columnName));
}
} else {
setMethod.invoke(obj, rs.getInt(columnName));
}
} catch (Exception var36) {
}
}
list.add(obj);
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
try {
ps.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
return list;
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.rongx.core;
import java.util.Map;
public class SqlSessionFactory {
private TransactionManager transactionManager;
private Map<String, EasyMybatisMappedStatement> mappedStatements;
public SqlSessionFactory(TransactionManager transactionManager, Map<String, EasyMybatisMappedStatement> mappedStatements) {
this.transactionManager = transactionManager;
this.mappedStatements = mappedStatements;
}
public TransactionManager getTransactionManager() {
return this.transactionManager;
}
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public Map<String, EasyMybatisMappedStatement> getMappedStatements() {
return this.mappedStatements;
}
public void setMappedStatements(Map<String, EasyMybatisMappedStatement> mappedStatements) {
this.mappedStatements = mappedStatements;
}
public SqlSession openSession() {
this.transactionManager.openConnection();
SqlSession sqlSession = new SqlSession(this.transactionManager, this.mappedStatements);
return sqlSession;
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.rongx.core;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream inputStream) throws DocumentException {
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(inputStream);
Element environmentsElt = (Element)document.selectSingleNode("/configuration/environments");
String defaultEnv = environmentsElt.attributeValue("default");
Element environmentElt = (Element)document.selectSingleNode("/configuration/environments/environment[@id='" + defaultEnv + "']");
Element dataSourceElt = environmentElt.element("dataSource");
DataSource dataSource = this.getDataSource(dataSourceElt);
Element transactionManagerElt = environmentElt.element("transactionManager");
TransactionManager transactionManager = this.getTransactionManager(transactionManagerElt, dataSource);
Element mappers = environmentsElt.element("mappers");
Map<String, EasyMybatisMappedStatement> mappedStatements = this.getMappedStatements(mappers);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactory(transactionManager, mappedStatements);
return sqlSessionFactory;
}
private Map<String, EasyMybatisMappedStatement> getMappedStatements(Element mappers) {
Map<String, EasyMybatisMappedStatement> mappedStatements = new HashMap();
mappers.elements().forEach((mapperElt) -> {
try {
String resource = mapperElt.attributeValue("resource");
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(Resources.getResourcesAsStream(resource));
Element mapper = (Element)document.selectSingleNode("/mapper");
String namespace = mapper.attributeValue("namespace");
mapper.elements().forEach((sqlMapper) -> {
String sqlId = sqlMapper.attributeValue("id");
String sql = sqlMapper.getTextTrim();
String parameterType = sqlMapper.attributeValue("parameterType");
String resultType = sqlMapper.attributeValue("resultType");
String sqlType = sqlMapper.getName().toLowerCase();
EasyMybatisMappedStatement easyMybatisMappedStatement = new EasyMybatisMappedStatement(sqlId, resultType, sql, parameterType, sqlType);
mappedStatements.put(namespace + "." + sqlId, easyMybatisMappedStatement);
});
} catch (DocumentException e) {
throw new RuntimeException(e);
}
});
return mappedStatements;
}
private TransactionManager getTransactionManager(Element transactionManagerElt, DataSource dataSource) {
String type = transactionManagerElt.attributeValue("type").toUpperCase();
TransactionManager transactionManager = null;
if ("JDBC".equals(type)) {
transactionManager = new EasyMybatisJDBCTransaction(dataSource, false);
} else if ("MANAGED".equals(type)) {
}
return transactionManager;
}
private DataSource getDataSource(Element dataSourceElt) {
Map<String, String> dataSourceMap = new HashMap();
dataSourceElt.elements().forEach((propertyElt) -> dataSourceMap.put(propertyElt.attributeValue("name"), propertyElt.attributeValue("value")));
String dataSourceType = dataSourceElt.attributeValue("type").toUpperCase();
DataSource dataSource = null;
if (!"POOLED".equals(dataSourceType)) {
if ("UNPOOLED".equals(dataSourceType)) {
dataSource = new EasyMybatisUNPOOLEDDataSource((String)dataSourceMap.get("driver"), (String)dataSourceMap.get("url"), (String)dataSourceMap.get("username"), (String)dataSourceMap.get("password"));
} else if ("JNDI".equals(dataSourceType)) {
}
}
return dataSource;
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.rongx.core;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
public class EasyMybatisJDBCTransaction implements TransactionManager {
private Connection conn;
private DataSource dataSource;
private boolean autoCommit;
public EasyMybatisJDBCTransaction(DataSource dataSource, boolean autoCommit) {
this.dataSource = dataSource;
this.autoCommit = autoCommit;
}
public void commit() {
try {
this.conn.commit();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void rollback() {
try {
this.conn.rollback();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void close() {
try {
this.conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void openConnection() {
try {
this.conn = this.dataSource.getConnection();
this.conn.setAutoCommit(this.autoCommit);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public Connection getConnection() {
return this.conn;
}
}
easy-mybatis 0.0.1 完整代码讲解
我将结合完整的代码,详细讲解这个简化版MyBatis框架的实现原理和工作流程。
项目整体架构
easy-mybatis 0.0.1
├── 配置文件解析层 (SqlSessionFactoryBuilder)
├── 会话管理层 (SqlSessionFactory, SqlSession)
├── 数据操作层 (SqlSession的CRUD方法)
├── 事务管理层 (EasyMybatisJDBCTransaction)
├── 数据源层 (EasyMybatisUNPOOLEDDataSource)
├── 映射配置层 (EasyMybatisMappedStatement)
└── 资源工具层 (Resources)
1. 配置解析与初始化流程
1.1 Resources - 资源加载工具类
package com.rongx.core;
import java.io.InputStream;
public class Resources {
public static InputStream getResourcesAsStream(String config) {
return Thread.currentThread().getContextClassLoader().getResourceAsStream(config);
}
}
作用 :从类路径加载配置文件,为框架提供统一的资源访问入口。
1.2 SqlSessionFactoryBuilder - 工厂建造者
这是框架的启动入口,负责解析XML配置文件并构建SqlSessionFactory。
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream inputStream) throws DocumentException {
// 1. 读取主配置文件
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(inputStream);
// 2. 解析环境配置
Element environmentsElt = (Element)document.selectSingleNode("/configuration/environments");
String defaultEnv = environmentsElt.attributeValue("default");
Element environmentElt = (Element)document.selectSingleNode(
"/configuration/environments/environment[@id='" + defaultEnv + "']");
// 3. 创建数据源
Element dataSourceElt = environmentElt.element("dataSource");
DataSource dataSource = this.getDataSource(dataSourceElt);
// 4. 创建事务管理器
Element transactionManagerElt = environmentElt.element("transactionManager");
TransactionManager transactionManager = this.getTransactionManager(transactionManagerElt, dataSource);
// 5. 加载Mapper文件并解析SQL映射
Element mappers = environmentsElt.element("mappers");
Map<String, EasyMybatisMappedStatement> mappedStatements = this.getMappedStatements(mappers);
// 6. 创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactory(transactionManager, mappedStatements);
return sqlSessionFactory;
}
private Map<String, EasyMybatisMappedStatement> getMappedStatements(Element mappers) {
Map<String, EasyMybatisMappedStatement> mappedStatements = new HashMap();
mappers.elements().forEach((mapperElt) -> {
try {
// 读取Mapper XML文件
String resource = mapperElt.attributeValue("resource");
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(Resources.getResourcesAsStream(resource));
// 解析Mapper中的SQL语句
Element mapper = (Element)document.selectSingleNode("/mapper");
String namespace = mapper.attributeValue("namespace");
mapper.elements().forEach((sqlMapper) -> {
// 提取SQL配置信息
String sqlId = sqlMapper.attributeValue("id");
String sql = sqlMapper.getTextTrim();
String parameterType = sqlMapper.attributeValue("parameterType");
String resultType = sqlMapper.attributeValue("resultType");
String sqlType = sqlMapper.getName().toLowerCase(); // select/insert/update/delete
// 创建MappedStatement并存储
EasyMybatisMappedStatement easyMybatisMappedStatement =
new EasyMybatisMappedStatement(sqlId, resultType, sql, parameterType, sqlType);
mappedStatements.put(namespace + "." + sqlId, easyMybatisMappedStatement);
});
} catch (DocumentException e) {
throw new RuntimeException(e);
}
});
return mappedStatements;
}
private TransactionManager getTransactionManager(Element transactionManagerElt, DataSource dataSource) {
String type = transactionManagerElt.attributeValue("type").toUpperCase();
TransactionManager transactionManager = null;
if ("JDBC".equals(type)) {
// 创建JDBC事务管理器,autoCommit设置为false(手动提交)
transactionManager = new EasyMybatisJDBCTransaction(dataSource, false);
}
return transactionManager;
}
private DataSource getDataSource(Element dataSourceElt) {
// 提取数据源配置属性
Map<String, String> dataSourceMap = new HashMap();
dataSourceElt.elements().forEach((propertyElt) ->
dataSourceMap.put(propertyElt.attributeValue("name"), propertyElt.attributeValue("value")));
String dataSourceType = dataSourceElt.attributeValue("type").toUpperCase();
DataSource dataSource = null;
if ("UNPOOLED".equals(dataSourceType)) {
// 创建非池化数据源
dataSource = new EasyMybatisUNPOOLEDDataSource(
dataSourceMap.get("driver"),
dataSourceMap.get("url"),
dataSourceMap.get("username"),
dataSourceMap.get("password")
);
}
return dataSource;
}
}
1.3 EasyMybatisMappedStatement - SQL映射模型
public class EasyMybatisMappedStatement {
private String sqlId; // SQL语句ID,如"selectUserById"
private String resultType; // 返回类型,如"com.example.User"
private String sql; // SQL语句,如"SELECT * FROM user WHERE id = #{id}"
private String parameterType; // 参数类型,如"java.lang.String"
private String sqlType; // SQL类型,如"select"
// 构造函数和getter/setter
}
作用 :封装一个SQL语句的所有配置信息。
2. 数据源与事务管理
2.1 EasyMybatisUNPOOLEDDataSource - 非池化数据源
public class EasyMybatisUNPOOLEDDataSource implements DataSource {
private String url;
private String username;
private String password;
public EasyMybatisUNPOOLEDDataSource(String driver, String url, String username, String password) {
try {
Class.forName(driver); // 加载数据库驱动
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
this.url = url;
this.username = username;
this.password = password;
}
public Connection getConnection() throws SQLException {
// 每次调用都创建新连接(非池化)
return DriverManager.getConnection(this.url, this.username, this.password);
}
// 其他DataSource接口方法(简单实现或返回null)
}
2.2 EasyMybatisJDBCTransaction - JDBC事务管理器
public class EasyMybatisJDBCTransaction implements TransactionManager {
private Connection conn;
private DataSource dataSource;
private boolean autoCommit; // 通常设置为false,手动控制事务
public void openConnection() {
try {
this.conn = this.dataSource.getConnection();
this.conn.setAutoCommit(this.autoCommit); // 设置手动提交
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void commit() {
try {
this.conn.commit(); // 提交事务
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
// 其他事务操作方法...
}
3. 核心执行流程
3.1 SqlSessionFactory - 会话工厂
public class SqlSessionFactory {
private TransactionManager transactionManager;
private Map<String, EasyMybatisMappedStatement> mappedStatements;
public SqlSession openSession() {
this.transactionManager.openConnection(); // 打开数据库连接
SqlSession sqlSession = new SqlSession(this.transactionManager, this.mappedStatements);
return sqlSession;
}
}
3.2 SqlSession - 核心会话类(重点)
3.2.1 SQL参数处理机制
框架使用#{}作为参数占位符,需要将其转换为JDBC的?:
// 以insert方法为例,展示参数处理流程:
public int insert(String sqlId, Object obj) {
// 1. 获取SQL映射配置
EasyMybatisMappedStatement easyMybatisMappedStatement = this.mappedStatements.get(sqlId);
// 2. 替换#{}为?
String easyMybatisbatisSql = easyMybatisMappedStatement.getSql();
String sql = easyMybatisbatisSql.replaceAll("#\\{[a-zA-Z0-9_\\$]*}", "?");
// 3. 提取参数名映射关系
Map<Integer, String> map = new HashMap();
for(int index = 1; easyMybatisbatisSql.indexOf("#") >= 0; easyMybatisbatisSql = easyMybatisbatisSql.substring(endIndex + 1)) {
int beginIndex = easyMybatisbatisSql.indexOf("#") + 2; // 跳过"#{"
int endIndex = easyMybatisbatisSql.indexOf("}");
map.put(index++, easyMybatisbatisSql.substring(beginIndex, endIndex).trim());
}
// 4. 设置PreparedStatement参数
try {
PreparedStatement ps = connection.prepareStatement(sql);
map.forEach((k, v) -> {
try {
// 通过反射调用getter方法获取参数值
// 例如:#{userName} -> getUserName()
char var10000 = v.toUpperCase().charAt(0);
String getMethodName = "get" + var10000 + v.substring(1);
Method getMethod = obj.getClass().getDeclaredMethod(getMethodName);
ps.setString(k, getMethod.invoke(obj).toString()); // 设置为字符串
} catch (Exception e) {
throw new RuntimeException(e);
}
});
int endIndex = ps.executeUpdate();
ps.close();
return endIndex;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
3.2.2 结果集映射机制
以selectOne方法为例:
public Object selectOne(String sqlId, Object parameterObj) {
// 1. 执行查询
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, parameterObj.toString()); // 设置参数
ResultSet rs = ps.executeQuery();
// 2. 如果有结果,开始映射
if (rs.next()) {
String resultType = easyMybatisMappedStatement.getResultType();
Class<?> aClass = Class.forName(resultType); // 加载返回类型类
// 3. 创建返回对象实例
Constructor<?> con = aClass.getDeclaredConstructor();
Object obj = con.newInstance();
// 4. 获取ResultSet元数据
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
// 5. 遍历每一列,设置对象属性
for(int i = 1; i <= columnCount; ++i) {
String columnName = rsmd.getColumnName(i);
// 生成setter方法名,如:user_name -> setUserName
char var10000 = columnName.toUpperCase().charAt(0);
String setMethodName = "set" + var10000 + columnName.substring(1);
// 获取字段类型,调用对应的setter方法
Method setMethod = aClass.getDeclaredMethod(setMethodName,
aClass.getDeclaredField(columnName).getType());
setMethod.invoke(obj, rs.getString(columnName));
}
return obj;
}
}
3.2.3 selectList方法的类型处理
// selectList方法中处理不同类型字段的逻辑
if (fieldType == String.class) {
setMethod.invoke(obj, rs.getString(columnName));
} else if (fieldType != Integer.class && fieldType != Integer.TYPE) {
if (fieldType != Long.class && fieldType != Long.TYPE) {
setMethod.invoke(obj, value); // 其他类型直接设置
} else {
setMethod.invoke(obj, rs.getLong(columnName)); // Long类型
}
} else {
setMethod.invoke(obj, rs.getInt(columnName)); // Integer类型
}
4. 完整使用示例
4.1 配置文件
mybatis-config.xml:
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="UNPOOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
UserMapper.xml:
<mapper namespace="UserMapper">
<select id="selectUserById" parameterType="java.lang.String" resultType="com.example.User">
SELECT id, user_name, age, email FROM users WHERE id = #{id}
</select>
<insert id="insertUser" parameterType="com.example.User">
INSERT INTO users(id, user_name, age, email) VALUES(#{id}, #{userName}, #{age}, #{email})
</insert>
<update id="updateUser" parameterType="com.example.User">
UPDATE users SET user_name = #{userName}, age = #{age}, email = #{email} WHERE id = #{id}
</update>
<delete id="deleteUser" parameterType="java.lang.String">
DELETE FROM users WHERE id = #{id}
</delete>
</mapper>
4.2 Java实体类
package com.example;
public class User {
private String id;
private String userName;
private Integer age;
private String email;
// 必须有无参构造函数
public User() {}
// getter和setter方法(框架依赖这些方法)
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getUserName() { return userName; }
public void setUserName(String userName) { this.userName = userName; }
// 其他getter/setter...
}
4.3 使用代码
public class Main {
public static void main(String[] args) throws DocumentException {
// 1. 读取配置文件
InputStream inputStream = Resources.getResourcesAsStream("mybatis-config.xml");
// 2. 创建SqlSessionFactory
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);
// 3. 获取SqlSession
SqlSession session = factory.openSession();
try {
// 4. 执行查询
User user = (User) session.selectOne("UserMapper.selectUserById", "1001");
System.out.println(user.getUserName());
// 5. 执行插入
User newUser = new User();
newUser.setId("1002");
newUser.setUserName("张三");
newUser.setAge(25);
newUser.setEmail("zhangsan@example.com");
int rows = session.insert("UserMapper.insertUser", newUser);
System.out.println("插入行数: " + rows);
// 6. 提交事务
session.commit();
} catch (Exception e) {
// 7. 异常时回滚
session.rollback();
e.printStackTrace();
} finally {
// 8. 关闭会话
session.close();
}
}
}
5. 框架执行流程总结
1. 初始化阶段:
Resources.getResourcesAsStream() -> 加载配置文件
↓
SqlSessionFactoryBuilder.build() -> 解析XML配置
↓
创建DataSource -> EasyMybatisUNPOOLEDDataSource
↓
创建TransactionManager -> EasyMybatisJDBCTransaction
↓
解析Mapper文件 -> 创建EasyMybatisMappedStatement集合
↓
创建SqlSessionFactory
2. 执行阶段:
SqlSessionFactory.openSession() -> 打开连接
↓
SqlSession.insert/update/delete/select() -> 执行SQL
↓
参数处理:#{}替换为? + 反射调用getter
↓
执行SQL:PreparedStatement.execute()
↓
结果映射:ResultSet -> 反射调用setter
↓
返回结果
3. 清理阶段:
session.commit()/rollback() -> 提交/回滚事务
↓
session.close() -> 关闭连接
6. 当前版本的局限性
-
数据类型支持有限:
-
参数设置只支持
ps.setString(),所有参数都被转为字符串 -
结果映射只支持String、Integer、Long等少数类型
-
-
SQL处理简单:
-
不支持动态SQL
-
不支持
${}表达式 -
参数名只能是简单的属性名,不支持嵌套
-
-
性能问题:
-
非池化数据源,每次操作都创建新连接
-
大量使用反射,性能较低
-
没有缓存机制
-
-
错误处理:
-
异常处理简单,直接包装为RuntimeException
-
没有详细的错误信息
-
-
功能缺失:
-
不支持注解配置
-
不支持插件/拦截器
-
不支持多数据源
-
不支持延迟加载
-
尽管有这些限制,但这个简化版本清晰地展示了MyBatis框架的核心原理:配置解析、SQL执行、参数映射和结果映射,是一个很好的学习示例。