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(./图…

CMMI认证有什么意义

什么是CMMI认证 CMMI&#xff08;Capability Maturity Model Integration&#xff09;即能力成熟度模型集成&#xff0c;是由美国卡内基梅隆大学软件工程研究所&#xff08;SEI&#xff09;开发的&#xff0c;用于评估和改善企业软件开发过程的一种标准。它主要关注软件开发过…

1、命名空间、C++的复合类型、缺省参数

命名空间 1、命名空间的定义 使用namespace定义&#xff0c;使用作用域限定符::访问 #include <iostream> namespace ICBC{int money 0;void save( int m){money m;} } int main( void ){ICBC::save( 100); std::cout << "工行卡余额&#xff1a;"…

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

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

javascript object转换成json格式

JavaScript中object与json的转换 1、JSON字符串: var str1 { "name": "cxh", "sex": "man" };2、JSON对象: var str2 { "name": "cxh", "sex": "man" };3、JSON字符串转换为JSON对象 要使…

判断是否存在重复的数

系列文章目录 进阶的卡莎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;在各种产品交互中都有广泛应用。 但在使用串…

Java中的锁

Java常见锁 【超全面】_java锁-CSDN博客 一文看懂Java中的锁 - 知乎 一文读懂 Java 中的各种锁 - 知乎 java中锁的分类总结_java string 锁-CSDN博客 https://www.cnblogs.com/hangychn/p/17184814.html https://www.cnblogs.com/durenniu/p/10949491.html 锁-Java - 知…

【LeeCode】链表总结

理论基础 链表的种类主要为&#xff1a;单链表&#xff0c;双链表&#xff0c;循环链表 链表的存储方式&#xff1a;链表的节点在内存中是分散存储的&#xff0c;通过指针连在一起。 链表是如何进行增删改查的。 数组和链表在不同场景下的性能分析。 经典题目 虚拟头节点 …

Passkey模式

以下内容参考自谷歌的代码&#xff1a; 原文地址 Passkey 模式用于以比简单地将其他类设置为友元类更细粒度的方式将类的部分方法暴露给另一个类。本质上&#xff0c;它涉及创建一个“passkey”类&#xff0c;该类只能由特定的其他类构造&#xff0c;并要求在调用您希望限制使…

手把手教你做基于stm32的红外、语音、按键智能灯光控制(下)

目录&#xff1a; 4.6. DHT11温湿度传感器模块4.7. 语音识别模块4.7. OLED显示屏模块 5. 不同的工作模式6. 总结 在接着&#xff08;上&#xff09;写之前&#xff0c;首先来看一下效果&#xff1a; 链接: link 需要源码什么的可以私信我哦 4.6. DHT11温湿度传感器模块 这个…

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

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

[go 面试] 构建高效微服务通信:选择合适的通信方式

关注公众号【爱发白日梦的后端】分享技术干货、读书笔记、开源项目、实战经验、高效开发工具等&#xff0c;您的关注将是我的更新动力&#xff01; 构建分布式系统或微服务架构时&#xff0c;服务间通信成为至关重要的一环。不同的通信方式各有优劣&#xff0c;因此在选择时需根…

数据库sql是什么?

数据库是什么&#xff1f; 为了将数据持久保存&#xff0c;java中的对象都是在内存中&#xff0c;程序结束数据就会销毁&#xff0c;在学习IO过程中将数据存储在文件中&#xff0c;但是内容整体是字符串&#xff0c;使用起来不方便。所以需要数据库长久保存数据还便于提取 数…

值和引用的传递与初始化块

值&#xff1a;1.基本类型数据&#xff1b;2.对象的地址 引用&#xff1a;传句柄的地址 在java中只有按值传递没有按引用传递&#xff0c;c语言也只有按值传递&#xff0c;只有c才有引用传递。 块&#xff1a; 静态块>块>构造方法 package com;public class Person {pri…

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

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

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

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