蜂信物联FastBee平台https://gitee.com/beecue/fastbee

阿里资料开源项目https://gitee.com/vip204888

百度低代码前端框架https://gitee.com/baidu/amis

OpenHarmony开源项目https://gitcode.com/openharmony

仓颉编程语言开放项目https://gitcode.com/Cangjie

1.2.1 获取SqlSession

==================

我们通过factory工厂对象的openSession()方法获取到SqlSession,打开openSession()方法,代码如下:

// 6. 进入openSession方法

@Override

public SqlSession openSession() {

//getDefaultExecutorType()传递的是SimpleExecutor

return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);

}

// 7. 进入openSessionFromDataSource。

// ExecutorType 为Executor的类型,TransactionIsolationLevel为事务隔离级别,autoCommit是否开启事务

// openSession的多个重载方法可以指定获得的SeqSession的Executor类型和事务的处理

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {

Transaction tx = null;

try {

// 获得 Environment 对象

final Environment environment = configuration.getEnvironment();

// 创建 Transaction 对象

final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);

tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

// 创建 Executor 对象

final Executor executor = configuration.newExecutor(tx, execType);

// 创建 DefaultSqlSession 对象

return new DefaultSqlSession(configuration, executor, autoCommit);

} catch (Exception e) {

// 如果发生异常,则关闭 Transaction 对象

closeTransaction(tx); // may have fetched a connection so lets call close()

throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);

} finally {

ErrorContext.instance().reset();

}

}

复制代码

1.2.2 执行SqlSession接口

====================

获取到SqlSession过后,接下来就是执行SqlSession中的接口,也就是传统方式代码的第二行,参数为全限定类名+方法名,接收到参数后,会从Configuration对象的Mapped Statement容器中取到对应的MappedStatement对象,然后交由Executor来执行。selectList()重载方法源码如下:

//8.进入selectList方法,多个重载方法

@Override

public List selectList(String statement) {

return this.selectList(statement, null);

}

@Override

public List selectList(String statement, Object parameter) {

return this.selectList(statement, parameter, RowBounds.DEFAULT);

}

@Override

public List selectList(String statement, Object parameter, RowBounds rowBounds) {

try {

// 根据传入的全限定类名+方法名从映射的Map中获得 MappedStatement 对象

MappedStatement ms = configuration.getMappedStatement(statement);

// 调用Executor中的方法来处理

// RowBounds用来处理分页逻辑

return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

} catch (Exception e) {

throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);

} finally {

ErrorContext.instance().reset();

}

}

复制代码

1.2.3 执行Executor接口

==================

继续上面的源码,进入executor.query()方法。

//此方法在SimpleExecutor的父类BaseExecutor中实现

@Override

public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {

//根据传入的参数动态获得SQL语句,最后返回用BoundSql对象表示

BoundSql boundSql = ms.getBoundSql(parameter);

//为本次查询创建缓存的Key

CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);

// 查询

return query(ms, parameter, rowBounds, resultHandler, key, boundSql);

}

@SuppressWarnings(“unchecked”)

@Override

public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {

ErrorContext.instance().resource(ms.getResource()).activity(“executing a query”).object(ms.getId());

// 已经关闭,则抛出 ExecutorException 异常

if (closed) {

throw new ExecutorException(“Executor was closed.”);

}

// 清空本地缓存,如果 queryStack 为零,并且要求清空本地缓存。

if (queryStack == 0 && ms.isFlushCacheRequired()) {

clearLocalCache();

}

List list;

try {

// queryStack + 1

queryStack++;

// 从一级缓存中,获取查询结果

list = resultHandler == null ? (List) localCache.getObject(key) : null;

// 获取到,则进行处理

if (list != null) {

handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);

// 获得不到,则从数据库中查询

} else {

list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

}

} finally {

// queryStack - 1

queryStack–;

}

if (queryStack == 0) {

// 执行延迟加载

for (DeferredLoad deferredLoad : deferredLoads) {

deferredLoad.load();

}

// issue #601

// 清空 deferredLoads

deferredLoads.clear();

// 如果缓存级别是 LocalCacheScope.STATEMENT ,则进行清理

if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {

// issue #482

clearLocalCache();

}

}

