1.注解开发开发实现复杂映射:
-
一对一查询
一对一
public interface OrderMapper {
@Select("select * from orders")
@Results({@Result(id = true, property = "id", column = "id"),
@Result(property = "ordertime", column = "ordertime"),
@Result(property = "total", column = "total"),
@Result(property = "user", column = "uid", javaType = User.class,
one = @One(select = "com.lagou.mapper.UserMapper.findById"))})
List<Order> findAll();
}
public interface UserMapper {
@Select("select * from user where id=#{id}")
User findById(int id);
}
-
一对多查询
一对多
public interface UserMapper {
@Select("select * from user")
@Results({@Result(id = true, property = "id", column = "id"),
@Result(property = "username", column = "username"),
@Result(property = "password", column = "password"),
@Result(property = "birthday", column = "birthday"),
@Result(property = "orderList", column = "id", javaType = List.class,
many = @Many(select = "com.lagou.mapper.OrderMapper.findByUid"))})
List<User> findAllUserAndOrder();
}
public interface OrderMapper {
@Select("select * from orders where uid=#{uid}")
List<Order> findByUid(int uid);
}
代码实现如上,通过@Results、@Result注解实现,其中@Results注解等价于xlm配置文件中的<resultMap>标签@Result注解等价<id>/<result>标签;复杂关系则通过与@One注解,@Many注解组合实现,@One可以实现单个对象的映射,@Many可以映射为集合形式
疑问:
1.一对一映射查询中,通过订单表中保存的uid,带入UserMapper中,来获取对应的用户对象。代码实现中,只展示了单字段关联查询(uid),如果表之间,不存在外键或者使用了组合外键,要通过对个参数查询,应该实现?
2.@One与@Many注解分别对应单个对象以及集合,那么能用@One注解实现的,@many注解应该只需要替换一下接受返回结果的类型就可以达到同样的效果?
1、Mybatis 动态 sql 是做什么的?都有哪些动态 sql?简述 一下动态 sql 的执行原理?
1.动态 SQL 的概念
动态 sql 是指在执行 sql 的时候,同过传入的参数,根据匹配的条件,有可能需要动态的 去判断部分参数是否为空,是否需要循环,或者拼接等情况;
2.动态 Sql
①<if>:
if 是为了判断传入的值是否符合某种规则,比如是等于特定值;
②<where>:
where 标签可以用来做动态拼接查询条件,当和 if 标签配合的时候,不用显示的声明类似 where 1=1 这种无用的条件;
③<foreach>:
foreach 标签可以把传入的集合对象进行遍历,然后把每一项的内容作为参数传到 sql 语句 中 , 里 面 涉 及 到 item( 具 体 的 每 一 个 对 象 ), index( 序 号 ), open( 开 始 符 ), close( 结 束 符), separator(分隔符);
④<choose><when><otherwise>:
这是一组组合标签,他们的作用类似于 Java 中的 switch、case、default。只有一个条件生 效,也就是只执行满足的条件 when,没有满足的条件就执行 otherwise,表示默认条件;
⑤<include>:
include 可以把大量重复的代码整理起来,当使用的时候直接 include 即可,减少重复代码的 编写;
⑥<set>:
适用于更新中,当匹配某个条件后,才会对该字段进行更新操作
⑦<trim>:
是一个格式化标签,主要有 4 个参数:
- prefix(前缀);
- prefixOverrides(去掉第一个标记);
- suffix(后缀);
- suffixOverrides(去掉最后一个标记);
3.动态 sql 的执行原理:
第一部分:
在启动加载解析 xml 配置文件的时候进行解析,根据关键标签封装成对应的 handler 处理对象,封装成 sqlSource 对象存在 mappedStatement。
调用流程:
- SqlSessionFactoryBuilder 在 builder 对 象 的 时 候 , 调 用 XMLConfigBuilder 解 析 sqlMapConfig.xml 配置文件,在解析过程中使用到了私有的 mapperElement(XNode parent)方 法
- 上面方法中通过构建 XMLMapperBuilder,获取到所有的配置 mapper 配置,在调用 private void configurationElement(XNode context) 方 法 进 行 解 析 mapper.xml, 通 过 void buildStatementFromContext(List<XNode> list, String requiredDatabaseId)方法解析 mapper.xml 内的每一个标签
- 循环中构建 XMLStatementBuilder 对象,调用 parseStatementNode()方法来封装 mappedStatment 对象,
- 在过程中需要构建 sqlSource 对象,通过 XMLLanguageDriver 对象进行处理,在 XMLLanguageDriver 中构建解析动态标签对象 XMLScriptBuilder
第二部分:
在执行过程中获取 sqlSource 中获取 bondSql 对象时,执行相应的标签 handler 调用查询执行到 BaseExecutor 的 query 方法时候会去 getBoundSql 并且将参数传进去, 在 sqlSource 接口 DynamicSqlSource 实现类中,调用 getBoundSql 方法执行过程共创建 DynamicContext 对象进行判定解析封装成 SqlSource 对象返回。
2、Mybatis 是否支持延迟加载?如果支持,它的实现原理 是什么?
1.Mybatis 是否支持延迟加载
Mybatis 可以通过 association 和 collection 实现延迟加载,association 是一对一,collection 指的是一对多查询,在 mybatis 配置文件中可以配置 lazyloadingEnable=true/false.
2.原理:
使用 javassistProxy 为目标对象创建代理对象,当调用目标对象的方法时进入拦截器方 法。比如调用 a.getB().getName(),拦截器方法 invoke() 发现 a.getb()为 null 值,会单独发送 事先保存好的查询关联 b 对象的 sql 语句,把 b 查询上来然后调用 a.setB(b),于是 a 的对象的 属性 b 就有值了,然后接着调用 a.getB().getName(),这就是延迟加载的原理。
3、Mybatis 都有哪些 Executor 执行器?它们之间的区别 是什么?
SimpleExecutor:
每执行一次 update 或 select,就开启一个 Statement 对象,用完立刻关闭 Statement 对象。
ReuseExecutor:
执行 update 或 select,以 sql 作为 key 查找 Statement 对象,存在就使用, 不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map 内,供下一次使用。简 言之,就是重复使用 Statement 对象。
BatchExecutor:
执行 update(没有 select,JDBC 批处理不支持 select),将所有 sql 都添加 到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个 Statement 对象,每个 Statement 对象都是 addBatch()完毕后,等待逐一执行 executeBatch()批处理。与 JDBC 批处理相同。
4、简述下 Mybatis 的一级、二级缓存(分别从存储结构、 范围、失效场景。三个方面来作答)?
1.一级缓存:
一级缓存是指 SqlSession 级别的,作用域是 SqlSession,Mybatis 默认开启一级缓存,在 同一个 SqlSession 中,相同的 Sql 查询的时候,第一次查询的时候,就会从缓存中取,如果 缓存未命中,那么就从数据库查询出来,并将结果以 HashMap 的形式保存,下次查询的时 候,就直接从缓存中查询,就不在去查询数据库,对应的就不在去执行 SQL 语句。当查询到 的数据,进行增删改的操作的时候,缓存将会失效。在 spring 容器管理中每次查询都是创建 一个新的 sqlSession,所以在分布式环境中不会出现数据不一致的问题
2.二级缓存:
二级缓存是 mapper 级别的缓存,多个 SqlSession 去操作同一个 mapper 的 sql 语句,多 个 SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession。第一次调用 mapper 下的 sql 的 时候去查询信息,查询到的信息会存放到该 mapper 对应的二级缓存区域,第二次调用 namespace 下的 mapper 映射文件中,相同的 SQL 去查询,回去对应的二级缓存内取结果, 使用值需要开启 cache 标签,在 select 上添加 useCache 属性为 true,在更新和删除时候需要 手动开启 flushCache 刷新缓存。
5、简述 Mybatis 的插件运行原理,以及如何编写一个插件?
1.原理:
Mybatis 通过动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每 当执行这 4 种接口对象的方法时(update、query、commit、rollback 等方法),就会进入拦截 方法,具体就是 InvocationHandler 的 invoke()方法,只会拦截那些你指定需要拦截的方法。
2. 实现方法:
- 编写 Intercepror 接口的实现类;
- 设置插件的签名,告诉 mybatis 拦截哪个对象的哪个方法;
- 最后将插件注册到全局配置文件中。
网友评论