# IPersistence **Repository Path**: lagou-ch/IPersistence ## Basic Information - **Project Name**: IPersistence - **Description**: 自定义持久层框架-mybatis - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2020-09-28 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README [TOC] # mybatis常见问题 ## 实体类的属性和表的字段名称不一致,怎么办? - 查询的sql语句中,定义字段的别名,此别名和实体的属性名一致就行 - 通过``来映射字段名称和实体属性名称的一一对应的关系 ## 如何获取自动生成的(主)键值? - 按照mysql数据库的话,按照以下思路,关键字:`LAST_INSERT_ID`,获取插入之后ID值 ```java select LAST_INSERT_ID() ``` ## 在mapper中如何传递多个参数? - 第一种:使用占位符的思想 - 在映射文件中使用#{0},#{1}代表传递进来的第几个参数 ```java ``` - 使用@param注解 ```java public user selectUser(@param(“username”) string username); ``` - 第二种:使用map的方式 ````java Map map = new HashMap(); map.put("start", start); map.put("end", end); return sqlSession.selectList("user.selectUser", map); ```` ## Mybatis动态sql是做什么的?都有哪些动态sql?简述一下动态sql的执行原理? - **作用**: - Mybatis动态sql可以让我们在Xml映射文件内,**以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能** - **类型**: - **trim|where|set|foreach|if|choose|when|otherwise|bind** - **原理**: - 使用OGNL从sql参数对象中计算表达式的值,**根据表达式的值动态拼接sql,以此来完成动态sql的功能** ## Mybatis是否支持延迟加载?如果支持,它的实现原理是什么? - **分析** - Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载`lazyLoadingEnabled=true|false`。 - **原理** - 它的原理是,使用`CGLIB`创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用user.getProject().getName(),拦截器`invoke()`方法发现user.getProject()是null值,那么就会单独发送事先保存好的查询关联Project对象的sql,把Project查询上来,然后调用user.setProject(project),于是user的对象project属性就有值了,接着完成user.getProject().getName()方法的调用。 这就是延迟加载的基本原理,也就是直到用到的时候才会去加载,不会完成初始化就加载出来。 ## Mybatis都有哪些Executor执行器?它们之间的区别是什么? Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。 - **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批处理相同。 以上的这些Executor的作用范围都严格控制在SqlSession生命周期范围内 ## 简述下Mybatis的一级、二级缓存(分别从存储结构、范围、失效场景。三个方面来作答)? - **存储结构** - **一级**:mybatis的一级缓存是SqlSession级别的缓存,在操作数据库的时候需要先创建SqlSession会话对象,在对象中有一个HashMap用于存储缓存数据,此HashMap是当前会话对象私有的,别的SqlSession会话对象无法访问。 - **二级**:二级缓存与一级缓存其机制相同,默认是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache,Redis。 - **范围** - **一级**:一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。 也就是在同一个SqlSession中,执行相同的查询SQL,第一次会去数据库进行查询,并写到缓存中;第二次以后是直接去缓存中取。 - **二级**:二级缓存的作用域是同一个namespace下的mapper映射文件内容,多个SqlSession共享。Mybatis需要手动设置开启二级缓存,不过不建议开启。 - **失效场景** - **一级**:当执行SQL查询中间发生了增删改的操作,MyBatis会把SqlSession的缓存清空。这里注意,清空的缓存,只是当前私有线程下独享的缓存,一级缓存的范围有`SESSION`和`STATEMENT`两种,默认是`SESSION`,如果不想使用一级缓存,可以把一级缓存的范围指定为`STATEMENT`,这样每次执行完一个Mapper中的语句后都会将一级缓存清除。 ``` ``` - **二级**:和一级缓存一样,存在增删改的操作,当进行事务的提交时,会将缓存清空,但是需要注意的是,这里清空的是同一namespace下的缓存,还就执行缓存的clear操作做也会清空缓存,实质上是对所查找key对应的value将其置为null,而非将彻底删除缓存数据。 **tips** 二级缓存,建议在 SQL 映射文件中添加配置: `````` 这个语句的效果如下: - 映射语句文件中的所有 select 语句将会被缓存。 - 映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。 - 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。 - 根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。 - 缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。 - 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。 使用二级缓存容易出现脏读,建议避免使用二级缓存,在业务层使用可控制的缓存代替更好