Notes-Mybatis中的数据读写-《Java笔记》

admin 2025-10-19 04:05:22 编程 来源:ZONE.CI 全球网 0 阅读模式

Mybatis

数据读写的本质

不管是哪种ORM框架,数据读写其本质都是对JDBC的封装,其目的主要都是简化JDBC的开发流程,进而让开发人员更关注业务。下面是JDBC的核心流程:

  1. 注册 JDBC 驱动(Class.forName(“XXX”);)
  2. 打开连接(DriverManager.getConnection(“url”,”name”,”password”))
  3. 根据连接,创建 Statement(conn.prepareStatement(sql))
  4. 设置参数(stmt.setString(1, “wyf”);)
  5. 执行查询(stmt.executeQuery();)
  6. 处理结果,结果集映射(resultSet.next())
  7. 关闭资源(finally)

Mybatis也是在对JDBC进行封装,它将注册驱动打开连接交给了数据库连接池来负责,Mybatis支持第三方数据库连接池,也可以使用自带的数据库连接池;创建 Statement执行查询交给了StatementHandler来负责;设置参数交给ParameterHandler来负责;最后两步交给了ResultSetHandler来负责。

Executor内部运作过程

测试方法org.apache.ibatis.binding.BindingTest#shouldFindThreeSpecificPosts