return list;

}

// 从数据库中读取操作

private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {

List list;

// 在缓存中,添加占位对象。此处的占位符,和延迟加载有关,可见 DeferredLoad#canLoad() 方法

localCache.putObject(key, EXECUTION_PLACEHOLDER);

try {

// 执行读操作

list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);

} finally {

// 从缓存中,移除占位对象

localCache.removeObject(key);

}

// 添加到缓存中

localCache.putObject(key, list);

// 暂时忽略,存储过程相关

if (ms.getStatementType() == StatementType.CALLABLE) {

localOutputParameterCache.putObject(key, parameter);

}

return list;

}

// SimpleExecutor中实现父类的doQuery()抽象方法

@Override

public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {

Statement stmt = null;

try {

Configuration configuration = ms.getConfiguration();

// 传入参数创建StatementHanlder对象来执行查询

StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);

// 创建jdbc中的statement对象

stmt = prepareStatement(handler, ms.getStatementLog());

// 执行 StatementHandler ,进行读操作

return handler.query(stmt, resultHandler);

} finally {

// 关闭 StatementHandler 对象

closeStatement(stmt);

}

}

// 初始化 StatementHandler 对象

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {

Statement stmt;

// 获得 Connection 对象

Connection connection = getConnection(statementLog);

// 创建 Statement 或 PrepareStatement 对象

stmt = handler.prepare(connection, transaction.getTimeout());

// 设置 SQL 上的参数,例如 PrepareStatement 对象上的占位符

handler.parameterize(stmt);

return stmt;

}

复制代码

Executor.query()方法几经转折,最后会创建一个StatementHandler对象,然后将必要的参数传递给StatementHandler,使用StatementHandler来完成对数据库的查询,最终返回List结果集。从以上代码可以看出,Executor的功能和作用是:

  1. 根据传递的参数,完成SQL语句的动态解析,生成BoundSql对象,供StatementHandler使用;

  2. 为查询创建缓存,以提高性能;

  3. 创建JDBC的Statement连接对象,床底给StatementHandler对象,返回List查询结果。

1.2.4 StatementHandler接口

========================

StatementHandler接口主要完成两个工作:

  1. 对于JDBC的PreparedStatement类型的对象,创建的过程中,我们使用的是SQL语句字符串会包含若干个?占位符。后面介绍如何对占位符设值,StatementHandler通过parameterize()方法对Statement进行设值;

  2. StatementHandler通过List query(Statement statement, ResultHandler resultHandler)方法来完成执行Statement和将Statement对象返回resultSet封装成List。

进入到StatementHandler的parameterize(statement)方法的实现:

@Override

public void parameterize(Statement statement) throws SQLException {

//使用ParameterHandler对象来完成对Statement的设值

parameterHandler.setParameters((PreparedStatement) statement);

}

@SuppressWarnings(“Duplicates”)

@Override

public void setParameters(PreparedStatement ps) {

ErrorContext.instance().activity(“setting parameters”).object(mappedStatement.getParameterMap().getId());

// 遍历 ParameterMapping 数组

List parameterMappings = boundSql.getParameterMappings();

if (parameterMappings != null) {

for (int i = 0; i < parameterMappings.size(); i++) {

// 获得 ParameterMapping 对象

ParameterMapping parameterMapping = parameterMappings.get(i);

if (parameterMapping.getMode() != ParameterMode.OUT) {

// 获得值

Object value;

String propertyName = parameterMapping.getProperty();

if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params

value = boundSql.getAdditionalParameter(propertyName);

} else if (parameterObject == null) {

value = null;

} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {

value = parameterObject;

} else {

MetaObject metaObject = configuration.newMetaObject(parameterObject);

value = metaObject.getValue(propertyName);

}

// 获得 typeHandler、jdbcType 属性

TypeHandler typeHandler = parameterMapping.getTypeHandler();

JdbcType jdbcType = parameterMapping.getJdbcType();

if (value == null && jdbcType == null) {

jdbcType = configuration.getJdbcTypeForNull();

}

// 设置 ? 占位符的参数

try {

typeHandler.setParameter(ps, i + 1, value, jdbcType);

} catch (TypeException | SQLException e) {

throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);

}

}

}

}

}

