Mybatis中的设计模式

Mybatis中的设计模式

Mybatis中使用了大量的设计模式。

以下列举一些看源码时,觉得还不错的用法:

创建型模式

工厂方法模式

DataSourceFactory

在这里插入图片描述

通过不同的子类工厂,实例化不同的DataSource

TransactionFactory

在这里插入图片描述

通过不同的工厂,生产不同的Transaction

单例模式

ErrorContext是单例模式,但只是线程级别的:

 private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<>();private ErrorContext() {}public static ErrorContext instance() {ErrorContext context = LOCAL.get();if (context == null) {context = new ErrorContext();LOCAL.set(context);}return context;}

Configuration 类虽然正常情况下只有一个实例,但是它的设计并不符合单例模式

  public Configuration(Environment environment) {this();this.environment = environment;}

可以看到,它的构造器并没有私有化,我们可以new多个实例

LogFactory也不是单例模式,每次都会通过构造器实例化对象

 private LogFactory() {// disable construction}public static Log getLog(Class<?> aClass) {return getLog(aClass.getName());}public static Log getLog(String logger) {try {return logConstructor.newInstance(logger);} catch (Throwable t) {throw new LogException("Error creating logger for logger " + logger + ".  Cause: " + t, t);}}

结构型模式

代理模式

在初始化配置时,会调用MapperRegistry的addMapper方法将Mapper接口存储在knownMappers中,key为Mapper接口,value为MapperProxyFactory实例

  public <T> void addMapper(Class<T> type) {if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {knownMappers.put(type, new MapperProxyFactory<>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}

当我们通过UserMapper mapper = sqlSession.getMapper(UserMapper.class);获取Mapper接口时,则会调用MapperRegistry#getMapper,返回Mapper接口的代理类

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);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);}}

代理类的生成

  public T newInstance(SqlSession sqlSession) {final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);}
  protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}

可以看到,使用的是JDK动态代理,MapperProxy实现了InvocationHandler接口,我们关注MapperProxy#invoke即可:

  @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else if (method.isDefault()) {return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}final MapperMethod mapperMethod = cachedMapperMethod(method);return mapperMethod.execute(sqlSession, args);}

调用mapper接口,最终执行的就是我们编写的SQL

行为型模式

模板方法模式

BaseExecutor

BaseExecutor实现了Executor接口,定义了模板方法,并将钩子方法留给子类实现:

模板方法钩子方法
querydoQuery
updatedoUpdate
flushStatementsdoFlushStatements
queryCursordoQueryCursor
BaseTypeHandler

BaseTypeHandler实现了TypeHandler接口,定义模板方法,将钩子方法留给子类实现:

  • setNonNullParameter

  • getNullableResult

模板方法钩子方法
setParametersetNonNullParameter
getResult(ResultSet rs, String columnName)getNullableResult(ResultSet rs, String columnName)
getResult(ResultSet rs, int columnIndex)getNullableResult(ResultSet rs, int columnIndex)
getResult(CallableStatement cs, int columnIndex)getNullableResult(CallableStatement cs, int columnIndex)

策略模式

DefaultParameterHandler#setParameters中,

  @Overridepublic void setParameters(PreparedStatement ps) {ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();if (parameterMappings != null) {for (int i = 0; i < parameterMappings.size(); i++) {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 paramsvalue = 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 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);}}}}}

重点关注:

          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);}

根据参数映射,拿到类型处理器,就是策略模式的应用,不同的类型处理器setParameter方法实现并不一样,这就是不同的参数类型,使用不同的类型处理器处理。

在解析结果集时,也是一样的,不同的类型需要使用不同的类型处理器获取结果:

