MyBatis
一、什么是MyBatis
MyBatis 是一款优秀的持久层框架 , 用于简化 JDBC 开发
MyBatis 本是 Apache 的一个开源项目 iBatis, 2010 年这个项目由 apache software foundation 迁移到了 google code, 并且改名为 MyBatis 。 2013 年 1 1 月迁移到 Github
官网 : mybatis – MyBatis 3 | 简介
**持久层** - 负责将数据到保存到数据库的那一层代码 - JavaEE 三层架构表现层业务层 、 持久层 **框架** - 框架就是一个半成品软件 , 是一套可重用的 、 通用的 、 软件基础代码模型 - 在框架的基础之上构建软件编写更加高效 、 规范 、 通用 、 可扩展
MyBatis的作用
简化硬编码,自动完成繁琐操作
二、MyBatis入门
-
导入坐标
<!-- mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <!-- slf4j日志api--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.25</version> </dependency> -
编写mybatis-config.xml配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!-- 数据库的连接信息--> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <!-- 加载sql的映射文件--> <mapper resource="usermapper.xml"/> </mappers> </configuration> -
编写表映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace:名称空间--> <mapper namespace="usermapper"> <select id="selectAll" resultType="com.model.User"> select * from user </select> </mapper> -
构建SqlSessionFactory
//加载mybatis的核心配置文件,获取SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //获取sqlSession对象,用于执行sql SqlSession sqlSession = sqlSessionFactory.openSession(); //执行sql List<User> users = sqlSession.selectList("usermapper.selectAll"); System.out.println(users); //释放资源 sqlSession.close();
三、Mapper代理开发
目的:
- 解决原生方式的硬编码 - 简化后期执行sql
使用步骤:
-
定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录(创建mapper包,并在resource下创建com/mapper/目录)
-
设置SQL映射文件的namespace属性为Mapper接口全限定名
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace:名称空间--> <mapper namespace="com.mapper.UserMapper"> <select id="selectAll" resultType="com.model.User"> select * from user </select> </mapper> -
在mybatis-config.xml配置文件里加载sql映射文件
<mappers> <!-- 加载sql的映射文件--> <!-- <mapper resource="usermapper.xml"/>--> <package name="com.mapper"/> </mappers> -
在Mapper接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致
/** * 查询用户列表 * * @return 返回用户列表 */ List<User> selectAll(); -
编码
//加载mybatis的核心配置文件,获取SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //获取sqlSession对象,用于执行sql SqlSession sqlSession = sqlSessionFactory.openSession(); //获取UserMapper接口的单例对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> userList = userMapper.selectAll(); System.out.println(userList); //释放资源 sqlSession.close();
四、MyBatis核心配置文件
MyBatis的配置文件包含了会深深影响MyBatis行为的设置和属性信息,配置文档的顶层结构如下:
-
configuration(配置)
-
properties(属性)
-
settings(设置)
-
typeAliases(类型别名)
-
typeHandlers(类型处理器)
-
objectFactory(对象工厂)
-
plugins(插件)
-
environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
-
databaseIdProvider(数据库厂商标识)
-
mappers(映射器)
-
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 给实体类起别名并使用包扫描--> <typeAliases> <package name="com.model"/> </typeAliases> <!-- 环境配置 environments:配置数据库连接信息,可以配置多个环境,通过default属性切换成不同的数据源--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!-- 数据库的连接信息--> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <!-- 加载sql的映射文件--> <!-- <mapper resource="usermapper.xml"/>--> <package name="com.mapper"/> </mappers> </configuration>
五、处理字段不一致
1. 对表字段起别名
-
直接在sql上写
<select id="selectAll" resultType="Brand"> select id, brand_name as brandName, company_name as companyName, ordered, description, status from tb_brand; </select> 缺点:每次查询都需要定义别名
-
使用sql片段
<!-- sql片段--> <sql id="brand_select"> id, brand_name as brandName, company_name as companyName, ordered, description, status </sql> <select id="selectAll" resultType="Brand"> select # 通过id引入sql片段 <include refid="brand_select"></include> from tb_brand; </select> 缺点:不灵活
2. 使用resultMap
<!-- id:唯一标识 type:映射的类型,支持别名--> <resultMap id="brandResultMap" type="Brand"> <!-- id:完成主键字段的映射 result:完成一般字段的映射 column:表的列名 property:实体类的属性--> <result column="brand_name" property="brandName"/> <result column="company_name" property="companyName"/> </resultMap> <select id="selectAll" resultMap="brandResultMap"> select * from tb_brand; </select>
六、使用配置文件开发
1. 参数占位符
-
#{}
# 会将其替换为?,可以防止sql注入 -
${}
# 直接拼接sql,存在sql注入问题
使用时机:
- 参数传递时使用#{}
- 表名列名不固定时使用${}
2. 特殊字符处理
-
转义字符
<!--小于--> < -
CDATA区
<![CDATA[ ]]>
3. 参数的设置
Mapper:
<select id="selectByCondition" resultMap="brandResultMap"> select * from tb_brand where status = #{status} and company_name like #{companyName} and brand_name like #{brandName} </select>
参数:
int status = 1; String companyName = "华为"; String brandName = "华为"; companyName = "%" + companyName + "%"; brandName = "%" + brandName + "%";
3-1. 散装参数
如果方法中有多个参数,需要使用@Param注解
/** * 条件查询(散装参数) * * @param status 传入状态 * @param companyName 传入企业名称 * @param brandName 传入产品名称 * @return 返回商品列表 */ List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName, @Param("brandName") String brandName); ----------------------------------------------- System.out.println(sqlSession.getMapper(BrandMapper.class).selectByCondition(status, companyName, brandName));
3-2. 对象参数
对象的属性名称要和参数占位符名称一致
/** * 条件查询(对象参数) * * @param brand 传入品牌对象 * @return 返回商品列表 */ List<Brand> selectByCondition(Brand brand); ---------------------------------- Brand brand = new Brand(); brand.setStatus(status); brand.setCompanyName(companyName); brand.setBrandName(brandName); System.out.println(sqlSession.getMapper(BrandMapper.class).selectByCondition(brand));
3-3. Map集合参数
Map集合的键要和参数占位符名称保持一致
/** * 条件查询(Map集合) * * @param map 传入Map的键 * @return 返回商品列表 */ List<Brand> selectByCondition(Map map); ------------------------------------ Map<String, Object> map = new HashMap<>(); map.put("status", status); map.put("companyName", companyName); map.put("brandName", brandName); System.out.println(sqlSession.getMapper(BrandMapper.class).selectByCondition(map));
4.动态SQL
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
4-1. 多条件查询
4-1-1. <if>
条件判断 test属性:写逻辑表达式
<select id="selectByCondition" resultMap="brandResultMap"> select * from tb_brand where <if test="status != null"> status = #{status} </if> <if test="companyName != null and companyName != ''"> and company_name like #{companyName} </if> <if test="brandName != null and brandName != ''"> and brand_name like #{brandName} </if> </select>
问题:直接使用上面的方法可能造成sql语法错误,解决方法:
- 恒等式 1=1
- <where>
4-1-2. <where>
解决<if>标签可能造成的sql语法错误
<select id="selectByCondition" resultMap="brandResultMap"> select * from tb_brand <where> <if test="status != null"> status = #{status} </if> <if test="companyName != null and companyName != ''"> and company_name like #{companyName} </if> <if test="brandName != null and brandName != ''"> and brand_name like #{brandName} </if> </where> </select>
4-2. 单条件查询
4-2-1. <choose>
选择分支,类似于java的switch语句
<choose>相当于switch <when>相当于case <otherwise>相当于default
<select id="selectByConditionSingle" resultMap="brandResultMap"> select * from tb_brand <where> <choose> <when test="status != null"> status = #{status} </when> <when test="companyName != null and companyName != ''"> and company_name like #{companyName} </when> <when test="brandName != null and brandName != ''"> and brand_name like #{brandName} </when> </choose> </where> </select>
5. 添加、修改
5-1. 添加
<insert id="add"> insert into tb_brand (brand_name, company_name, ordered, description, status) values (#{brandName},#{companyName},#{ordered},#{description},#{status}); </insert>
注意点:
MyBatis默认关闭了自动提交,需要自己手动提交或在获取sqlSession对象时开启
//获取sqlSession对象 //true:开启自动提交,false:关闭自动提交;默认为false SqlSession sqlSession = sqlSessionFactory.openSession(true); --------------------------------- //手动提交 sqlSession.commit();
5-1-1. 主键返回
使用主键返回
- 设置useGeneratedKeys的属性值为true
- Property的属性值为主键对应的实体属性名称
<insert id="add" useGeneratedKeys="true" keyProperty="id"> insert into tb_brand (brand_name, company_name, ordered, description, status) values (#{brandName},#{companyName},#{ordered},#{description},#{status}); </insert>
5-2.修改
5-2-1. 修改全部字段
<update id="update"> update tb_brand set brand_name = #{brandName}, company_name = #{companyName}, ordered = #{ordered}, description = #{description}, status = #{status} where id = #{id}; </update>
5-2-2. 修改指定字段
使用<set>和<if>标签判断哪些值不为空
<update id="update"> update tb_brand <set> <if test="brandName != null and brandName != ''"> brand_name = #{brandName}, </if> <if test="companyName != null and companyName != ''"> company_name = #{companyName}, </if> <if test="ordered != null"> ordered = #{ordered}, </if> <if test="description != null and description != ''"> description = #{description}, </if> <if test="status != null"> status = #{status} </if> </set> where id = #{id}; </update>
6.删除
6-1. 批量删除
使用<foreach>标签遍历需要删除的id,即遍历id数组
/** * 批量删除 * * @param id 传入id数组 */ void deleteByIds(@Param("ids") int[] id);
<delete id="deleteByIds"> delete from tb_brand where id in # collection:需要遍历的数组名称 # item:需要遍历的键 # separator:分隔符 # open:开始 # close:结束 <foreach collection="ids" item="id" separator="," open="(" close=")"> #{id} </foreach> </delete>
注意:
-
mybatis会将数组参数,封装为一个Map集合
-
默认为:array = 数组
-
可以使用@Param注解改变Map集合的默认key的名称
6-2. 删除指定
<delete id="deleteById"> delete from tb_brand where id = #{id}; </delete>
7.参数传递
在进行参数传递时,mybatis会使用ParamNameResolver类将参数封装
注意:使用@Param注解,替换Map集合默认的arg键名
MyBatis 参数封装 :
-
单个参数 :
-
Model类型 :
直接使用属性名(要保证属性名和参数占位符名称一致)
-
Map集合 :
直接使用键名(要保证键名和参数占位符名称一致)
-
CotIection :封装为Map集合
map.put("arg0",CotIection集合); map.put("cotIection",CotIection集合); -
List :封装为Map集合
map.put("arg0",List集合); map.put("cotIection",CotIection集合); map.put("list",List集合); -
Array :封装为Map集合
map.put("arg0",数组); map.put("array",数组); -
其他类型 :直接使用
-
-
多个参数 : 封装为Map集合
map.put("arg0",参数1); | map.put("param1",参数1); map.put("arg1", 参数2); | map.put("param2", 参数2);
七、使用注解开发
使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java注解不仅力不从心,还会让你本就复杂的SQL语句更加混乱不堪。因此,如果你需要做一些很复杂的操作最好
用XML来映射语句
选择何种方式来配置映射,以及认为是否应该要统一映射语句定义的形式,完全取决于你和你的团队。换句话说,永远不要拘泥于一种方式,你可以很轻松在基于汪解和XML的语句映射方式间自由自移植和切换
# 查询 : @Select # 添加 : @lnsert # 修改 : @Update # 删除 : @Delete
提示:
- 注解完成简单功能
- 配置文件完成复杂功能
八、MyBatis的缓存机制
1. 概述
Mybatis缓存机制是指Mybatis框架提供的一种提高查询效率的功能,它可以将经常查询的数据存储在内存中,减少和数据库的交互次数;Mybatis缓存机制分为一级缓存和二级缓存
一级缓存:
一级缓存是SqlSession级别的缓存,也称为本地缓存;它默认开启,只对当前SqlSession有效;当SqlSession执行增删改操作或者关闭时,一级缓存会失效
注意:执行insert、update、delete操作会刷新缓存
二级缓存:
二级缓存是mapper级别的缓存,它是基于namespace的缓存,一个命名空间(namespace)对应一个二级缓存,也称为全局缓存。它需要手动开启和配置,可以跨SqlSession共享数据。二级缓存可以自定义实现Cache接口来扩展
2. 如何开启和配置二级缓存
要开启和配置二级缓存,需要做以下几个步骤:
-
1.在全局配置文件mybatis-config.xml中设置cacheEnabled为true,这是开启二级缓存的前提。
<!--显式的开启二级缓存--> <settings> <setting name="cacheEnabled" value="true"/> </settings> -
2.在mapper.xml或mapper.java的命名空间中添加<cache/>标签,这是开启二级缓存的必要条件。
<!-- 开启二级缓存 readOnly:表示缓存以只读的形式保存,当有新的缓存进入的时候,会直接删除旧的缓存,如果不配置这个属性,会导致一个报错。 flushInterval:刷新间隔,以毫秒为单位。即缓存的刷新间隔,默认不刷新 size:引用数目,默认为1024 eviction:回收策略,有四个值,分别对应四种不同的缓存回收策略, 默认是LRU,即移除最长时间不使用的缓存。 其次是FIFO,按照缓存进入的时间移除最早进入的缓存。--> <cache readOnly="true"/> -
3.让返回的Model(POJO)实现Serializable接口(序列化),这是使用二级缓存的要求。
如果需要自定义缓存实现,可以在<cache/>标签中指定type属性为自定义的Cache接口实现类。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通