mybatis常见问题1

Posted ityousee

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis常见问题1相关的知识,希望对你有一定的参考价值。

mybatis缓存

一级缓存:也就是sqlsession级别的,默认是开启的,查询条件不一样是没有缓存的,执行过增删改的操作会清理缓存。

二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。作用域为namespance是指对该namespance对应的配置文件中所有的select操作结果都缓存,这样不同线程之间就可以共用二级缓存。

启动二级缓存:在mapper配置文件中:<cache readOnly="true"/>
二级缓存可以设置返回的缓存对象策略:<cache readOnly="true">。当readOnly="true"时,表示二级缓存返回给所有调用者同一个缓存对象实例,调用者可以update获取的缓存实例,但是这样可能会造成其他调用者出现数据不一致的情况(因为所有调用者调用的是同一个实例)。当readOnly="false"时,返回给调用者的是二级缓存总缓存对象的拷贝,即不同调用者获取的是缓存对象不同的实例,这样调用者对各自的缓存对象的修改不会影响到其他的调用者,即是安全的,所以默认是readOnly="false";

二级缓存例子:

ehcache.xml
<ehcache>
   <!--设置缓存的路劲-->
   <diskStore path="/Users/ityousee/Desktop/cache"/>
   <defaultCache
           maxElementsInMemory="10000"
           eternal="false"
           timeToIdleSeconds="120"
           timeToLiveSeconds="120"
           overflowToDisk="true"
           maxElementsOnDisk="10000000"
           diskPersistent="false"
           diskExpiryThreadIntervalSeconds="120"
           memoryStoreEvictionPolicy="LRU"
           />

</ehcache>
Mapper.xml文件
<cache type="org.mybatis.caches.ehcache.EhcacheCache">
 <property name="timeToIdleSeconds" value="3600"/>
 <property name="timeToLiveSeconds" value="3600"/>
 <!-- 同ehcache参数maxElementsInMemory -->
 <property name="maxEntriesLocalHeap" value="1000"/>
 <!-- 同ehcache参数maxElementsOnDisk -->
 <property name="maxEntriesLocalDisk" value="10000000"/>
 <property name="memoryStoreEvictionPolicy" value="LRU"/>
</cache>
pom.xml
<dependency>
   <groupId>cglib</groupId>
   <artifactId>cglib</artifactId>
   <version>2.2.2</version>
</dependency>
<dependency>
   <groupId>net.sf.ehcache</groupId>
   <artifactId>ehcache-core</artifactId>
   <version>2.6.5</version>
</dependency>
<dependency>
   <groupId>org.mybatis</groupId>
   <artifactId>mybatis-ehcache</artifactId>
   <version>1.0.0</version>
</dependency>

mybatis设置别名

设置别名后就可以在参数还有返回类型上直接使用设置的别名而不用使用全限定名

<typeAliases>  
       <typeAlias type="com.wjh.User" alias="User"/>  
   </typeAliases>

#{}和${}的区别是什么?

${}是Properties文件中的变量占位符,它可以用于标签属性值和sql内部,属于静态文本替换,比如${driver}会被静态替换为com.mysql.jdbc.Driver。#{}是sql的参数占位符,Mybatis会将sql中的#{}替换为?号,在sql执行前会使用PreparedStatement的参数设置方法,按序给sql的?号占位符设置参数值,比如ps.setInt(0, parameterValue),#{item.name}的取值方式为使用反射从参数对象中获取item对象的name属性值,相当于param.getItem().getName()。

Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

第一种是使用 标签,逐一定义列名和对象属性名之间的映射关系。第二种是使用sql列的别名功能,将列别名书写为对象属性名,比如T_NAME AS NAME,对象属性名一般是name,小写,但是列名不区分大小写,Mybatis会忽略列名大小写,智能找到与之对应对象属性名,你甚至可以写成T_NAME AS NaMe,Mybatis一样可以正常工作。

有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。

Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。

它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。

当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。

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是否可以映射Enum枚举类?