下面是一个简单的数据读写过程,在宏观上先来了解一下每一个组件在整个数据读写上的作用:Mybatis的数据读写主要是通Excuter来协调StatementHandler、ParameterHandler和ResultSetHandler三个组件来实现的:

  • StatementHandler:它的作用是使用数据库的Statement或PrepareStatement执行操作,启承上启下作用;
  • ParameterHandler:对预编译的SQL语句进行参数设置,SQL语句中的的占位符“?”都对应BoundSql.parameterMappings集合中的一个元素,在该对象中记录了对应的参数名称以及该参数的相关属性
  • ResultSetHandler:对数据库返回的结果集(ResultSet)进行封装,返回用户指定的实体类型; StatementHandlerStatementHandler类图 RoutingStatementHandler通过StatementType来创建StatementHandler,使用静态代理模式来完成方法的调用,主要起到路由作用。它是Excutor组件真正实例化的组件。public%20class%20RoutingStatementHandler%20implements%20StatementHandler%20{
  • %20%20/**%20*%20静态代理模式%20*/%20%20private%20final%20StatementHandler%20delegate;%20%20public%20RoutingStatementHandler(Executor%20executor,%20MappedStatement%20ms,%20Object%20parameter,%20RowBounds%20rowBounds,%20ResultHandler%20resultHandler,%20BoundSql%20boundSql)%20{%20%20%20%20%20%20//%20根据{@link%20org.apache.ibatis.mapping.StatementType}%20来创建不同的实现类%20(策略模式)%20%20%20%20%20%20switch%20(ms.getStatementType())%20{%20%20%20%20%20%20%20%20%20%20case%20STATEMENT:%20%20%20%20%20%20%20%20%20%20%20%20%20%20delegate%20=%20new%20SimpleStatementHandler(executor,%20ms,%20parameter,%20rowBounds,%20resultHandler,%20boundSql);%20%20%20%20%20%20%20%20%20%20%20%20%20%20break;%20%20%20%20%20%20%20%20%20%20case%20PREPARED:%20%20%20%20%20%20%20%20%20%20%20%20%20%20delegate%20=%20new%20PreparedStatementHandler(executor,%20ms,%20parameter,%20rowBounds,%20resultHandler,%20boundSql);%20%20%20%20%20%20%20%20%20%20%20%20%20%20break;%20%20%20%20%20%20%20%20%20%20case%20CALLABLE:%20%20%20%20%20%20%20%20%20%20%20%20%20%20delegate%20=%20new%20CallableStatementHandler(executor,%20ms,%20parameter,%20rowBounds,%20resultHandler,%20boundSql);%20%20%20%20%20%20%20%20%20%20%20%20%20%20break;%20%20%20%20%20%20%20%20%20%20default:%20%20%20%20%20%20%20%20%20%20%20%20%20%20throw%20new%20ExecutorException("Unknown%20statement%20type:%20"%20+%20ms.getStatementType());%20%20%20%20%20%20}%20%20}%20%20@Override%20%20public%20Statement%20prepare(Connection%20connection,%20Integer%20transactionTimeout)%20throws%20SQLException%20{%20%20%20%20%20%20return%20delegate.prepare(connection,%20transactionTimeout);%20%20}%20%20...%20%20} BaseStatementHandler所有子类的抽象父类,定义了初始化statement的操作顺序,由子类实现具体的实例化不同的statement(模板模式)。@Overridepublic%20Statement%20prepare(Connection%20connection,%20Integer%20transactionTimeout)%20throws%20SQLException%20{%20%20ErrorContext.instance().sql(boundSql.getSql());%20%20Statement%20statement%20=%20null;%20%20try%20{%20%20%20%20%20%20//%20实例化Statement(由子类实现)【模板方法+策略模式】%20%20%20%20%20%20statement%20=%20instantiateStatement(connection);%20%20%20%20%20%20//%20设置超时时间%20%20%20%20%20%20setStatementTimeout(statement,%20transactionTimeout);%20%20%20%20%20%20//%20设置获取数据记录条数%20%20%20%20%20%20setFetchSize(statement);%20%20%20%20%20%20return%20statement;%20%20}%20catch%20(SQLException%20e)%20{%20%20%20%20%20%20closeStatement(statement);%20%20%20%20%20%20throw%20e;%20%20}%20catch%20(Exception%20e)%20{%20%20%20%20%20%20closeStatement(statement);%20%20%20%20%20%20throw%20new%20ExecutorException("Error%20preparing%20statement.%20%20Cause:%20"%20+%20e,%20e);%20%20}}protected%20abstract%20Statement%20instantiateStatement(Connection%20connection)%20throws%20SQLException; instantiateStatement()就是一个模板方法,由子类实现。 SimpleStatementHandler使用JDBCStatement执行模式,不需要做参数处理,源码如下:@Overrideprotected%20Statement%20instantiateStatement(Connection%20connection)%20throws%20SQLException%20{%20%20//%20实例化Statement%20%20if%20(mappedStatement.getResultSetType()%20==%20ResultSetType.DEFAULT)%20{%20%20%20%20%20%20return%20connection.createStatement();%20%20}%20else%20{%20%20%20%20%20%20return%20connection.createStatement(mappedStatement.getResultSetType().getValue(),%20ResultSet.CONCUR_READ_ONLY);%20%20}}@Overridepublic%20void%20parameterize(Statement%20statement)%20{%20%20//%20N/A%20%20//%20使用Statement是直接执行sql%20所以没有参数} PreparedStatementHandler使用JDBCPreparedStatement预编译执行模式。@Overrideprotected%20Statement%20instantiateStatement(Connection%20connection)%20throws%20SQLException%20{%20%20//%20实例化PreparedStatement%20%20String%20sql%20=%20boundSql.getSql();%20%20if%20(mappedStatement.getKeyGenerator()%20instanceof%20Jdbc3KeyGenerator)%20{%20%20%20%20%20%20String[]%20keyColumnNames%20=%20mappedStatement.getKeyColumns();%20%20%20%20%20%20if%20(keyColumnNames%20==%20null)%20{%20%20%20%20%20%20%20%20%20%20return%20connection.prepareStatement(sql,%20PreparedStatement.RETURN_GENERATED_KEYS);%20%20%20%20%20%20}%20else%20{%20%20%20%20%20%20%20%20%20%20return%20connection.prepareStatement(sql,%20keyColumnNames);%20%20%20%20%20%20}%20%20}%20else%20if%20(mappedStatement.getResultSetType()%20==%20ResultSetType.DEFAULT)%20{%20%20%20%20%20%20return%20connection.prepareStatement(sql);%20%20}%20else%20{%20%20%20%20%20%20return%20connection.prepareStatement(sql,%20mappedStatement.getResultSetType().getValue(),%20ResultSet.CONCUR_READ_ONLY);%20%20}}@Overridepublic%20void%20parameterize(Statement%20statement)%20throws%20SQLException%20{%20%20//%20参数处理%20%20parameterHandler.setParameters((PreparedStatement)%20statement);} CallableStatementHandler使用JDBCCallableStatement执行模式,用来调用存储过程。现在很少用。 ParameterHandler主要作用是给PreparedStatement设置参数,源码如下:@Overridepublic%20void%20setParameters(PreparedStatement%20ps)%20{%20%20ErrorContext.instance().activity("setting%20parameters").object(mappedStatement.getParameterMap().getId());%20%20//%20获取参数映射关系%20%20List<ParameterMapping>%20parameterMappings%20=%20boundSql.getParameterMappings();%20%20if%20(parameterMappings%20!=%20null)%20{%20%20%20%20%20%20//%20循环获取处理参数%20%20%20%20%20%20for%20(int%20i%20=%200;%20i%20<%20parameterMappings.size();%20i++)%20{%20%20%20%20%20%20%20%20%20%20//%20获取对应索引位的参数%20%20%20%20%20%20%20%20%20%20ParameterMapping%20parameterMapping%20=%20parameterMappings.get(i);%20%20%20%20%20%20%20%20%20%20if%20(parameterMapping.getMode()%20!=%20ParameterMode.OUT)%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20Object%20value;%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20获取参数名称%20%20%20%20%20%20%20%20%20%20%20%20%20%20String%20propertyName%20=%20parameterMapping.getProperty();%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20判断是否是附加参数%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(boundSql.hasAdditionalParameter(propertyName))%20{%20//%20issue%20#448%20ask%20first%20for%20additional%20params%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20value%20=%20boundSql.getAdditionalParameter(propertyName);%20%20%20%20%20%20%20%20%20%20%20%20%20%20}%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20判断是否是没有参数%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20if%20(parameterObject%20==%20null)%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20value%20=%20null;%20%20%20%20%20%20%20%20%20%20%20%20%20%20}%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20判断参数是否有相应的%20TypeHandler%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20if%20(typeHandlerRegistry.hasTypeHandler(parameterObject.getClass()))%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20value%20=%20parameterObject;%20%20%20%20%20%20%20%20%20%20%20%20%20%20}%20else%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20以上都不是,通过反射获取value值%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20MetaObject%20metaObject%20=%20configuration.newMetaObject(parameterObject);%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20value%20=%20metaObject.getValue(propertyName);%20%20%20%20%20%20%20%20%20%20%20%20%20%20}%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20获取参数的类型处理器%20%20%20%20%20%20%20%20%20%20%20%20%20%20TypeHandler%20typeHandler%20=%20parameterMapping.getTypeHandler();%20%20%20%20%20%20%20%20%20%20%20%20%20%20JdbcType%20jdbcType%20=%20parameterMapping.getJdbcType();%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(value%20==%20null%20&&%20jdbcType%20==%20null)%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20jdbcType%20=%20configuration.getJdbcTypeForNull();%20%20%20%20%20%20%20%20%20%20%20%20%20%20}%20%20%20%20%20%20%20%20%20%20%20%20%20%20try%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20根据TypeHandler设置参数%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20typeHandler.setParameter(ps,%20i%20+%201,%20value,%20jdbcType);%20%20%20%20%20%20%20%20%20%20%20%20%20%20}%20catch%20(TypeException%20|%20SQLException%20e)%20{%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20throw%20new%20TypeException("Could%20not%20set%20parameters%20for%20mapping:%20"%20+%20parameterMapping%20+%20".%20Cause:%20"%20+%20e,%20e);%20%20%20%20%20%20%20%20%20%20%20%20%20%20}%20%20%20%20%20%20%20%20%20%20}%20%20%20%20%20%20}%20%20}}
  1. 获取参数映射关系
  2. 获取参数名称
  3. 根据参数名称获取参数值
  4. 获取参数的类型处理器
  5. 根据TypeHandler设置参数值

通过上面的流程可以发现,真正设置参数是由TypeHandler来实现的。

TypeHandler

Mybatis基本上提供了所需要用到的所有TypeHandler,当然也可以自己实现。TypeHandler的主要作用是:

  1. 设置PreparedStatement参数值
  2. 获取查询结果值public%20interface%20TypeHandler<T>%20{
  3. %20/***%20给{@link%20PreparedStatement}设置参数值**%20@param%20ps%20%20%20%20%20%20%20%20{@link%20PreparedStatement}*%20@param%20i%20%20%20%20%20%20%20%20%20参数的索引位*%20@param%20parameter%20参数值*%20@param%20jdbcType%20%20参数类型*%20@throws%20SQLException*/%20void%20setParameter(PreparedStatement%20ps,%20int%20i,%20T%20parameter,%20JdbcType%20jdbcType)%20throws%20SQLException;%20/***%20根据列名获取结果值**%20@param%20columnName%20Colunm%20name,%20when%20configuration%20<code>useColumnLabel</code>%20is%20<code>false</code>*/%20T%20getResult(ResultSet%20rs,%20String%20columnName)%20throws%20SQLException;%20/***%20根据索引位获取结果值*/%20T%20getResult(ResultSet%20rs,%20int%20columnIndex)%20throws%20SQLException;%20/***%20获取存储过程结果值*/%20T%20getResult(CallableStatement%20cs,%20int%20columnIndex)%20throws%20SQLException;}

TypeHandler的本质就是对JDBC中stmt.setString(1,%20"wyf");resultSet.getString("name")的封装,JDBC完整代码可以查看JDBC%20面试要点。

ResultSetHandler

ResultSetHandler主要作用是:对数据库返回的结果集(ResultSet)进行封装,通过通过ResultMap配置和反射完成自动映射,返回用户指定的实体类型;核心思路如下:

  1. 根据RowBounds做分页处理
  2. 根据ResultMap配置的返回值类型和constructor配置信息实例化目标类
  3. 根据ResultMap配置的映射关系,获取到TypeHandler,进而从ResultSet中获取值
  4. 根据ResultMap配置的映射关系,获取到目标类的属性名称,然后通过反射给目标类赋值

源码太多了,这里就不发了,有兴趣就在下面的源码上看注释吧,下面的流程图会画出方法的调用栈。

Mybatis自带的RowBounds分页是逻辑分页,数据量大了有可能会内存溢出,所以不建议使用Mybatis默认分页。

数据读取流程图

总结

Mybatis的整个数据读取流程其实就是对JDBC的一个标准实现。

以太坊cppgolang区别 编程

以太坊cppgolang区别

以太坊是一种去中心化的开源平台,它采用智能合约技术,旨在构建和运行不受干扰的分布式应用程序。作为目前最受欢迎的区块链平台之一,以太坊提供了多种编程语言的支持,其
progolang 编程

progolang

Go语言(Golang)是由Google开发的一门静态类型编程语言。作为一名专业的Golang开发者,我深知这门语言的优势和特点。在本文中,我将介绍Golang
golangn个发送者 编程

golangn个发送者

Golang是一种开源的编程语言,由Google团队开发,旨在提高程序的并发性和简化软件开发过程。在Go语言中,有时需要向多个接收者发送信息。本文将介绍如何在G
golang技能图谱 编程

golang技能图谱

从互联网行业的快速发展到人工智能技术的日益成熟,各种编程语言也应运而生。而在这众多的编程语言中,Golang(即Go)作为一门强大且高效的开发语言备受关注。Go
评论:0   参与:  8