Mybatis
MyBatis是什么
Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,加载驱动、创建连接、创建statement等繁杂的过程,开发者开发时只需要关注如何编写SQL语句,可以严格控制sql执行性能,灵活度高
作为一个半ORM框架,MyBatis 可以使用 XML 或注解来配置和映射原生信息
为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?
Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的
而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。Mybatis 直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高
mongodb 中的 mongoose,也算是一个全自动 ORM,需要定义 Schema 和 Model
JDBC编程有哪些不足之处,MyBatis是如何解决这些问题的
1、SQL 语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
解决:将SQL语句配置在XXXXmapper.xml文件中与java代码分离
2、 向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应
解决: Mybatis自动将java对象映射至sql语句
3、 对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便
解决:Mybatis自动将sql执行结果映射至java对象
MyBatis编程步骤是什么样的?
- 创建
UserMapper.xml
SQL 映射文件 和UserMapper.java
接口类 - 创建 SqlSessionFactory
- 通过 SqlSessionFactory 创建 SqlSession
- 通过 SqlSession 获取 mapper
- 通过 mapper 执行数据库操作
- 调用 session.commit() 提交事务
- 调用 session.close() 关闭会话
#{}和${}的区别
#{}:占位符,被替换为 ?,相当于用 JDBC中的 PerparedStatement 的 set 方法赋值,能够防止 SQL 注入
?{}:拼接符,字符串替换,相当于 JDBC 中的 Statement,没有预编译处理,存在 SQL 注入的风险
Dao接口的工作原理是什么?能重载接口中的方法吗
Dao接口即Mapper接口。接口的全类名就是映射文件中的 namespace
的值
接口的方法名,就是映射文件中Mapper的Statement的id值
接口方法内的参数,就是传递给sql的参数
Mapper接口是没有实现类的,当调用接口方法时,接口全类名+方法名的拼接字符串作为key值,可唯一定位一个MapperStatement
Dao接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略
Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后将sql执行结果返回
Mybatis动态sql是做什么的?都有哪些动态sql
Mybatis动态sql可以在xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值完成逻辑判断,并动态拼接sql的功能
Mybatis提供了9种动态sql标签:where、set、foreach、if、choose、when、otherwise
<select id="selectByCondition" resultMap="brandResultMap">select *from tb_brand<where><if test="status != null">and 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>
xml映射文件中,不同的xml映射文件id是否可以重复?
不同的xml映射文件,如果配置了namespace,那么id可以重复
如果没有配置namespace,那么id不能重复
原因是 namespace+id
是作为 Map<String,MapperStatement>
的key使用的
使用MyBatis的Mapper接口调用时有哪些要求?
1、Mapper接口方法名和mapper.xml中定义的每个sql的id相同
2、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
3、Mapper.xml文件中的namespace即是mapper接口的类路径
在Mapper中如何传递多个参数
1、使用@Param注解:在Dao层的参数中前加@Param注解,注解内的参数名为传递到Mapper中的参数名
2、多个参数封装成Map,以HashMap的形式传递到Mapper中
3、实体类对象传参
<select id="selectById" resultMap="brandResultMap">select * from tb_brand where id=#{id}</select>
Brand selectById(int id);
<select id="selectByCondition" resultMap="brandResultMap">select *from tb_brandwherestatus = #{status}and company_name like #{companyName}and brand_name like #{brandName}</select>
List<Brand> selectByCondition(@Param("status")int status, @Param("companyName")String companyName, @Param("brandName")String brandName);
List<Brand> selectByCondition(Brand brand);
List<Brand> selectByCondition(Map map);
MyBatis 的执行流程
首先加载 mybatis-config.xml
映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><typeAliases><package name="com.itheima.pojo"/></typeAliases><!--environments:配置数据库连接环境信息。可以配置多个environment,通过default属性切换不同的environment--><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><!--数据库连接信息--><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://192.168.93.12/itcast?useSSL=false"/><property name="username" value="root"/><property name="password" value=""/></dataSource></environment></environments><mappers><!--加载sql映射文件--><mapper resource="UserMapper.xml"/></mappers></configuration>
Mybatis是否支持延迟加载
支持,但是默认没有开启
什么叫延迟加载:
如果不在业务代码中调用 user.getOrderList()
,就不会去查询订单表
局部开启延迟加载:在 resultMap
标签中设置 fetchType
属性
<resultMap><collection property="orderList" ofType="order" select="" fetchType="lazy"></collection>
</resultMap>
全局开启延迟加载:在 mybatis-config.xml
配置文件中的 settings
标签中增加对应的配置:
<settings><setting name="lazyLoadingEnabled" value="true">
</settings>
MyBatis 的一级和二级缓存
1,对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了新增、修改、删除操作后,默认该作用域下所有 select 中的缓存将被 clear
2,二级缓存需要缓存的数据实现Serializable接口
3,只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中