Mybatis可以映射枚举类,不单可以映射枚举类,Mybatis可以映射任何对象到表的一列上。映射方式为自定义一个TypeHandler,实现TypeHandler的setParameter()和getResult()接口方法。TypeHandler有两个作用,一是完成从javaType至jdbcType的转换,二是完成jdbcType至javaType的转换,体现为setParameter()和getResult()两个方法,分别代表设置sql问号占位符参数和获取列查询结果。

mybatis中foreach使用

foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合 ,也可用于批量操作

foreach元素的属性主要有 item,index,collection,open,separator,close。      item表示集合中每一个元素进行迭代时的别名,     index指 定一个名字,用于表示在迭代过程中,每次迭代到的位置,     open表示该语句以什么开始,     separator表示在每次进行迭代之间以什么符号作为分隔 符,     close表示以什么结束。

collection有三种情况:

  1. 传入的collection为一个list

  2. 传入的collection为一个数组

  3. 传入的collection为一个map

  • 例(list):

<select id="selectTestForEach" parameterType="java.util.List" resultMap="NewsResultMapper">
 select * from t_news n where id in
 <foreach collection="传入的集合" index="index" item="item" open="("
   separator="," close=")">
  #{item}
 </foreach>
  • 例(数组):

<select id="selectTestForEach" parameterType="java.util.ArrayList" resultMap="NewsResultMapper">
 select * from t_news n where id in
 <foreach collection="传入的数组如:int[] ids = new int[] {1,3,5,7};" index="index" item="item" open="("
   separator="," close=")">
  #{item}
 </foreach>
  • 例(map):

<select id="selectTestForEach" parameterType="java.util.HashMap" resultMap="NewsResultMapper">
 select * from t_news n where id in
 <foreach collection="传入的map的一个key" index="index" item="item" open="("
   separator="," close=")">
  #{item}
 </foreach>
 传入的map的key:ids  ,对应map中的一个key
           List ids = new ArrayList();
         ids.add(1);
         ids.add(2);
         ids.add(3);
        Map map = new HashMap();
        map.put("ids", ids);
        map.put("name", "wjh");

返回插入的数据的自增id

<insert id="insertAndGetId" useGeneratedKeys="true" keyProperty="userId" parameterType="对象">
   插入时是不插入id的,插入成功后通过对象.id获取插入的数据的主键id
</insert>

mybatis的几种开发方式

  1. 注解方式:在接口方法上面写SQL语句,有点类似springdataJPA 的query sql 语句,简洁但处理复杂业务则代码可读性不好

@select("select * from  t_user ")
public  list<User> findAll();
  1. 接口+xml方法:只写接口,但接口命名要与xml的id名一致,处理业务和代码可服用性较好但存在繁琐的xml

(1).接口方法

int insert(User record);

(2).xml中的ID命名及其语句

#命名空间要写mapper接口对应的全限定名
<insert id="insert" parameterType="com.***.Domain">
insert into user (id, name,sex
)

values (#{id,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{sex,jdbcType=CHAR}
)

</insert>
  1. 接口+实现类的方式,处理缓存和处理事务方便但添加了多余的代码

(1).接口 方法int insert(User record);

(2).实现类 需要继承 sqlsessiondaosupport

mybatis映射文件中的namespace用法

  1. namespace绑定实体类的全路径

    当namespace绑定的是实体类的全路径时,其实现数据持久化的方式为无代理模式实现数据持久化。

    需要手动实现dao层的接口。

  2. namespace绑定mapper接口的全路径

    当namespace绑定的是mapper接口的全路径时,其实现数据持久化的方式为有代理模式实现数据持久化。

    即会自动产生代理,自动实现数据的持久化,不需要实现mapper层的接口。

mybatis中文参考文档:http://www.mybatis.org/mybatis-3/zh/index.html


以上是关于mybatis常见问题1的主要内容,如果未能解决你的问题,请参考以下文章

mybatis动态sql片段与分页,排序,传参的使用

浅谈Mybatis

mybatis学习(39):动态sql片段

SSM-MyBatis-05:Mybatis中别名,sql片段和模糊查询加getMapper

MyBatis高级特性

MYBATIS05_ifwherechoosewhentrimsetforEach标签sql片段