复制代码

从以上代码可以看出,StatementHandler的parameterize(Statement) 方法调用了ParameterHandlerጱsetParameters(statement) 方法,ParameterHandlerጱsetParameters(Statement)根据我们输入的参数,对statement对象的? 占位符进行赋值。进入到StatementHandler 的List query(Statement statement, ResultHandler resultHandler)方法的实现:

@Override

public List query(Statement statement, ResultHandler resultHandler) throws SQLException {

PreparedStatement ps = (PreparedStatement) statement;

// 1、执行查询

ps.execute();

// 2、交由ResultHandler处理返回结果

return resultSetHandler.handleResultSets(ps);

}

复制代码

1.3 封装返回结果集

===========

从query()方法我们可以看出,StatementHandler的List query(Statement statement, ResultHandler resultHandler)方法的实现,是调用了ResultSetHandler的handleResultSets(Statement) 方法。ResultSetHandler的handleResultSets(Statement) 方法会将Statement语句执行后得到的resultSet结果集转换成List结果集。

// 处理 {@link java.sql.ResultSet} 结果集

@Override

public List handleResultSets(Statement stmt) throws SQLException {

ErrorContext.instance().activity(“handling results”).object(mappedStatement.getId());

// 多 ResultSet 的结果集合,每个 ResultSet 对应一个 Object 对象。而实际上,每个 Object 是 List 对象。

// 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,multipleResults 最多就一个元素。

final List multipleResults = new ArrayList<>();

int resultSetCount = 0;

// 获得首个 ResultSet 对象,并封装成 ResultSetWrapper 对象

ResultSetWrapper rsw = getFirstResultSet(stmt);

// 获得 ResultMap 数组

// 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,也就是说,resultMaps 就一个元素。

List resultMaps = mappedStatement.getResultMaps();

int resultMapCount = resultMaps.size();

validateResultMapsCount(rsw, resultMapCount); // 校验

while (rsw != null && resultMapCount > resultSetCount) {

// 获得 ResultMap 对象

ResultMap resultMap = resultMaps.get(resultSetCount);

// 处理 ResultSet ,将结果添加到 multipleResults 中

handleResultSet(rsw, resultMap, multipleResults, null);

// 获得下一个 ResultSet 对象,并封装成 ResultSetWrapper 对象

rsw = getNextResultSet(stmt);

// 清理

cleanUpAfterHandlingResultSet();

// resultSetCount ++

resultSetCount++;

}

// 因为 mappedStatement.resultSets 只在存储过程中使用,本系列暂时不考虑,忽略即可

String[] resultSets = mappedStatement.getResultSets();

if (resultSets != null) {

while (rsw != null && resultSetCount < resultSets.length) {

ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);

if (parentMapping != null) {

String nestedResultMapId = parentMapping.getNestedResultMapId();

ResultMap resultMap = configuration.getResultMap(nestedResultMapId);

handleResultSet(rsw, resultMap, null, parentMapping);

}

rsw = getNextResultSet(stmt);

cleanUpAfterHandlingResultSet();

resultSetCount++;

}

}

// 如果是 multipleResults 单元素,则取首元素返回

return collapseSingleResultList(multipleResults);

}

复制代码

2 mapper代理方式

============

回顾一下Mapper代理方式的写法:

public static void main(String[] args) {

// 前三步相同

InputStream inputStream = Resources.getResourceAsStream(“sqlMapConfig.xml”);

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);

SqlSession sqlSession = factory.openSession();

// 这里不再调用SqlSession的API,而是获取了接口对象,调用接口中的方法

UserMapper mapper = sqlSession.getMapper(UserMapper.class);

List list = mapper.getUserByName(“tom”);

}

复制代码

思考一个问题,通常的Mapper接口我们都没有写实现类却可以正常使用,这是为什么呢?答案就是:动态代理

