
一、mybatis的工作原理:
MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。
MyBatis使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Ordinary Java Objects,普通的 Java对象)映射成数据库中的记录。
每个MyBatis应用程序主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得。用xml文件构建SqlSessionFactory实例是非常简单的事情。
推荐在这个配置中使用类路径资源,但可以使用任何Reader实例,包括用文件路径或file://开头的url创建的实例。MyBatis有一个实用类----Resources,它有很多方法,可以方便地从类路径及其它位置加载资源。
二、使用mybatis的原因:因为mybatis具有许多的优点,具体如下:
1、简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
2、灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足 *** 作数据库的所有需求。
3、解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
4、提供映射标签,支持对象与数据库的orm字段关系映射。
5、提供对象关系映射标签,支持对象关系组建维护。
6、提供xml标签,支持编写动态sql。
扩展资料:
mybatis的功能构架:
1、API接口层:提供给外部使用的接口API,开发人员通过这些本地API来 *** 纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
2、数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库 *** 作。
3、基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
参考资料来源:百度百科-MyBatis
参考资料来源:百度百科-MyBatis从入门到精通
创建数据库
选择开始菜单中→程序→Management SQL Server 2008→SQL Server Management Studio命令,打开SQL Server Management Studio窗口,并使用Windows或 SQL Server身份验证建立连接。
在对象资源管理器窗口中展开服务器,然后选择数据库节点
右键单击数据库节点,从d出来的快捷菜单中选择新建数据库命令。
执行上述 *** 作后,会d出新建数据库对话框。在对话框、左侧有3个选项,分别是常规、选项和文件组。完成这三个选项中的设置会后,就完成了数据库的创建工作,
在数据库名称文本框中输入要新建数据库的名称。例如,这里以“新建的数据库”。
在所有者文本框中输入新建数据库的所有者,如sa。根据数据库的使用情况,选择启用或者禁用使用全文索引复选框。
在数据库文件列表中包括两行,一行是数据库文件,而另一行是日记文件。通过单击下面的添加、删除按钮添加或删除数据库文件。
切换到选项页、在这里可以设置数据库的排序规则、恢复模式、兼容级别和其他属性。
切换到文件组页,在这里可以添加或删除文件组。
完成以上 *** 作后,单击确定按钮关闭新建数据库对话框。至此“新建的数据”数据库创建成功。新建的数据库可以再对象资源管理器窗口看到。
学习了Spring之后,我们已经了解如何将一个类作为Bean交由IoC容器管理,也就是说,现在我们可以通过更方便的方式来使用Mybatis框架,我们可以直接把SqlSessionFactory、Mapper交给Spring进行管理,并且可以通过注入的方式快速地使用它们。
因此,我们要学习一下如何将Mybatis与Spring进行整合,那么首先,我们需要在之前知识的基础上继续深化学习。
在之前,我们如果需要创建一个JDBC的连接,那么必须使用 DriverManagergetConnection() 来创建连接,连接建立后,我们才可以进行数据库 *** 作。
而学习了Mybatis之后,我们就不用再去使用 DriverManager 为我们提供连接对象,而是直接使用Mybatis为我们提供的 SqlSessionFactory 工具类来获取对应的 SqlSession 通过会话对象去 *** 作数据库。
那么,它到底是如何封装JDBC的呢?我们可以试着来猜想一下,会不会是Mybatis每次都是帮助我们调用 DriverManager 来实现的数据库连接创建?我们可以看看Mybatis的源码:
在通过 SqlSessionFactory 调用 openSession 方法之后,它调用了内部的一个私有的方法 openSessionFromDataSource ,我们接着来看,这个方法里面定义了什么内容:
也就是说,我们的数据源配置信息,存放在了 Transaction 对象中,那么现在我们只需要知道执行器到底是如何执行SQL语句的,我们就知道到底如何创建 Connection 对象了,就需要获取数据库的链接信息了,那么我们来看看,这个 DataSource 到底是个什么:
我们发现,它是在 javaxsql 定义的一个接口,它包括了两个方法,都是用于获取连接的。因此,现在我们可以断定,并不是通过之前 DriverManager 的方法去获取连接了,而是使用 DataSource 的实现类来获取的,因此,也就正式引入到我们这一节的话题了:
数据库链接的建立和关闭是极其耗费系统资源的 *** 作,通过DriverManager获取的数据库连接,
一个数据库连接对象均对应一个物理数据库连接,每次 *** 作都打开一个物理连接,使用完后立即关闭连接,频繁的打开、关闭连接会持续消耗网络资源,造成整个系统性能的低下。
因此,JDBC为我们定义了一个数据源的标准,也就是 DataSource 接口,告诉数据源数据库的连接信息,并将所有的连接全部交给数据源进行集中管理,当需要一个 Connection 对象时,可以向数据源申请,数据源会根据内部机制,合理地分配连接对象给我们。
一般比较常用的 DataSource 实现,都是采用池化技术,就是在一开始就创建好N个连接,这样之后使用就无需再次进行连接,而是直接使用现成的 Connection 对象进行数据库 *** 作。
当然,也可以使用传统的即用即连的方式获取 Connection 对象,Mybatis为我们提供了几个默认的数据源实现,我们之前一直在使用的是官方的默认配置,也就是池化数据源:
一共三个选项:
那么我们先来看看,不使用池化的数据源实现,它叫做 UnpooledDataSource ,我们来看看源码:
首先这个类中定义了很多的成员,包括数据库的连接信息、数据库驱动信息、事务相关信息等。
我们接着来看,它是如何实现 DataSource 中提供的接口的:
实际上,这两个方法都指向了内部的一个 doGetConnection 方法,那么我们接着来看:
首先它将数据库的连接信息也给添加到 Properties 对象中进行存放,并交给下一个 doGetConnection 来处理,套娃就完事了呗,接着来看下一层源码:
到这里,就返回 Connection 对象了,而此对象正是通过 DriverManager 来创建的,因此,非池化的数据源实现依然使用的是传统的连接创建方式,那我们接着来看池化的数据源实现,它是 PooledDataSource 类:
我们发现,在这里的定义就比非池化的实现复杂得多了,因为它还要考虑并发的问题,并且还要考虑如何合理地存放大量的链接对象,该如何进行合理分配,因此它的玩法非常之高级。
首先注意,它存放了一个UnpooledDataSource,此对象是在构造时就被创建,其实创建Connection还是依靠数据库驱动创建,我们后面慢慢解析,首先我们来看看它是如何实现接口方法的:
可以看到,它调用了 popConnection() 方法来获取连接对象,然后进行了一个代理,我们可以猜测,有可能整个连接池就是一个类似于栈的集合类型结构实现的。那么我们接着来看看 popConnection 方法:
经过上面一顿猛如虎的 *** 作之后,我们可以得到以下信息:
如果最后得到了连接对象(有可能是从空闲列表中得到,有可能是直接创建的新的,还有可能是经过回收策略回收得到的)。
那么连接(Connection)对象一定会被放在活跃列表中(stateactiveConnections)
那么肯定有一个疑问,现在我们已经知道获取一个链接会直接进入到活跃列表中,那么,如果一个连接被关闭,又会发生什么事情呢,我们来看看此方法返回之后,会调用 getProxyConnection 来获取一个代理对象,实际上就是 PooledConnection 类:
它直接代理了构造方法中传入的Connection对象,也是使用JDK的动态代理实现的,那么我们来看一下,它是如何进行代理的:
那么我们最后再来看看 pushConnection 方法:
这样,我们就已经完全了解了Mybatis的池化数据源的执行流程了。
只不过,无论Connection管理方式如何变换,无论数据源再高级,我们要知道,它都最终都会使用 DriverManager 来创建连接对象,而最终使用的也是 DriverManager 提供的 Connection 对象。
通过了解数据源,我们已经清楚,Mybatis实际上是在使用自己编写的数据源(数据源有很多,之后我们再聊其他的)默认使用的是池化的数据源,它预先存储了很多的连接对象。
那么我们来看一下,如何将Mybatis与Spring更好的结合呢,比如我们现在希望将SqlSessionFactory交给IoC容器进行管理,而不是我们自己创建工具类来管理(我们之前一直都在使用工具类管理和创建会话)
首先导入依赖:
在mybatis-spring依赖中,为我们提供了SqlSessionTemplate类,它其实就是官方封装的一个工具类,我们可以将其注册为Bean,这样我们随时都可以向IoC容器索要,而不用自己再去编写一个工具类了,我们可以直接在配置类中创建:
最后成功得到Student实体类,证明 SqlSessionTemplate 成功注册为Bean可以使用了。
虽然这样已经很方便了,但是还不够方便,我们依然需要手动去获取Mapper对象,那么能否直接得到对应的Mapper对象呢,我们希望让Spring直接帮助我们管理所有的Mapper,当需要时,可以直接从容器中获取,我们可以直接在配置类上方添加注解:
这样,Spring会自动扫描所有的Mapper,并将其实现注册为Bean,那么我们现在就可以直接通过容器获取了:
请一定注意,必须存在 SqlSessionTemplate 或是 SqlSessionFactoryBean 的Bean,否则会无法初始化(毕竟要数据库的链接信息)
我们接着来看,如果我们希望直接去除Mybatis的配置文件,那么改怎么去实现呢?
我们可以使用 SqlSessionFactoryBean 类:
首先我们需要创建一个数据源的实现类,因为这是数据库最基本的信息,然后再给到 SqlSessionFactoryBean 实例,这样,我们相当于直接在一开始通过IoC容器配置了 SqlSessionFactory ,只需要传入一个 DataSource 的实现即可。
删除配置文件,重新再来运行,同样可以正常使用Mapper。
从这里开始,通过IoC容器,Mybatis已经不再需要使用配置文件了,之后基于Spring的开发将不会再出现Mybatis的配置文件。
两种方式:
方式1:假设bean的属性xxx为主键,则在getxxx()
前添加以下注解
@id
@sequencegenerator(name="名称a",
sequencename="库中已存在的sequence名称",allocationsize=递增值)
@generatedvalue(strategy=generationtypesequence,
generator="名称a")
方式2:假设bean的属性xxx为主键,则在getxxx()
前添加以下注解
@id
@tablegenerator(name="名称a",allocationsize=递增值)//若不指定递增值,则生成的主键值不一定连续
@generatedvalue(strategy=generationtypetable,
generator="名称a")
1、批量插入 *** 作
mapperjava层定义:
int batchInsert(List stockList);
mapperxml层的sql语句:
insert into t_stock (status, asset_classify_id,asset_id,asset_item_id, name,num, batch_num, tag_id,rfid, epc, barcode,qr_code, erp, unit,pic_url, specification, model,material, color,length,width, height, weight,density, volume, price01,price02,warehouse_id, storage_zone_id,storage_location_id,storage_location_tag_id,remark, attr01, attr02,attr03, create_date,last_update,creater, client_id)values (#{itemstatus,jdbcType=VARCHAR},#{itemassetClassifyId,jdbcType=BIGINT},#{itemassetId,jdbcType=BIGINT}, #{itemassetItemId,jdbcType=BIGINT},#{itemname,jdbcType=VARCHAR},#{itemnum,jdbcType=VARCHAR},#{itembatchNum,jdbcType=VARCHAR}, #{itemtagId,jdbcType=VARCHAR},#{itemrfid,jdbcType=VARCHAR}, #{itemepc,jdbcType=VARCHAR},#{itembarcode,jdbcType=VARCHAR},#{itemqrCode,jdbcType=VARCHAR},#{itemerp,jdbcType=VARCHAR}, #{itemunit,jdbcType=VARCHAR},#{itempicUrl,jdbcType=VARCHAR},#{itemspecification,jdbcType=VARCHAR},#{itemmodel,jdbcType=VARCHAR},#{itemmaterial,jdbcType=VARCHAR},#{itemcolor,jdbcType=VARCHAR}, #{itemlength,jdbcType=DECIMAL},#{itemwidth,jdbcType=DECIMAL}, #{itemheight,jdbcType=DECIMAL},#{itemweight,jdbcType=DECIMAL},#{itemdensity,jdbcType=DECIMAL},#{itemvolume,jdbcType=DECIMAL}, #{itemprice01,jdbcType=DECIMAL},#{itemprice02,jdbcType=DECIMAL},#{itemwarehouseId,jdbcType=BIGINT},#{itemstorageZoneId,jdbcType=BIGINT},#{itemstorageLocationId,jdbcType=BIGINT},#{itemstorageLocationTagId,jdbcType=BIGINT},#{itemremark,jdbcType=VARCHAR}, #{itemattr01,jdbcType=VARCHAR},#{itemattr02,jdbcType=VARCHAR},#{itemattr03,jdbcType=VARCHAR},#{itemcreateDate,jdbcType=TIMESTAMP},#{itemlastUpdate,jdbcType=TIMESTAMP},#{itemcreater,jdbcType=BIGINT}, #{itemclientId,jdbcType=BIGINT})
mysql分库分表一般有如下场景
其中1,2相对较容易实现,本文重点讲讲水平拆表和水平拆库,以及基于mybatis插件方式实现水平拆分方案落地。
在 《聊一聊扩展字段设计》 一文中有讲解到基于KV水平存储扩展字段方案,这就是非常典型的可以水平分表的场景。主表和kv表是一对N关系,随着主表数据量增长,KV表最大N倍线性增长。
这里我们以分KV表水平拆分为场景
对于kv扩展字段查询,只会根据id + key 或者 id 为条件的方式查询,所以这里我们可以按照id 分片即可
分512张表(实际场景具体分多少表还得根据字段增加的频次而定)
分表后表名为kv_000 ~ kv_511
id % 512 = 1 分到 kv_001,
id % 512 = 2 分到 kv_002
依次类推!
水平分表相对比较容易,后面会讲到基于mybatis插件实现方案
场景:以下我们基于博客文章表分库场景来分析
目标:
表结构如下(节选部分字段):
按照user_id sharding
假如分1024个库,按照user_id % 1024 hash
user_id % 1024 = 1 分到db_001库
user_id % 1024 = 2 分到db_002库
依次类推
目前是2个节点,假如后期达到瓶颈,我们可以增加至4个节点
最多可以增加只1024个节点,性能线性增长
对于水平分表/分库后,非shardingKey查询首先得考虑到
基于mybatis分库分表,一般常用的一种是基于spring AOP方式, 另外一种基于mybatis插件。其实两种方式思路差不多。
为了比较直观解决这个问题,我分别在Executor 和StatementHandler阶段2个拦截器
实现动态数据源获取接口
测试结果如下
由此可知,我们需要在Executor阶段 切换数据源
对于分库:
原始sql:
目标sql:
其中定义了三个注解
@useMaster 是否强制读主
@shardingBy 分片标识
@DB 定义逻辑表名 库名以及分片策略
1)编写entity
Insert
select
以上顺利实现mysql分库,同样的道理实现同时分库分表也很容易实现。
此插件具体实现方案已开源: >
MyBatis-plus是完全基于MyBatis开发的一个增强工具,是在MyBatis的基础上做增强的框架,为简化开发、提高效率而生。
它在MyBatis原本的框架上增加了很多实用性功能,比如乐观锁插件、字段自动填充功能、分页插件、条件构造器、sql 注入器等等。使用 MyBatis-plus 可以完全不写任何 XML 文件,直接使用继承了BaseMapper 接口的类对象完成对数据库的映射 *** 作
基于映射的原理,MyBatis-plus 必然要实现 Mapper中的方法与 SQL 语句的对应转化,以下即为 MyBatis-plus 重要流程图例
1在 MyBatis-plus 中, MybatisPlusAutoConfiguration 自动配置类的 sqlSessionFactory() 方法为 Spring提供创建 sqlSession 的工厂类对象,对 sqlSessionFactory 进行定义的定义类变为了 MybatisSqlSessionFactoryBean 。
在 sqlSessionFactory() 方法中,除了注入 MyBatis本身的组件,还会注入MyBatis-plus 的 主键生成器、SQL 注入器等组件,最后通过 MybatisSqlSessionFactoryBean#getObject() 方法获取到 sqlSessionFactory 对象
2 MybatisSqlSessionFactoryBean#getObject() 执行懒加载策略,最后通过 buildSqlSessionFactory() 方法创建 SqlSessionFactory 工厂类对象。这个方法的流程很长,不过大致可以分为两个步骤:
3 MybatisXMLConfigBuilder#parse() 会去解析配置文件,最后会调用到其内部方法 mapperElement() 。这个方法完成解析 Mapper工作,并将其添加到配置类 MybatisConfiguration 中
4 MybatisConfiguration#addMapper() 方法其实是去调用 MybatisMapperRegistry#addMapper() 方法,其核心是 MybatisMapperAnnotationBuilder#parse()
5 MybatisMapperAnnotationBuilder#parse() 方法真正开始完成 Mapper 接口中的方法与 SQL 语句的映射,其中 parseStatement() 方法是解析 @Select/@Update 等注解写入的 SQL语句,而代码 GlobalConfigUtilsgetSqlInjector(configuration)inspectInject(assistant, type ) 通过 MaBatis-plus的 SQL 注入器完成 Mapper 方法与 SQL 语句的转化
6 AbstractSqlInjector#inspectInject() 会完成 BaseMapper 接口中提供的通用方法对应的 SQL 语句准备,这部分主要通过 AbstractMethod#inject() 方法完成
7 AbstractMethod#inject() 方法并没有什么特别的 *** 作,只是调用其子类实现 injectMappedStatement() 方法。以 SelectOne#injectMappedStatement() 为例,其 SQL 语句的核心在于 SqlMethod 类,这个枚举类中缓存了可以动态拼接的 SQL 语句脚本,只需要填上参数 format 就可以得到 SQL 语句的执行脚本。
以上过程结束,只需要将所有信息通过 addInsertMappedStatement() 方法封装成 MappedStatement 对象并将其加入到容器中,这样 Mapper接口方法调用时,就可以通过 动态代理 的方式找到其对应执行的 SQL 脚本,至此 SQL 语句准备及配置解析就完成了。
最后拼接的 SQL 语句 脚本形式如下示例,实际执行数据库 *** 作时会解析这个脚本完成变量替换,从而得到可执行的 SQL 语句
8 SqlSessionFactory 对象的创建需要回到 MybatisSqlSessionFactoryBean#buildSqlSessionFactory() 方法中,很容易追踪到 MybatisSqlSessionFactoryBuilder#build() 方法,最后其实是通过 SqlSessionFactoryBuilder#build() 方法创建了一个 DefaultSqlSessionFactory 对象返回
1 @MapperScan 注解通过 @Import(MapperScannerRegistrarclass) 引入扫描注册的类 MapperScannerRegistrar ,该类实现了 ImportBeanDefinitionRegistrar 接口并重写 registerBeanDefinitions() 方法,在该方法中注册了 MapperScannerConfigurer 类
2 MapperScannerConfigurer 是 Mapper接口的扫描配置类,实现了 BeanDefinitionRegistryPostProcessor 接口,其 postProcessBeanDefinitionRegistry() 方法会在容器启动过程中被回调,通过 ClassPathMapperScanner#scan() 方法完成 Mapper 的扫描注册
3 ClassPathMapperScanner#processBeanDefinitions() 将扫描到的 Mapper接口生成的对应 BeanDefinition 的 beanClass 属性替换为 MapperFactoryBean ,这样每次获取 Mapper 实例实际是通过 MapperFactoryBean 的实例去获取
此处体现了 FactoryBean 的定位,即用于获取同一类 bean 的工厂 bean。
4 @Autowired 自动注入 Mapper 触发容器获取 bean 的方法,调用到 MapperFactoryBean#getObject() 方法,最终调用到 sqlSessionTemplate#getMapper() 方法
5MyBatis-plus 使用的配置类是 MybatisConfiguration ,最终调用到 MybatisMapperRegistry#getMapper() 方法,这里就进入了动态代理获取 MapperProxy 实例的流程
6 MybatisMapperProxyFactory#newInstance() 方法给自动注入返回一个 MybatisMapperProxy 代理对象
7调用 Mapper 接口的方法触发代理对象的 MybatisMapperProxy#invoke() ,此时根据 Mapper 对象被调用的方法生成 MybatisMapperMethod 对象,通过 MybatisMapperMethod#execute() 去真正地执行 SQL 语句,从而完成数据库 *** 作。
以上就是关于mybatis工作原理及为什么要用全部的内容,包括:mybatis工作原理及为什么要用、maven搭建spring mvc mybatis 是怎样连接数据库的、深入Mybatis框架等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)