MyBatis-基础知识


MyBatis注解 说明
@Select 查询语句
@Insert 添加语句
@Update 更新语句
@Delete 删除语句
@MapKey 当返回值类型为Map时,设置Map的主键列名

HelloWorld

全局配置文件:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>


    
        
            
            
                
                
                
                
            
        
    
    
        
        
    

SQL映射文件:EmpMapper.xml
<?xml version="1.0" encoding="UTF-8"?>


    

具体代码
// 实体类
@Data
public class Employee {
    private Integer id;
    private String empName;
    private Integer gender;
    private String email;
}

// DAO层接口
public interface EmployeeDao {
    public Employee getEmpById(Integer id);
}

// 测试方法
@Test
public void getEmpById() throws IOException {
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
    SqlSession sqlSession = sqlSessionFactory.openSession();
    EmployeeDao mapper = sqlSession.getMapper(EmployeeDao.class);
    Employee emp = mapper.getEmpById(2);
    System.out.println(emp);
}

全局配置文件

properties

如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:

  • 在 properties 元素体内指定的属性首先被读取
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性
  • 最后读取作为方法参数传递的属性,并覆盖已读取的同名属性
    因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的是 properties 属性中指定的属性



    
        
        
            
            
            
            
        
    

settings

设置参数 描述 有效值 默认值
mapunderscoretocamelcase 是否开启自动驼峰命名规则映射 true或false false
autoMappingBehavior 指定MyBatis应如何自动映射列到字段或属性.NONE表示取消自动映射;PARTIAL只会自动映射没有定义嵌套结果集映射的结果集.FULL会自动映射任意复杂的结果集(无论是否嵌套) NONE,PARTIAL,FULL PARTIAL
lazyLoadingEnabled 延迟加载的全局开关.当开启时,所有关联对象都会延迟加载.特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态 true或false false
aggressiveLazyLoading 当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载 true或false true
cacheEnabled 该配置影响的所有映射器中配置的缓存的全局开关 true或false true
multipleResultSetsEnabled 是否允许单一语句返回多结果集(需要兼容驱动) true或false true
useColumnLabel 使用列标签代替列名.不同的驱动在这方面会有不同的表现,具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果 true或false true
useGeneratedKeys 允许JDBC支持自动生成主键,需要驱动兼容.如果设置为true则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如Derby) true或false false
autoMappingUnknownColumnBehavior 指定检测到自动映射目标的未知列(或未知属性类型)时的行为 NONE,WARNING,FAILING NONE
defaultExecutorType 配置默认的执行器.SIMPLE就是普通的执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 SIMPLE REUSE BATCH SIMPLE
defaultStatementTimeout 设置超时时间,它决定驱动等待数据库响应的秒数 任何正整数 未设置(空)
defaultFetchSize 为驱动程序设置一个提示,以控制返回结果的获取大小.该参数值可以被查询设置覆盖 任何正整数 未设置(空)
safeRowBoundsEnabled 允许在嵌套语句中使用分页 true或false false
safeResultHandlerEnabled 允许在嵌套语句中使用分页 true或false true
localCacheScope MyBatis利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询.默认值为SESSION,这种情况下会缓存一个会话中执行的所有查询.若设置值为STATEMENT,本地会话仅用在语句执行上,对相同SqlSession的不同调用将不会共享数据 SESSION或STATEMENT SESSION
jdbcTypeForNull 当没有为参数提供特定的JDBC类型时,为空值指定JDBC类型.某些驱动需要指定列的JDBC类型,多数情况直接用一般类型即可,比如NULL、VARCHAR或OTHER JdbcType枚举.最常见的是:NULL、VARCHAR 和 OTHER OTHER
lazyLoadTriggerMethods 指定哪个对象的方法触发一次延迟加载 以逗号分隔的方法名称列表
defaultScriptingLanguage 指定动态SQL生成的默认语言 类型别名或完全限定的类名
callSettersOnNulls 指定当结果集中值为null的时候是否调用映射对象的setter(map对象时为put)方法,这对于有Map.keySet()依赖或null值初始化的时候是有用的.注意基本类型(int、boolean等)是不能设置成null的 true或false false
logPrefix 指定MyBatis增加到日志名称的前缀 任何字符串 没有设置
logImpl 指定MyBatis所用日志的具体实现,未指定时将自动查找 SLF4J或LOG4J或LOG4J2或JDK_LOGGING或COMMONS_LOGGING或STDOUT_LOGGING或NO_LOGGING 没有设置
proxyFactory 指定Mybatis创建具有延迟加载能力的对象所用到的代理工具 CGLIB或JAVASSIST JAVASSIST
vfsImpl 指定VFS实现 自定义VFS实现的完全限定类名,以逗号分隔 没有设置
useActualParamName 允许通过方法签名中声明的实际名称引用语句参数.要使用此功能,您的项目必须使用-parameters选项在Java8中编译 true或false true

