Java框架--MyBatis
Java框架--MyBatis
简化数据库操作的持久层框架
中文手册:
https://mybatis.org/mybatis-3/zh/index.html
Maven仓库:
工作示意图
快速入门
代码参考gitee仓库,其中使用maven两个功能:继承父工程环境、创建配置资源拷贝
日志输出
日志文档:
https://mybatis.org/mybatis-3/zh/logging.html
配置日志:
https://mybatis.org/mybatis-3/zh/configuration.html#settings
框架核心原理图
- mybatis的核心配置文件
- mybatis-config.xml:进行全局配置,全局只能有一个这样的配置文件
- XxxMapper.xml配置多个SQL,可以有多个XxxMappe.xml配置文件
- 通过mybatis-config.xml配置文件得到SqlSessionFactory
- 通过SqlSessionFactory得到SqlSession,用SqlSession就可以操作数据了
- SqlSession底层是Executor(执行器),有2重要的实现类,有很多方法
- MappedStatement是通过XxxMapper.xml中定义,生成的statement对象
- 参数输入执行并输出结果集,无需手动判断参数类型和参数下标位置,且自动将结果集映射为Java对象
原生的API&注解的方式
原生API,直接使用API来操作数据(其实就是把底层的api直接使用,该配置的xml、接口也还是需要配置)
//=============add==================
sqlSession.insert("com.hspedu.mapper.MonsterMapper.addMonster",monster);
//=============del==================
sqlSession.delete("com.hspedu.mapper.MonsterMapper.delMonster",3);
//=============update==================
sqlSession.update("com.hspedu.mapper.MonsterMapper.updateMonster",monster);
注解的方式,可不再使用MonsterMapper.xml(但是仍需要在mybatis-config.xml注册含注解的类),但其实不推荐此方式,没有解耦
// 注册xml(原来)
<mapper resource="com.hspedu.mapper.MonsterMapper.xml"/>
// 注册注解类
<mapper class="com.hspedu.mapper.MonsterAnnotation"/>
注解类代码参考gitee仓库
配置文件
mybatis的核心配置文件(mybatis-config.xml),实现配置jdbc连接信息,注册mapper
文档地址:
https://mybatis.org/mybatis-3/zh/configuration.html
- settings全局参数定义
- typeAliases别名处理器
- typeHandlers类型处理器(Mybatis的映射基本已经满足,不太需要重新定义)
- environments环境
- 注册mapper: xml文件、注解类、url:外部路径(使用很少,不推荐)、package方式注册(当一个包下有很多的Mapper.xml文件和基于注解实现的接口时为了方便,我们可以以包方式进行注册)
映射文件
XxxxMapper.xml-SQL映射文件
文档地址:
https://mybatis.org/mybatis-3/zh/sqlmap-xml.html
常用的顶级元素:
按照应被定义的顺序列出
-
cache–该命名空间的缓存配置。
-
cache-ref–引用其它命名空间的缓存配置。
-
resultMap–描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
-
parameterType-将会传入这条语句的参数的类全限定名或别名
-
sql–可被其它语句引用的可重用语句块。
-
insert–映射插入语句。
-
update–映射更新语句。
-
delete–映射删除语句。
-
select–映射查询语句。
动态SQL语句
处理更复杂的查询业务需求
文档地址:
https://mybatis.org/mybatis-3/zh/dynamic-sql.html
常用标签
1.if[判断]
2.where[拼接where子句]
3.choose/when/otherwise[类似java的switch语句,注意是单分支]
4.foreach[类似in]
5.trim[替换关键字/定制元素的功能
6.set[在update的set中,可以保证进入set标签的属性被修改,而没有进入set的,保持原来的值]
使用案例代码请参考gitee仓库
映射关系1对1
关键代码(相互引用时注意sout输出代码会造成栈溢出)
//不推荐,使用联合查询
<resultMap id="PersonResultMap" type="Person">
<!--<result property="id" column="id"/>-->
<!--id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
1.property="id" 表示person 属性 id ,通常是主键
2.column="id" 表示对应表的字段
-->
<id property="id" column="id"/>
<result property="name" column="name"/>
<!--association – 一个复杂类型的关联
1. property="card" 表示 Person对象的 card 属性
2. javaType="IdenCard" 表示card 属性 的类型
3. column="id" 是从我们的 下面这个语句查询后返回的字段
SELECT * FROM `person`,`idencard` WHERE `person`.id=1
AND `person`.card_id = `idencard`.id
-->
<association property="card" javaType="IdenCard">
<result property="id" column="id"/>
<result property="card_sn" column="card_sn"/>
</association>
</resultMap>
<select id="getPersonById" parameterType="Integer"
resultMap="PersonResultMap">
SELECT * FROM `person`,`idencard` WHERE `person`.id = #{id}
AND `person`.card_id = `idencard`.id
</select>
================================================================================
//推荐方式,使用嵌套其他select方法
<resultMap id="PersonResultMap2" type="Person">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!--
1. mybatis第二种方式核心思想: 将这个多表联查,分解成单表操作 , 这样简洁,而且易于维护 ,推荐
2. 而且可以复用你已经写好的方法 -组合
3. property="card": 表示 Person对象的 card 属性
4. column="card_id" 这个是
SELECT * FROM `person` WHERE `id` = #{id} 返回的 字段 card_id 信息/数据
5. 返回的 字段 card_id 信息/数据 作为getIdenCardById入参, 来执行
-->
<association property="card" column="card_id"
select="com.hspedu.mapper.IdenCardMapper.getIdenCardById" />
</resultMap>
<select id="getPersonById2" parameterType="Integer" resultMap="PersonResultMap2">
SELECT * FROM `person` WHERE `id` = #{id}
</select>
映射关系1对多
关键代码(相互引用时注意sout输出代码会造成栈溢出)
<resultMap id="UserResultMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!--解读:因为pets属性是集合,因此这里需要是collection标签来处理
1. ofType="Pet" 指定返回的集合中存放的数据类型Pet
2. collection 表示 pets 是一个集合
3. property="pets" 是返回的user对象的属性 pets
4. column="id" SELECT * FROM `mybatis_user` WHERE `id` = #{id} 返回的id字段对应的值
-->
<collection property="pets" column="id" ofType="Pet"
select="com.hspedu.mapper.PetMapper.getPetByUserId"/>
</resultMap>
<select id="getUserById" parameterType="Integer" resultMap="UserResultMap">
SELECT * FROM `mybatis_user` WHERE `id` = #{id}
</select>
缓存
-
机制的目的是提高检索效率
-
缓存执行顺序:二级缓存 -> 一级缓存 -> DB数据库
-
不会出现二级缓存和一级缓存有重复数据的情况,因为二级是一级关闭后才有的数据
一级缓存
会话级别缓存,默认开启,当你重复查询相同内容时,不会再发出sql请求
存储的位置:SqlSession.executor.delegate.localCache.cache(HashMap类型)
失效分析:
- 关闭SqlSession会话(sqlSession.close() 调用后),此时如果配置二级缓存,一级缓存数据会放入到二级缓存
- 执行sqlSession.clearCache() 清除缓存
- 当同一个缓存对象被修改,该对象在一级缓存中会失效
二级缓存
全局级别缓存,针对不同会话都有效,会在日志中输出命中率:二级缓存返回数据次数/获取数据次数
开关使用:
- settings全局参数定义 开启 cacheEnabled(默认true开启)
- entity类实现序列号接口(serialize),因二级缓存可能使用序列号技术(看具体使用的技术)
- 在对应的XxxMapper.xml中设置二级缓存策略则开启:eviction回收策略、flushInterval时间间隔、size引用数目、readOnly只读优化(这样配置是Mybatis自带缓存产品)
- 对应的XxxMapper.xml中对应方法标签属性useCache(默认true开启)
EhCache-第三方缓存产品
纯Java的缓存框架,具有快速、精干等特点,可以利用Mybatis二级缓存的接口接入Mybatis框架使用
配置文档:https://www.cnblogs.com/zqyanywn/p/10861103.html
细节说明(Java Ehcache缓存的timeToIdleSeconds和timeToLiveSeconds区别):https://www.taobye.com/f/view-11-23.html
使用:
-
导入核心jar包
<dependencies> <!--核心库--> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache-core</artifactId> <version>2.6.11</version> </dependency> <!--引入需要使用的slf4j--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <!--引入mybatis整合ehcache库/jar--> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.1</version> </dependency> </dependencies>
-
配置相关缓存
mybatis-config.xml <settings> <!--默认开启,这里显式的配置--> <setting name="cacheEnabled" value="true"/> </settings> XxxMapper.xml 注意如果原来有配置二级缓存,记得注销原来的cache配置 <cache type="org.mybatis.caches.ehcache.EncacheCache"/>
-
加入ehcache配置文件,放入资源文件夹(运行时的类路径)--固定文件名--ehcache.xml
<?xml version="1.0" encoding="UTF-8"?> <ehcache> <!-- diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下: user.home – 用户主目录 user.dir – 用户当前工作目录 java.io.tmpdir – 默认临时文件路径 --> <diskStore path="java.io.tmpdir/Tmp_EhCache"/> <!-- defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。 --> <!-- name:缓存名称。 maxElementsInMemory:缓存最大数目 maxElementsOnDisk:硬盘最大缓存个数。 eternal:对象是否永久有效,一但设置了,timeout将不起作用。 overflowToDisk:是否保存到磁盘,当系统当机时 timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。 diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 clearOnFlush:内存数量最大时是否清除。 memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。 FIFO,first in first out,这个是大家最熟的,先进先出。 LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。 LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。 --> <defaultCache eternal="false" maxElementsInMemory="10000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="259200" memoryStoreEvictionPolicy="LRU"/> </ehcache>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?