Mybatis中的设计模式
Contents
引言
在学习设计模式的过程中,我们往往只停留在理论层面,很少有机会在实际开发中应用这些模式。然而,理解设计模式的最佳途径之一是通过阅读优秀框架的源码,如Mybatis。Mybatis作为一个广泛使用的持久层框架,其实现中运用了多种设计模式,包括Builder模式、工厂模式、单例模式、代理模式、组合模式、模板方法模式、适配器模式、装饰者模式和迭代器模式。本文将探讨这些设计模式在Mybatis中的具体应用,帮助大家更好地理解和应用这些设计模式。
正文
1. Builder模式
定义
Builder模式将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
classDiagram
class SqlSessionFactoryBuilder {
+build(InputStream inputStream) SqlSessionFactory
note for SqlSessionFactoryBuilder "负责构建SqlSessionFactory的类"
}
class XMLConfigBuilder {
+parse() Configuration
note for XMLConfigBuilder "解析Mybatis配置文件的类"
}
class Configuration {
+addMapper(Class<?> type)
note for Configuration "Mybatis的核心配置类,包含Mapper的配置信息"
}
class SqlSessionFactory {
+openSession() SqlSession
note for SqlSessionFactory "用于创建SqlSession的工厂类"
}
class SqlSession {
+selectOne(String statement, Object parameter)
note for SqlSession "执行SQL语句的会话类"
}
SqlSessionFactoryBuilder --> XMLConfigBuilder : uses
SqlSessionFactoryBuilder --> Configuration : creates
SqlSessionFactory --> SqlSession : creates
SqlSessionFactory --> Configuration : configures
在Mybatis中的应用
Mybatis中的SqlSessionFactoryBuilder使用了Builder模式。该类通过调用XMLConfigBuilder读取Mybatis配置文件和Mapper文件,构建核心对象Configuration,然后使用该对象创建SqlSessionFactory。
代码示例
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);
2. 工厂模式
定义
工厂模式提供了一种创建对象的方式,而不需要指定具体的类。它通过定义一个接口来创建对象,允许子类决定实例化哪一个类。
classDiagram
class SqlSessionFactory {
+openSession() SqlSession
}
在Mybatis中的应用
在Mybatis中,SqlSessionFactory实现了简单工厂模式。根据不同的参数,工厂可以返回不同类型的SqlSession实例。具体来说,SqlSessionFactory根据配置文件中的信息,决定创建哪种类型的SqlSession,从而实现灵活的会话管理。
代码示例
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
3. 单例模式
定义:单例模式确保一个类只有一个实例,并提供一个全局访问点。
classDiagram
class ErrorContext {
+instance() ErrorContext
}
ErrorContext --> ErrorContext : singleton
note for ErrorContext "用于记录线程范围内的错误信息的类"
class LogFactory {
+getLog(Class<?> clazz) Log
}
LogFactory --> LogFactory : singleton
note for LogFactory "用于获取日志对象的工厂类"
在Mybatis中的应用
Mybatis中的ErrorContext和LogFactory使用了单例模式。ErrorContext用于记录线程范围内的错误信息,而LogFactory用于获取日志对象。
代码示例
ErrorContext context = ErrorContext.instance();
context.message("test");
4. 代理模式
定义
代理模式为其他对象提供一个代理以控制对这个对象的访问。
classDiagram
class UserMapper {
<<interface>>
+getUserById(int id) User
}
class MapperProxy {
-SqlSession sqlSession
+MapperProxy(SqlSession sqlSession)
+invoke(Object proxy, Method method, Object[] args) Object
}
class SqlSession {
+getMapper(Class<T> type) T
+selectOne(String statement, Object parameter) T
}
class Client {
+main(String[] args)
}
class User {
+int id
+String name
+User(int id, String name)
}
MapperProxy --> SqlSession : uses
SqlSession --> MapperProxy : creates
Client --> SqlSession : uses
Client --> UserMapper : uses
UserMapper <|.. MapperProxy : implements
MapperProxy --> User : returns
note for MapperProxy "MapperProxy实现了InvocationHandler接口,负责处理Mapper接口方法的调用。它使用SqlSession来执行SQL语句并返回结果。"
note for SqlSession "SqlSession提供了执行SQL语句、获取Mapper实例等功能。它使用JDK动态代理生成Mapper接口的实现类。"
note for Client "客户端代码使用SqlSession和MapperProxy来执行数据库操作。它通过SqlSession获取Mapper实例,并调用Mapper接口的方法。"
note for User "User表示数据库中的用户实体。它是Mapper接口方法的返回类型。"
在Mybatis中的应用
Mybatis的核心功能之一是通过代理模式实现的。开发者只需定义Mapper接口,Mybatis会自动生成代理类来执行SQL语句。
代码示例
class UserMapper {
List<User> selectAll();
}
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.selectAll();
// 生成代理类
class UserMapperProxy implements MapperProxy<UserMapper> {
@Override
public List<User> selectAll() {
return sqlSession.selectList("select * from users");
}
}
5. 组合模式
定义:组合模式将对象组合成树形结构以表示“部分-整体”的层次结构。
在Mybatis中的应用
Mybatis中的SqlNode和SqlSource使用了组合模式,构建复杂的SQL语句结构。
classDiagram
class SqlNode {
<<interface>>
+apply(DynamicContext context)
note for SqlNode "表示一个SQL片段,是组合模式中的叶子节点"
}
class StaticTextSqlNode {
-String text
+StaticTextSqlNode(String text)
+apply(DynamicContext context)
note for StaticTextSqlNode "表示一个静态文本SQL片段,实现了SqlNode接口"
}
class MixedSqlNode {
-List<SqlNode> contents
+MixedSqlNode(List<SqlNode> contents)
+apply(DynamicContext context)
note for MixedSqlNode "表示一个包含多个SqlNode的组合节点,实现了SqlNode接口"
}
class DynamicContext {
-StringBuilder sqlBuilder
+appendSql(String sql)
+getSql() String
note for DynamicContext "用于构建动态SQL语句的上下文"
}
class SqlSource {
-SqlNode rootSqlNode
+SqlSource(SqlNode rootSqlNode)
+getBoundSql() String
note for SqlSource "表示一个完整的SQL语句,由多个SqlNode组合而成"
}
class Client {
+main(String[] args)
note for Client "客户端代码,使用SqlSource和SqlNode来构建动态SQL语句"
}
SqlNode <|.. StaticTextSqlNode : implements
SqlNode <|.. MixedSqlNode : implements
MixedSqlNode --> SqlNode : contains
SqlSource --> SqlNode : uses
Client --> SqlSource : uses
StaticTextSqlNode --> DynamicContext : uses
MixedSqlNode --> DynamicContext : uses
SqlSource --> DynamicContext : uses
note for SqlNode "SqlNode表示一个SQL片段,是组合模式中的叶子节点。它定义了apply方法,用于将SQL片段应用到动态上下文中。"
note for StaticTextSqlNode "StaticTextSqlNode表示一个静态文本SQL片段,实现了SqlNode接口。它包含一个文本字段,并在apply方法中将文本追加到动态上下文中。"
note for MixedSqlNode "MixedSqlNode表示一个包含多个SqlNode的组合节点,实现了SqlNode接口。它包含一个SqlNode列表,并在apply方法中依次调用每个SqlNode的apply方法。"
note for DynamicContext "DynamicContext用于构建动态SQL语句的上下文。它包含一个StringBuilder,用于存储构建的SQL语句,并提供appendSql和getSql方法。"
note for SqlSource "SqlSource表示一个完整的SQL语句,由多个SqlNode组合而成。它包含一个根SqlNode,并在getBoundSql方法中调用根SqlNode的apply方法,返回构建的SQL语句。"
note for Client "客户端代码使用SqlSource和SqlNode来构建动态SQL语句。它创建SqlNode实例,并将它们组合成一个SqlSource,最后调用SqlSource的getBoundSql方法获取构建的SQL语句。"
6. 模板方法模式
定义:模板方法模式定义了一个算法的骨架,并允许子类为一个或多个步骤提供实现。
classDiagram
class Executor {
<<interface>>
+query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) List<E>
}
class SimpleExecutor {
+query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) List<E>
}
class BaseExecutor {
<<abstract>>
+query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) List<E>
}
Executor <|.. SimpleExecutor : implements
Executor <|.. BaseExecutor : implements
BaseExecutor --> SimpleExecutor : extends
在Mybatis中的应用
Mybatis中的BaseExecutor和SimpleExecutor使用了模板方法模式,定义了执行SQL的通用流程。
代码示例
public abstract class BaseExecutor implements Executor {
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 模板方法
}
}
7. 适配器模式
定义
适配器模式将一个类的接口转换成客户端期望的另一个接口。
classDiagram
class Log {
<<interface>>
+error(String s, Throwable e)
}
class Log4jImpl {
+Log4jImpl(String clazz)
+error(String s, Throwable e)
}
Log <|.. Log4jImpl : implements
note for Log4jImpl "Log4jImpl实现了Log接口,负责使用Log4j记录错误信息"
在Mybatis中的应用
Mybatis中的Log接口使用了适配器模式,适配不同的日志框架。
代码示例
public class Log4jImpl implements Log {
private Logger log;
public Log4jImpl(String clazz) {
log = Logger.getLogger(clazz);
}
// 实现Log接口的方法
}
8. 装饰者模式
定义
装饰者模式动态地给一个对象添加一些额外的职责。
classDiagram
class Cache {
<<interface>>
+putObject(Object key, Object value)
}
class LoggingCache {
-Cache delegate
+LoggingCache(Cache delegate)
+putObject(Object key, Object value)
}
Cache <|.. LoggingCache : implements
note for LoggingCache "LoggingCache装饰了Cache接口,负责记录缓存操作的日志"
在Mybatis中的应用
Mybatis中的Cache接口使用了装饰者模式,通过不同的装饰器实现缓存功能。
代码示例
public class LoggingCache implements Cache {
private Cache delegate;
public LoggingCache(Cache delegate) {
this.delegate = delegate;
}
// 实现Cache接口的方法
}
9. 迭代器模式
定义
迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部表示。
classDiagram
class ResultMap {
<<interface>>
+getResultMappings() Iterator<ResultMapping>
}
class DefaultResultMap {
+getResultMappings() Iterator<ResultMapping>
}
ResultMap <|.. DefaultResultMap : implements
note for DefaultResultMap "DefaultResultMap实现了ResultMap接口,负责获取结果映射"
在Mybatis中的应用
Mybatis中的ResultMap使用了迭代器模式,遍历结果集中的每一行数据。
代码示例
public class DefaultResultMap implements ResultMap {
public Iterator<ResultMapping> getResultMappings() {
return mappings.iterator();
}
}
结论
通过深入分析Mybatis中各种设计模式的应用,我们可以看到设计模式在实际开发中的重要性。设计模式不仅仅是理论,它们是解决实际问题的有效工具。通过阅读和理解优秀框架的源码,我们可以更好地掌握这些设计模式,并在自己的项目中灵活应用。希望本文能帮助读者在设计模式的学习和应用上更进一步。