typeAliases

类型别名是为常用的类型(JavaBean)起别名,别名默认就是类名,不区分大小写;也可以指定别名。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余



    







  

已经为许多常见的 Java 类型内建了相应的类型别名。它们都是大小写不敏感的,需要注意的是由基本类型名称重复导致的特殊处理

内置的类型别名
映射的类型 别名
byte _byte
short _short
int _int
int _integer
long _long
float _float
double _double
boolean _boolean
String string
Byte byte
Short short
Integer int
Integer integer
Long long
Float float
Double double
Boolean boolean
Date date
BigDecimal decimal
BigDecimal bigdecimal
Object object
Collection collection
Map map
List list
ArrayList arraylist
HashMap hashmap
Iterator iterator

typeHandlers

无论是MyBatis在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成Java类型。下表描述了一些默认的类型处理器

类型处理器
类型处理器 Java 类型 JDBC 类型
BooleanTypeHandler java.lang.Boolean, boolean 数据库兼容的 BOOLEAN
ByteTypeHandler java.lang.Byte, byte 数据库兼容的 NUMERIC 或 BYTE
ShortTypeHandler java.lang.Short, short 数据库兼容的 NUMERIC 或 SHORT INTEGER
IntegerTypeHandler java.lang.Integer, int 数据库兼容的 NUMERIC 或 INTEGER
LongTypeHandler java.lang.Long, long 数据库兼容的 NUMERIC 或 LONG INTEGER
FloatTypeHandler java.lang.Float, float 数据库兼容的 NUMERIC 或 FLOAT
DoubleTypeHandler java.lang.Double, double 数据库兼容的 NUMERIC 或 DOUBLE
BigDecimalTypeHandler java.math.BigDecimal 数据库兼容的 NUMERIC 或 DECIMAL
StringTypeHandler java.lang.String CHAR, VARCHAR
ClobReaderTypeHandler java.io.Reader -
ClobTypeHandler java.lang.String CLOB, LONGVARCHAR
NStringTypeHandler java.lang.String NVARCHAR, NCHAR
NClobTypeHandler java.lang.String NCLOB
BlobInputStreamTypeHandler java.io.InputStream -
ByteArrayTypeHandler byte[] 数据库兼容的字节流类型
BlobTypeHandler byte[] BLOB, LONGVARBINARY
DateTypeHandler java.util.Date TIMESTAMP
DateOnlyTypeHandler java.util.Date DATE
TimeOnlyTypeHandler java.util.Date TIME
SqlDateTypeHandler java.sql.Date DATE
SqlTimeTypeHandler java.sql.Time TIME
SqlTimestampTypeHandler java.sql.Timestamp TIMESTAMP
ObjectTypeHandler Any OTHER 或未指定类型
EnumTypeHandler Enumeration Type VARCHAR-任何兼容的字符串类型,存储枚举的名称(而不是索引)
EnumOrdinalTypeHandler Enumeration Type 任何兼容的 NUMERIC 或 DOUBLE 类型,存储枚举的索引(而不是名称)

你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。 具体做法为:实现 org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类org.apache.ibatis.type.BaseTypeHandler, 然后可以选择性地将它映射到一个 JDBC 类型。比如:

plugins

environments


    
        
        
            
            
            
            
        
    

databaseIdProvider

databaseIdProvider用来考虑数据库移植性问题

  • name:数据库产商标识
  • value:给这个标识起一个好用的名字

    
    
    




mappers

  • url:可以从磁盘或者网络路径引用
  • resource:在类路径下找SQL映射文件
  • class:直接引用接口的全类名,但需要将XML和DAO接口同目录存放,而且文件名和接口名需一致
  • package:批量注册,但需要将XML和DAO接口同目录存放,也可将配置文件放在resource/同包名/下

    
    
    
    

SQL映射文件

  • cache –命名空间的二级缓存配置
  • cache-ref – 其他命名空间缓存配置的引用
  • resultMap – 自定义结果集映射
  • parameterMap – 已废弃!老式风格的参数映射
  • sql –抽取可重用语句块
  • insert – 映射插入语句
  • update – 映射更新语句
  • delete – 映射删除语句
  • select – 映射查询语句

增删改标签