DefaultResultSetHandler#getPropertyMappingValue通过ResultMap的属性映射:

  private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)throws SQLException {if (propertyMapping.getNestedQueryId() != null) {return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);} else if (propertyMapping.getResultSet() != null) {addPendingChildRelation(rs, metaResultObject, propertyMapping);   // TODO is that OK?return DEFERRED;} else {final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);return typeHandler.getResult(rs, column);}}

DefaultResultSetHandler#applyAutomaticMappings自动映射也是一样的:

  private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);boolean foundValues = false;if (!autoMapping.isEmpty()) {for (UnMappedColumnAutoMapping mapping : autoMapping) {final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);if (value != null) {foundValues = true;}if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {// gcode issue #377, call setter on nulls (value is not 'found')metaObject.setValue(mapping.property, value);}}}return foundValues;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/197412.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

17:00面试,17:06就出来了,问的问题有点变态。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%,…

css新闻链接案例

利用html和css构建出新闻链接案例&#xff0c;使用渐变色做出背景色变化 background: linear-gradient(to bottom, rgb(137, 210, 251), rgb(238, 248, 254), white); 利用背景图片&#xff0c;调整位置完成 dd { height: 28px; line-height: 28px; background-image: url(./图…

@Scheduled,Quartz,XXL-JOB三种定时任务总结

Scheduled&#xff0c;Quartz&#xff0c;XXL-JOB三种定时任务总结 一、Scheduled 简介 Scheduled 是 Spring 框架中用于声明定时任务的注解。通过使用 Scheduled 注解&#xff0c;你可以指定一个方法应该在何时执行&#xff0c;无需依赖外部的调度器。 这个注解通常与Enab…

判断是否存在重复的数

系列文章目录 进阶的卡莎C_睡觉觉觉得的博客-CSDN博客数1的个数_睡觉觉觉得的博客-CSDN博客双精度浮点数的输入输出_睡觉觉觉得的博客-CSDN博客足球联赛积分_睡觉觉觉得的博客-CSDN博客大减价(一级)_睡觉觉觉得的博客-CSDN博客小写字母的判断_睡觉觉觉得的博客-CSDN博客纸币(C…

(C语言)通过循环按行顺序为一个矩阵赋予1,3,5,7,9,等奇数,然后输出矩阵左下角的值。

#include<stdio.h> int main() {int a[5][5];int n 1;for(int i 0;i < 5;i ){for(int j 0;j < 5;j ){a[i][j] n;n 2;}}for(int i 0;i < 5;i ){for(int j 0;j < i;j )printf("%-5d",a[i][j]);printf("\n");}return 0; } 运行截图…

深入理解JVM内存空间的担保策略

Java虚拟机&#xff08;JVM&#xff09;的内存管理是Java性能调优中最重要的方面之一&#xff0c;特别是在处理大型应用和服务时。JVM内存管理的一个关键组成部分是垃圾回收&#xff08;GC&#xff09;。在GC过程中&#xff0c;JVM需要确保有足够的内存来创建新对象&#xff0c…

STM32串口接收不定长数据(空闲中断+DMA)

玩转 STM32 单片机&#xff0c;肯定离不开串口。串口使用一个称为串行通信协议的协议来管理数据传输&#xff0c;该协议在数据传输期间控制数据流&#xff0c;包括数据位数、波特率、校验位和停止位等。由于串口简单易用&#xff0c;在各种产品交互中都有广泛应用。 但在使用串…

C陷阱与缺陷——第2章语法陷阱

1. 理解函数声明 硬件将调用首地址为0位置的子例程 (*(void(*)())0)(); 任何C变量的声明都由两部分组成&#xff1a;类型以及一组类似表达式的声明符&#xff0c;声明符从表面看与表达式有些类似&#xff0c;对它求值应该返回一个声明中给定类型的结果。 假定变量fp是一个函…

揭秘预付费电表怎么无线收费——方便快捷收费

【摘要】针对目前市场上普遍以Ic卡作为售电介质的预付费售电系统存在的问题&#xff0c;介绍了一种新型的无线预付费售电系统及其构成&#xff0c;并给出了整个系统设计的完整方案。整个系统包括用户终端和电力管理系统端&#xff0c;它们之间通过双工通信可以将用户用电信息和…

Kubernetes存储搭建NFS挂载失败处理

搞NFS存储时候发现如下问题&#xff1a; Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 5m1s default-scheduler Successful…

电子学会全国青少年软件编程等级考试 中小学生python一级历年真题解析【更新至2023年9月 持续更新】

中国电子学会python等级考试一级历年真题解析 一、考级知识点分析 一、 了解Python多种开发环境&#xff0c;熟练使用Python自带的IDLE开 发环境&#xff0c;能够进行程序编写、调试和分析&#xff0c;具备使用Python开发 环境进行程序设计的能力 了解Python常见的几种编程环…

堆排序详细解读

简介 堆排序是一种基于二叉堆数据结构的排序算法&#xff0c;它的特点是不同于传统的比较排序算法&#xff0c;它是通过建立一个堆结构来实现的。堆排序分为两个阶段&#xff0c;首先建立堆&#xff0c;然后逐步将堆顶元素与堆的最后一个元素交换并调整堆&#xff0c;使得最大…

EM32DX-C2【C#】

1说明&#xff1a; 分布式io&#xff0c;CAN总线&#xff0c;C#上位机二次开发&#xff08;usb转CAN模块&#xff09; 2DI&#xff1a; 公共端是&#xff1a; 0V【GND】 X0~X15&#xff1a;自带24v 寄存器地址&#xff1a;0x6100-01 6100H DI输入寄存器 16-bit &#x…

ROS2 galactic生成的bag包里的MarkerArray在humble下播放不正常

近期发现ROS2 galactic下生成的bag包在humble下回放时使用rviz可视化&#xff0c;bag里的点云可以正常看到&#xff0c;但是使用Marker和MarkerArray画的box却死活看不到&#xff0c;感觉很纳闷&#xff0c;看网上有人报告说foxy下生成的bag包在galactic下播放会报SQL错误&…

Redis部署-哨兵模式

目录 redis sentinel相关名词 redis sentinel架构 故障转移流程 基于docker搭建redis哨兵 准备工作 搭建过程 模拟主节点宕机,观察哨兵节点的工作流程 哨兵重新选取主节点的流程 1.主观下线 2.客观下线 3.哨兵节点推举出一个leader节点 4.leader选举完毕,leader挑选…

RflySim | 姿态控制器设计实验一

姿态控制器设计实验1 一. 姿态控制设计简介 本文是建立在多旋翼的姿态即控制器中的反馈信号能够被较好地估计的前提下&#xff0c;控制器中的反馈信号是估计值。不过&#xff0c;为了更加简便根据分离原理&#xff0c;我们用真值代替反馈信号。本文的目的是让多旋翼的姿态能够…

Linux入门攻坚——7、磁盘管理——文件系统挂载管理及RAID、LVM

已经安装文件系统的分区需要经过挂载才能使用。 一切文件系统的使用都是从根开始&#xff0c;根是文件系统的起始点。 计算机启动过程&#xff1a;加电自检——bootloader——kernel——rootfs——/sbin/init kernel第一步要加载根系统。 将额外文件系统与根文件系统某现存的…

用swig封装c++代码给python使用

前面我们用swig封装了c的代码给java使用&#xff1a; 如何用SWIG封装c接口给java使用&#xff1f;-CSDN博客 但是由于我们的代码写的太好了&#xff0c;python用户也想用&#xff0c;我们需要将c代码封装一下给python用户使用。 这种需求很常见吧。 现在AI动不动就是用pytho…

Python如何从文件中读取数据

从文件中读取数据 1. 读取整个文件 要读取文件&#xff0c;首先来创建一个文件&#xff1a; 然后打开并读取这个文件&#xff0c;再将其内容显示到屏幕上&#xff1a; file_reader.py with open(pi_digits.txt) as file_object:contents file_object.read()print(contents)…

代码随想录第二十一天(一刷C语言)|回溯算法组合

创作目的&#xff1a;为了方便自己后续复习重点&#xff0c;以及养成写博客的习惯。 一、回溯算法 1、种类 排列、组合、分割、子集、棋盘问题 2、回溯步骤 &#xff08;0&#xff09;回溯抽象 回溯法解决的问题均可以抽象为树形结构&#xff08;N叉树&#xff09; &…