概念
- MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。
- MyBatis 消除了几乎所有的 JDBC 代码和参数手工的设置以及结果集的检索。
- 中文官网: https://mybatis.net.cn/
优点
- 简化JDBC的开发,且支持各种Sql的代码
- 能够更好的完成ORM(对象关系映射)
- 提供缓存的技术,提高了程序查询的效率
- 减少了和数据库的交互次数,相同的数据不会在次访问数据库了(缓存)
- 自动完成ORM的映射(ORM:类和表的关系、属性和字段名的关系 不同类型系统的数据之间的转换)
ORM概念
是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换,在MyBatis里是对:
- 类和表之间数据互相对应转换的关系(类对应表)
- 属性和字段名类型数据的互相对应转换的关系(字段名对应属性名)
运行原理
缓存
MyBatis中默认事务是关闭的,如果对数据库进行了修改,则需要主动提交事务,否则数据库无法保存缓存的数据
- MyBatis中当第一次查询数据时,缓存中肯定没有,它直接会去查询数据库,然后还会把数据存入缓存
- 第二次查询相同的数据时,先去查缓存,有就直接用,没有再去查数据库
- MyBatis中默认是开启缓存的
使用缓存的前提是:
- 必须基于相同的数据
- 同一个SqlSession对象
MyBatis的两个配置文件
核心配置文件:包含数据源、事务、映射文件的配置
mybatis-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- mybatis的核心配置文件 -->
<configuration>
<environments default="test">
<environment id="test">
<!--使用什么技术访问数据库-->
<transactionManager type="JDBC"></transactionManager>
<!--指定数据源的节点-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=Asia/Shanghai"/>
<property name="username" value="数据库账号"/>
<property name="password" value="数据库密码"/>
</dataSource>
</environment>
</environments>
<!-- MyBatis可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃 -->
<!-- databaseIdProvider的type属性是必须的,不配置时会报错。DB_VENDOR这个属性值使用的是VendorDatabaseIdProvider类的别名 -->
<databaseIdProvider type="DB_VENDOR">
<!--property子元素是配置一个数据库,其中的name属性是数据库名称,value是我们自定义的别名,通过别名我们可以在SQL语句中标识适用于哪种数据库运行。-->
<property name="MySQL" value="mysql" />
<!-- 通过<select>等等标签添加databaseId="mysql"即可 -->
<property name="Oracle" value="oracle" />
</databaseIdProvider>
<!--·用来引入映射文件,底层维护了MapperRegistry,用来存各科Mapper文件-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
<mapper resource="mappers/DeptMapper.xml"/>
</mappers>
</configuration>
映射文件: 里面有大量的SQL
XXX Mapper.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--映射文件,用来写大量SQL语句-->
<mapper namespace="cn.learning.mybatis.dao.DeptDao">
<!--Id是每条数据的唯一标识-->
<!--resultType属性用来完成ORM,把表里每个字段的值自动映射给类里的属性 -->
<select id="getAll" resultType="类的全路径">
select * from dept
</select>
<select id="getById" resultType="类的全路径">
select * from dept where id = 1
</select>
<insert id="addOne">
insert into dept values(11,'全栈','三区')
</insert>
</mapper>
当SQL中有特殊字符,mybatis不能正常解析时可以使用该方法:
<![CDATA[
and age<=#{age}
]]>
xml中&是用:&
编译时自动转换成&
Mybatis面向接口编程时注意事项
- 需要在dao层创建表的对应类,且类名须和表名相同,参数与属性相同(大小写忽视)
- 需要在dao层创建该实体类的接口实现程序所需要的增删改查功能,需要继承Mapper接口
- Mapper.xml配置中mapper节点下的namespace属性必须和对应接口包的全路径相同
- 对数据库操作的节点如:select或insert等:id属性必须和接口中方法名相同
- Sql语句最后千万不能加分号 因为版本冲突严重(mysql可以, oraclet不可以)
注意好以上几点,Mybatis就可以自动完成对接口的映射,实现该接口的虚拟子类
Mybatis框架会在内存里面根据接口类型自动创建出一个接口的子类
Mybatis使用动态SQL
使用动态SQL先要定义XXXMapper.xml中查询节点下的SQL语句
<select id="getById" resultType="cn.learning.mybatis.dao.impl.Dept">
select * from dept where id = #{id}
select * from dept where id = ${id}
</select>
- 以上两种方式#{}和${}都可以达到动态查询的效果
- {}内的值必须和定义的Dao接口中的方法参数名字相同(映射的方法参数名相同)
#{id}与${id}实现相同的功能,但区别是:
强烈推荐#{}赋值方式:
使用#{parameterName}引用参数的时候,Mybatis会把这个参数认为是一个字符串例如:
Select * from emp where name = #{Smith}
使用时会自动转换: Select * from emp where name =‘Smith’
使用#{}可以从对象中获取指定的属性的值 ,有预编译的效果防止SqL注入攻击
使用#{}的好处
- 这种写法可以防止SQL注入,而且会自动拼接字符串,高效安全避免了SQL攻击问题
- #{}的参数还可以自动转换类型,无需担心数据类型的问题
能不就不用${}赋值:
使用${parameterName}引用参数的时,Mybatis会把这个参数认为是一个数字例如:
Select * from emp where name = ${employeeName}
使用时会自动转换:Select * from emp where name = Smith
使用${}的缺点
- 存在SQL注入问题,如果字段是varchar类型直接抛出SQL异常。
- 从安全性上考虑,能使用#尽量使用#来传参,因为这样可以有效防止SQL注入的问题。
Mybytis实现表主键自动回显
使用场景
当多表关联时,往往会在第一张表插入数据后,需要用该表中id与第二张表的id关联。
由于id是主键自增,我们无法获取到第一张表中新插入的id,所以使用Mybytis的主键自动回显功能
设置主键自动回显
在Mybytis的Mapper.xml配置映射关系时加入如下属性:
<insert id="xxxx" useGeneratedKeys="true" keyProperty="类的属性id" keyColumn="数据库字段id">
</insert>
useGeneratedKeys: true 表示开启Mybytis主键自动回显功能
keyProperty: 设置类中对应数据表中的主键变量名
keyColumn: 设置数据库的主键字段名\
当调用该插入的方法成功后,mybytis会自动注入到该对象主键的值即可调用
扩展:MyBatis Plus中Mapper接口已经实现了主键自动回显无需配置
ResultMap和resultType的区别
一般条件下,单表查询都使用resultType,多表关联查询时使用resultMap
resultType:
- 完成ORM的映射关系,并且把结果集转换成一个resultType指定的对象实例
- 但注意当字段名与属性名无法相同时必须使用ResultMap属性
ResultMap:
- 用于对复杂对象结构时,对应的ResultMap结构名称。
- 如数据库表的字段无法与类中成员属性名相同时使用
MyBatis提供了SQL标签
if / where / set / foreach
<!--where语法-->
<select id="getById" resultType="Car">
select * from car where
<where>
id = #{id}
</where>
</select>
<!--if语法-->
<select id="getByName" resultType="Car">
select * from car
<where>
<if test="name ==值">
name = #{name}
</if>
</where>
</select>
<update id="updateUser_if_set" parameterType="com.pojo.User">
UPDATE user
<set>
<if test="username!= null and username != '' ">
username = #{username},
</if>
<if test="sex!= null and sex!= '' ">
sex = #{sex},
</if>
<if test="birthday != null ">
birthday = #{birthday},
</if>
</set>
WHERE user_id = #{userid};
</update>
<!--foreach语法-->
<select id="getbyids" resultType="Car">
select * from car where id in
<foreach collection="" open="(" close=")" separator="," item="i">
#{i}
</foreach>
</select>
为什么要使用<if><where>
呢?
首先使用标签方式写入SQL
XML代码片段
<mapper namespace="cn.learning.dao.DeptDao">
<select id="getAll" resultType="cn.learning.mybatis.dao.impl.Dept">
select * from dept
<where>
<if test="dname !=null">
dname = #{dname}
</if>
and
<if test="loc !=null">
loc = #{loc}
</if>
</where>
</select>
</mapper>
JAVA代码片段
List<Dept> allList = deptDao.getAll(null,"二区");
Mybatis生成SQL语句
select * from dept WHERE loc = ?
查询结果
Dept{id=2, dname='哈哈哈哈', loc='二区'}
Dept{id=3, dname='operations', loc='二区'}
使用SQL语法方式写入:
XML代码片段
<mapper namespace="cn.learning.mybatis.dao.DeptDao">
<select id="getAll" resultType="cn.learning.mybatis.dao.impl.Dept">
select * from dept where dname = #{dname} and loc = #{loc}
</select>
</mapper>
java代码片段
List<Dept> allList = deptDao.getAll(null,"二区");
Mybatis生成SQL语句
select * from dept where dname = ? and loc = ?
查询结果
null(未查询到任何数据)
使用标签和不使用标签的总结:
使用标签:
如果SQL传参为空会自动把为空的SQL参数名以及参数和and或者Or删除只拼接有参数的数据
使用标签时面对复杂的业务时须要使用的
不使用标签
如果SQL传参为空:不会删除任何sql语句,而是在为空的参数后面=null and …
所以如果想使用动态sql语句,必须要使用标签的方式
SpringBoot整合Mybytis
- SpringBoot核心配置需要配置(驱动/配置文件目录/URL/账号/密码/XML文件目录/开启驼峰映射映射)
- 设置Dao的接口能被Mybatis读取到(接口中:@Mapper或者启动类@MapperScan(“Dao的全路径”)选择一种配置就行
- 一个Dao的接口须实现一个Mapper.xml文件(配置好 接口 / 接口方法 / 实体类 / 之间的映射),Mybytis会在内存自动实现实体类
- 调用时成员属性引用Dao的接口,用@Autowired会自动注入
Spring整合Mybytis的运行逻辑
- SprinaBoot程序启动时会加载pom.xml文件中指定的iar包
- 文件根据开箱用的原则则开始执行Mybatis代码
- SpringBoot程序加载Mybatis的iar包文件之后,通过yml配置文件实现数据的填充配置Mybaits相关信息
- 被SpringBoot整合后为简化代码结构Spring动态的为Mybatis的接口创建代理对象(启动类须加入@MapperScan…)
- 根据原有接口的模型,在运行期,通过Mapper.xml文件动态映射创建了一个一模一样功能的实例化子对象
- 通过注解@Autowired自动注入到指定引用对象上
Mybytis-SQL的两种写法方式
- 将所有的Sql语句都写到xml 映射文件中 :是万能的操作
- 将SQL通过注解的方式标识在接口方法上(只适用于简单操作)
通过注解一般都是操作简单的数据查询
如果遇到关联查询/复杂Sql则使用Mapper映射文件的方式 更加通用
JDBC和MyBatis的区别?
JDBC是java提供了一套专门用于和数据库对接的api,java.sql.*,其规范了如何和数据库进行对接,实现由各数据库厂商进行各自的实现和扩展。学习JDBC重点在学习如何使用其api。
MyBatis框架是轻量级封装了JDBC,我们已经看不到这些api,连接connection、语句preparedstatement、结果集ResultSet,而关注的是mybatis框架体系如何去使用,一旦写好,我们关注的是java对象。
XML和接口方式的区别?
MyBatis提供了两种操作数据库的方式,一种是通过xml映射文件,一种是通过java的接口类。按面向对象方式更加推荐接口方式,但如果复杂的多表映射,仍然需要使用xml映射文件的ResultMap方式实现。
接口只是假象,其底层仍然是通过xml实现,好不容易实现了一套方式,怎忍丢掉呢?可以做个测试就知道它底层怎么实现的?把xml中的sql删除,它就玩不转了。
SQL优化
与数据库交互的频次太多,会导致DB服务器性能出现问题
尽量避免一个请求业务须多次与数据库交互的操作来完成业务需求