标签属性 说明
id 命名空间中的唯一标识符
parameterType 将要传入语句的参数的完全限定类名或别名.这个属性是可选的,因为MyBatis可以通过TypeHandler推断出具体传入语句的参数类型,默认值为unset
statementType 可选值:STATEMENT或PREPARED或CALLABLE;让MyBatis分别选择使用Statement或PreparedStatement或CallableStatement;默认值:PREPARED
flushCache 将其设置为true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空;默认值:true
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数;默认值:unset
useGeneratedKeys (仅对insert和update有用)使用自增主键;底层是使用原生JDBC获取自增主键的方法;默认值:false
keyProperty (仅对insert和update有用)将自增的ID赋值给某个属性;Mybatis会通过getGeneratedKeys的返回值或者通过insert语句的selectKey子元素设置它的键值;默认值:unset
keyColumn (仅对insert和update有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置.如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表
databaseId 如果配置了databaseIdProvider,mybatis会加载所有的不带databaseId或匹配当前databaseId的语句;如果带或者不带的语句都有,则不带的会被忽略
  • 若数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),则可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上
  • 而对于不支持自增型主键的数据库(例如 Oracle),则可以使用 selectKey 子元素:selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用

    insert into t_employee(empname, gender, email) value (#{empName}, #{gender}, #{email});



    
        select max(id) + 1 from t_employee
    
    insert into t_employee(id, empname, gender, email) value (#{id}, #{empName}, #{gender}, #{email});

selectKey属性名 说明
order 可选值:BEFORE或AFTER;selectKey标签执行的时机
keyProperty selectKey语句结果应该被设置的目标属性
keyColumn 匹配属性的返回结果集中的列名称
resultType 结果的类型;通常MyBatis可以推算出来,加上也没什么问题
statementType 可选值:STATEMENT,PREPARED,CALLABLE

select标签

select标签属性 说明
id 唯一标识符;用来引用这条语句,需要和接口的方法名一致
parameterType 参数类型;可以不传,MyBatis会根据TypeHandler自动推断
resultType 返回值类型;别名或者全类名,如果返回的是集合,定义集合中元素的类型。不能和resultMap同时使用
parameterType 将会传入这条语句的参数类的完全限定名或别名.这个属性是可选的,因为MyBatis可以通过TypeHandler推断出具体传入语句的参数;默认值为unset
resultType 从这条语句中返回的期望类型的类的完全限定名或别名.注意如果是集合,那应该是集合可以包含的类型,而不是集合本身;该属性和resultMap不能同时使用
flushCache 该值为true时,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空;默认值:false
useCache 该值为true时,将会导致本条语句的结果被二级缓存;默认值:对select元素为true
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数;默认值为unset
fetchSize 影响驱动程序每次批量返回的结果行数;默认值为unset
statementType STATEMENT.PREPARED.CALLABLE;默认值为PREPARED
resultSetType FORWARD_ONLY,SCROLL_SENSITIVE,SCROLL_INSENSITIVE;默认值为unset
databaseId 如果配置了databaseIdProvider,mybatis会加载所有不带databaseId或匹配当前databaseId的语句;如果带或者不带的都有,则不带的会被忽略
resultOrdered 这个设置仅针对嵌套结果select语句使用:如果为true,就假设包含了嵌套结果集或者分组,这样当返回一个主结果行,就不会发生有前面结果集引用的情况.这就使得在获取嵌套的结果集的时候不至于导致内存不够用.默认值:false
resultSets 这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是都好分隔的

参数传递

  • 传入单个参数:可以接受基本类型,对象类型,集合类型的值。这种情况MyBatis可直接使用这个参数,不需要经过任何处理
  • 传入多个参数:任意多个参数,都会被MyBatis重新包装成一个Map传入。Map的key是param1,param2,0,1…,值就是参数的值
  • 传入命名参数:为参数使用@Param起一个名字,MyBatis就会将这些参数封装进map中,key就是我们自己指定的名字
  • 传入POJO:当这些参数属于我们业务POJO时,我们直接传递POJO
  • 传入Map:我们也可以封装多个参数为map,直接传递

参数处理:参数也可以指定一个特殊的数据类型

  • javaType 通常可以从参数对象中来去确定
  • 如果 null 被当作值来传递,对于所有可能为空的列,jdbcType 需要被设置(Oracle就需要,MySQL不需要)
  • 对于数值类型,还可以设置小数点后保留的位数
  • mode 属性允许指定 IN,OUT 或 INOUT 参数。如果参数为 OUT 或 INOUT,参数对象属性的真实值将会被改变,就像在获取输出参数时所期望的那样
#{key}:获取参数的值,预编译到SQL中。安全
${key}:获取参数的值,拼接到SQL中。有SQL注入问题。ORDER BY ${name}



// 查询单个结果对象的Map结构
Map getEmpReturnMap(Integer id);

// 查询多个结果对象的Map结构
@MapKey("id")
Map getEmpsReturnMap();

@Test
public void testReturnMap() {
    Map map = mapper.getEmpReturnMap(1);
    // {empname=zhangsan, gender=1, id=2, login_account=zhangsan, email=zhangsan@qq.com}
    Map maps = mapper.getEmpsReturnMap();
    System.out.println(map);
    System.out.println(maps);
}

resultMap自定义封装规则

自定义结果集:自定义数据库列名和JavaBean属性之间的映射规则

resultMap子标签 说明
id 一个ID结果; 标记结果作为ID可以帮助提高整体效能
result 注入到字段或 JavaBean 属性的普通结果
association 一个复杂的类型关联;许多结果将包成这种类型
collection 复杂类型的集
constructor 类在实例化时, 用来注入结果到构造方法中

association或者collection标签的fetchType=eager/lazy可以覆盖全局的延迟加载策略,指定立即加载(eager)或者延迟加载(lazy)



    
    
    
    
    



复杂对象映射方式一:POJO属性

    
    
    
    


复杂对象映射方式二:association嵌套结果集

    
    
    
        
    
    


复杂对象映射方式三:association-分段查询


    
    
    

复杂对象映射方式四:Collection-集合类型和嵌套结果集

    
    
    
        
        
    

复杂对象映射方式五:association-分段查询和延迟加载


    
    
    
    

动态SQL

  • where
  • if
  • choose(when,otherwise)
  • trim(where,set)
  • foreach
  • bind
  • include

缓存

MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存

  • 默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启
  • 二级缓存需要手动开启和配置,它是基于namespace级别的缓存
  • 为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

一级缓存

  • 一级缓存(local cache), 即本地缓存, 作用域默认为sqlSession。当 Session flush 或 close 后, 该 Session 中的所有 Cache 将被清空
  • 本地缓存不能被关闭, 但可以调用 clearCache() 来清空本地缓存, 或者改变缓存的作用域
  • 在mybatis3.1之后, 可以配置本地缓存的作用域. 在 mybatis.xml 中配置

同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中

一级缓存失效情况

  • 不同的SqlSession对应不同的一级缓存
  • 同一个SqlSession但是查询条件不同
  • 同一个SqlSession两次查询期间执行了任何一次增删改操作
  • 同一个SqlSession两次查询期间手动清空了缓存

二级缓存

  • 二级缓存(second level cache),全局作用域缓存.二级缓存默认不开启,需要手动配置
  • MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口
  • 二级缓存在 SqlSession 关闭或提交之后才会生效

使用步骤

  1. 全局配置文件中开启二级缓存
  2. 需要使用二级缓存的映射文件处使用cache配置缓存
  3. 注意:POJO需要实现Serializable接口

标签缓存相关属性

  • eviction=“FIFO”:缓存回收策略:
    • LRU – 最近最少使用的:移除最长时间不被使用的对象
    • FIFO – 先进先出:按对象进入缓存的顺序来移除它们
    • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象
    • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象
    • 默认的是 LRU
  • flushInterval:刷新间隔,单位毫秒
    • 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
  • size:引用数目,正整数
    • 代表缓存最多可以存储多少个对象,太大容易导致内存溢出
  • readOnly:只读,true/false
    • true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势
    • false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false

缓存相关设置

1、全局setting的cacheEnable:配置二级缓存的开关。一级缓存一直是打开的
2、select标签的useCache属性:配置这个select是否使用二级缓存。一级缓存一直是使用的
3、sql标签的flushCache属性:增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。查询默认flushCache=false
4、sqlSession.clearCache():只是用来清除一级缓存
5、当在某一个作用域 (一级缓存Session/二级缓存Namespaces) 进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear

第三方缓存整合

EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider

使用步骤:

  • 导入ehcache包,以及整合包,日志包
    • ehcache-core-2.6.8.jar、mybatis-ehcache-1.0.3.jar
    • slf4j-api-1.6.1.jar、slf4j-log4j12-1.6.2.jar
  • 编写ehcache.xml配置文件
  • 配置cache标签
  • 参照缓存:若想在命名空间中共享相同的缓存配置和实例。可以使用 cache-ref 元素来引用另外一个缓存