开始之前介绍一下MyBatis初始化时对接口的处理:MapperRegistry是Configuration中的一个属性,它内部维护一个HashMap用于存放mapper接口的工厂类,每个接口对应一个工厂类。mappers中可以配置接口的包路径,或者某个具体的接口类。

复制代码

当解析mappers标签时,它会判断如果解析到的是mapper配置文件时,会再将对应配置文件中的增删改查标签一一封装成MappedStatement对象,存入mappedStatements中。当解析到的是接口时,会建此接口对应的MapperProxyFactory对象,存入HashMap中,key为接口的字节码对象,value为此接口对应的MapperProxyFactory对象。

2.1 mapper动态代理

==============

进入sqlSession.getMapper(UserMapper.class)中:

@Override

public T getMapper(Class type) {

return configuration.getMapper(type, this);

}

public T getMapper(Class type, SqlSession sqlSession) {

return mapperRegistry.getMapper(type, sqlSession);

}

@SuppressWarnings(“unchecked”)

public T getMapper(Class type, SqlSession sqlSession) {

// 获得 MapperProxyFactory 对象

final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);

// 不存在,则抛出 BindingException 异常

if (mapperProxyFactory == null) {

throw new BindingException(“Type " + type + " is not known to the MapperRegistry.”);

}

/// 通过动态代理工厂生成实例。

try {

return mapperProxyFactory.newInstance(sqlSession);

} catch (Exception e) {

throw new BindingException("Error getting mapper instance. Cause: " + e, e);

}

}

//MapperProxyFactory类中的newInstance方法

public T newInstance(SqlSession sqlSession) {

// 创建了JDK动态代理的invocationHandler接口的实现类mapperProxy

final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);

// 调用了重载方法

return newInstance(mapperProxy);

}

@SuppressWarnings(“unchecked”)

protected T newInstance(MapperProxy mapperProxy) {

return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);

}

复制代码

2.2 invoke()方法

==============

在动态代理返回了实例后,我们就可以直接调用mapper类中的方法了,但代理对象调用方法,真正执行的是在MapperProxy中的invoke方法中:

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

try {

// 如果是 Object 定义的方法,直接调用

if (Object.class.equals(method.getDeclaringClass())) {

return method.invoke(this, args);

言尽于此,完结

无论是一个初级的 coder,高级的程序员,还是顶级的系统架构师,应该都有深刻的领会到设计模式的重要性。

  • 第一,设计模式能让专业人之间交流方便,如下:

程序员A:这里我用了XXX设计模式

程序员B:那我大致了解你程序的设计思路了

  • 第二,易维护

项目经理:今天客户有这样一个需求…

程序员:明白了,这里我使用了XXX设计模式,所以改起来很快

  • 第三,设计模式是编程经验的总结

程序员A:B,你怎么想到要这样去构建你的代码

程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题

  • 第四,学习设计模式并不是必须的

程序员A:B,你这段代码使用的是XXX设计模式对吗?

程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的

image

从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!

image

搜集费时费力,能看到此处的都是真爱!
调用方法,真正执行的是在MapperProxy中的invoke方法中:

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

try {

// 如果是 Object 定义的方法,直接调用

if (Object.class.equals(method.getDeclaringClass())) {

return method.invoke(this, args);

言尽于此,完结

无论是一个初级的 coder,高级的程序员,还是顶级的系统架构师,应该都有深刻的领会到设计模式的重要性。

  • 第一,设计模式能让专业人之间交流方便,如下:

程序员A:这里我用了XXX设计模式

程序员B:那我大致了解你程序的设计思路了

  • 第二,易维护

项目经理:今天客户有这样一个需求…

程序员:明白了,这里我使用了XXX设计模式,所以改起来很快

  • 第三,设计模式是编程经验的总结

程序员A:B,你怎么想到要这样去构建你的代码

程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题

  • 第四,学习设计模式并不是必须的

程序员A:B,你这段代码使用的是XXX设计模式对吗?

程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的

[外链图片转存中…(img-OZc7d1Oz-1725199652797)]

从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!

[外链图片转存中…(img-c28Gt01J-1725199652797)]

搜集费时费力,能看到此处的都是真爱!

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