目录
六、动态sql:if choose trim foreach
Q:Mybatis Mapper 接口没有实现类,怎么实现的动态代理?
Q: 为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?
Mybatis是什么?
MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架,被称为半自动化的ORM(Object Relational Mapping)框架
ORM:
查询结果不同语言之间映射 JDBCTpye - properties - JAVAType
一、原理
推荐文章:
JDBC连接的5中方法
JDBC的五种连接方式_DeepMost_的博客-CSDN博客_jdbc连接
Mybatis的JDBC连接
1.1 加载Mapper配置的4中方式
public class MybatisTest {
public static void main(String[] args) throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = factory.openSession();
//传统
String name = "tom";
List<Map> list1 = sqlSession.selectList("com.demo.mapper.UserMapper.getUserByName",name);
//这里不再调用SqlSession 的api,而是获得了接口对象,调用接口中的方法。
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<Map> list2 = mapper.getUserByName("tom");
}
}
1.2 三种ExcutorType操作
ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情。它为每个语句的执行创建一个新的预处理语句。(默认)
ExecutorType.REUSE: 这个执行器类型会复用预处理语句。
ExecutorType.BATCH: 这个执行器会批量执行所有更新语句,如果 SELECT 在它们中间执行还会标定它们是 必须的,来保证一个简单并易于理解的行为。
二、缓存机制
先判断是否有二级缓存->一级缓存->数据库
2.1 一级缓存
SqlSession级别的缓存,默认开启 classCacheEnabled = true;
一次会话中的多次相同操作,会先从缓存获取,如果执行了增、删、改会清楚缓存。
2.2 二级缓存
默认是关闭的,二级缓存的对象必须实现序列化接口
开启方法如下:
1、可以通过配置文件开启 核心配置文件mybatis-config.xml
<setting name="cacheEnabled" value="true"/>
2、映射Mapper文件全局
eviction:回收策略 LRU FIFO SOFT WEAK
<cache/> <!-- 本映射文件中的全部查询将开启二级缓存!-->
3、注解的方式实现二级缓存
@CacheNamespace //对注解下的接口中的全部查询实现二级缓存
4、mapper文件夹中select标签useCache
<select id="selectByPrimaryKey" useCache="true">
二级缓存什么时候用?
使用二级缓存时需要想好两个问题:
1)对该表的操作与查询都在同一个namespace下,其他的namespace如果有操作,就会发生数据的脏读。
2)对关联表的查询,关联的所有表的操作都必须在同一个namespace。
举个简单的例子:
订单和订单详情,orderMapper、orderDetailMapper。在查询订单详情时我们需要把订单信息也查询出来,那么这个订单详情的信息被二级缓存在orderDetailMapper的namespace中,这个时候有人要修改订单的基本信息,那就是在orderMapper的namespace下修改,他是不会影响到orderDetailMapper的缓存的,那么你再次查找订单详情时,拿到的是缓存的数据,这个数据其实已经是过时的。
三、Dao
3.1 反射
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class c1 = Class.forName("com.example.demo.reflect.ReflectVO");
Method sayHi = c1.getMethod("sayHi", new Class[] {String.class});
sayHi.invoke(c1.newInstance(),new Object[] { new String("周杰伦")});
// //第二种
// ReflectVO p = new ReflectVO();
// Class c2 = p.getClass();
// //第三种
// Class c3 = ReflectVO.class;
}
}
3.2 JDK动态代理模式
抽象类能否 JDK 动态代理, 说代码前结论先行,不能!
例子:
public interface Subject { String sayHello(); } public class SubjectImpl implements Subject { @Override public String sayHello() { System.out.println(" Hello World"); return "success"; } } /** * @author 10450 * @description 动态代理调用处理器 * @date 2021/12/21 13:49 */ public class ProxyInvocationHandler implements InvocationHandler { private Object target; public ProxyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(" 进入代理调用处理器 "); return method.invoke(target, args); } } public class ProxyMain { public static void main(String[] args) { Subject subject = new SubjectImpl(); Subject proxy = (Subject) Proxy.newProxyInstance( subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject)); proxy.sayHello(); } }
3.3 Mybatis Mapper 为什么不需要实现类?
超全MyBatis动态代理详解!(绝对干货)_石杉的架构笔记-CSDN博客
Dao接口的工作原理是JDK动态代理
先来说一下 Mybatis 代理工厂中具体生成动态代理类具体逻辑
-
根据 .xml 上关联的 namespace, 通过
Class#forName
反射的方式返回 Class 对象(不止 .xml namespace 一种方式) -
将得到的 Class 对象(实际就是接口对象)传递给 Mybatis 代理工厂生成代理对象,也就是刚才 mapperInterface 属性
四、分页
Mybatis是如何进行分页的?
Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页
分页插件的原理
在插件的拦截方法内拦截待执行的sql,然后重写sql
举例:select * from student,拦截sql后重写为:select t.* from (select * from student)t limit 0,10
五、延迟加载
5.1 延迟加载 VS立即加载
延迟加载:在真正使用数据的时候才发起查询,不用的时候不查询关联的数据,延迟加载又叫按需查询(懒加载)
立即加载:不管用不用,只要一调用方法,马上发起查询。
使用场景:在对应的四种表关系中,一对多、多对多通常情况下采用延迟加载,多对一、一对一通常情况下采用立即加载。
配置文件中:
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 | ||
aggressiveLazyLoading | 当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载(参考 lazyLoadTriggerMethods)。 |
5.2 例子
Mybatis延迟加载的实现以及使用场景 - 全me村的希望 - 博客园
<resultMap id="userAccountMap" type="com.example.domain.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="birthday" column="birthday"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
<collection property="accountList" ofType="com.example.domain.Account" column="id"
select="com.example.dao.AccountDao.findAllByUid"/>
</resultMap>
<select id="findAll" resultMap="userAccountMap">
SELECT * FROM USER;
</select>
注释绿色部分和没有注释的区别:
六、动态sql:if choose trim foreach
问题:
Q:JDK 动态代理能否对类代理?
因为 JDK 动态代理生成的代理类,会继承 Proxy 类,由于 Java 无法多继承,所以无法对类进行代理
Q:抽象类是否可以 JDK 动态代理?
不可以,抽象类本质上也是类,Proxy 生成代理类过程中,会校验传入 Class 是否接口
Q:Mybatis Mapper 接口没有实现类,怎么实现的动态代理?
Mybatis 会通过
Class#forname
得到 Mapper 接口 Class 对象,生成对应的动态代理对象,核心业务处理都会在InvocationHandler#invoke
进行处理
Q: 为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?
答:Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工
Q:Mybatis用了哪些设计模式?
1、构造器模式,build构造
2、装饰者模式,缓存、套娃一样增强
3、
常见面试题
1、什么是 Mybatis?
2、Mybaits 的优点
3、MyBatis 框架的缺点
4、MyBatis 框架适用场合
5、MyBatis 与 Hibernate 有哪些不同?
6、#{}和${}的区别是什么?
7、当实体类中的属性名和表中的字段名不一样 ,怎么办 ?
8、 模糊查询 like 语句该怎么写?
9、通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应,请问,这个 Dao 接口的工作原理是什么?Dao 接口里的方法,参数不同时,方法能重载吗?
10、Mybatis 是如何进行分页的?分页插件的原理是什么?
11、Mybatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式?
12、如何执行批量插入?
13、如何获取自动生成的(主)键值?
14、在 mapper 中如何传递多个参数?
15、Mybatis 动态 sql 有什么用?执行原理?有哪些动态 sql?
16、Xml 映射文件中,除了常见的 select|insert|updae|delete标签之外,还有哪些标签?
17、Mybatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重复?
18、为什么说 Mybatis 是半自动 ORM 映射工具?它与全自动的区别在哪里?
19、 一对一、一对多的关联查询 ?
20、MyBatis 实现一对一有几种方式?具体怎么操作的?
21、MyBatis 实现一对多有几种方式,怎么操作的?
22、Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?
23、Mybatis 的一级、二级缓存
24、Mapper 编写有哪几种方式?
25、简述 Mybatis 的插件运行原理,以及如何编写一个插件。