接口式编程
1、MyBatis可以使用简单的XML或注解用于配置和原
始映射,将接口和Java的POJO(Plain Old Java
Objects,普通的Java对象)映射成数据库中的记
录
jdbc: Dao ------------------> DaoImpl
Mybatis Mapper -------------> xxMapper.xml
2、SqlSession代表和数据库的一次会话,用完须关闭
3、SqlSession和Connection两者都不是线程安全的,每次使用都应该去获取新的对象
4、Mapper接口没有实现类,但是mybatis会为它生成一个代理对象。(将接口与xml进行绑定)
EmployeeMapper empMapper = sqlSession.getMapper(EmployeeMappper.getClass);
5、两个重要的配置文件
mybatis的全局配置文件:数据库连接池信息、事务管理器信息、系统运行环境信息等
sql映射文件:保存了每一个sql语句的映射信息
全局配置文件
MyBatis 的配置文件包含了影响 MyBatis 行为甚深的
设置(settings)和属性(properties)信息。文档的
顶层结构如下:
configuration 配置
properties 属性
settings 设置
typeAliases 类型命名
typeHandlers 类型处理器
objectFactory 对象工厂
plugins 插件
environments 环境
-------environment 环境变量
-------------transactionManager 事务管理器
-------------dataSource 数据源
databaseIdProvider 数据库厂商标识
mappers 映射器
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties"></properties>
resource:引入类路径下的资源
url:引入网络路径下或者磁盘路径下的资源
<settings>
<!-- 开启驼峰匹配:完成从经典的数据库列名(下划线)到驼峰式属性的映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启缓存 -->
<setting name="cacheEnabled" value="true"/>
<!-- setting还有好几种属性 -->
</settings>
databaseIdProvider标签:支持多数据厂商的 type属性作用就是得到数据库厂商的标识(Mysql、Oracle、SQL server…)
<databaseIdProvider type="DB_VENDOR">
<property name="Mysql" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL server" value="sqlserver"/>
</databaseIdProvider>
id:指定当前环境的唯一标识
transactionManager、和dataSource都必须有
<environments default="itg">
<environment id="dev">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED" >
<property name="driverClassName" value="${jdbc.driverClass}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
</environment>
<environment id="itg">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED" >
<property name="driverClassName" value="${jdbcItg.driverClass}"></property>
<property name="url" value="${jdbcItg.url}"></property>
<property name="username" value="${jdbcItg.username}"></property>
<property name="password" value="${jdbcItg.password}"></property>
</dataSource>
</environment>
</environments>
mapper注入 别名扫描,最好是整合spring的时候在spring里面配置,
<!-- 注入映射文件路径,mybatis/mapper/**/*.xml表示匹配mybatis/mapper目录及子目录下所有的xml文件 -->
<property name="mapperLocations" value="classpath:mybatis/mapper/**/*.xml"></property>
<!-- 别名扫描 -->
<property name="typeAliasesPackage" value="com.kk.domain"></property>
</configuration>
sql映射文件
#和$的区别
#{key}:获取参数的值,预编译到SQL中。安全
${key}:获取参数的值,拼接到SQL中。有SQL注入问
题。应用:ORDER BY ${name},分表查询 select * from ${year}_name ;
cache –命名空间的二级缓存配置
cache-ref – 其他命名空间缓存配置的引用。
resultMap – 自定义结果集映射
parameterMap – 已废弃!老式风格的参数映射
sql –抽取可重用语句块。
insert – 映射插入语句
update – 映射更新语句
delete – 映射删除语句
select – 映射查询语句
开启自动生成主键
<insert id="saveUser" useGeneratedKeys="true" keyProperty="id">
INSERT INTO `kkkk`.`users` ( `age`, `name`, `birthday`, `salary`)
VALUES ( #{age}, #{name}, '1991-02-28', '99999')
</insert>
自定义resultMap,实现高级结果集映射
<resultMap type="User" id="MySimpleEmp">
<!--指定主键列的封装规则
id定义主键会底层有优化;
column:指定哪一列
property:指定对应的javaBean属性
-->
<id column="id" property="id"/>
<!-- 定义普通列封装规则 -->
<result column="name" property="name"/>
<!-- 其他不指定的列会自动封装:我们只要写resultMap就把全部的映射规则都写上。 -->
<result column="age" property="age"/>
多对一的情况 多个人共有一个老师
两种方式
1:关联查询
property="dept":指定哪个属性是联合的对象
javaType:指定这个属性对象的类型
<association property="teacher" javaType="Teacher">
<id column="Tid" property="id"/>
<result column="teacherName" property="teacherName"/>
<result column="teacherAge" property="teacherAge"/>
</association>
2:分步查询
select:调用目标的方法查询当前属性的值
column:将指定列的值传入目标方法
<association property="teacher"
select="com.kk.mapper.UserMapper.+分布查询的id(saveUser)"
column="id">
</association>
一对多的情况 一个人有多个老师,也是两种写法
1:
ofType:集合中的类型
columnPrefix:数据库中查询出来的column前缀
<collection property="teacher" ofType="Teacher" columnPrefix="T_">
<!-- 定义这个集合中元素的封装规则 -->
<id column="id" property="id"/>
<result column="teacherName" property="teacherName"/>
<result column="teacherAge" property="teacherAge"/>
</collection>
2:分步查询
<collection property="teacher" select="com.kk.mapper.UserMapper.+分布查询的id(saveUser)" column="id">
有时候可传入的column不止一个,可以通过map的形式 column="{id=id,age=teacherAge}"
</collection>
!!其实遇到这种情况,一般可以考虑是否开启懒加载,降低数据库的压力
<!--在mybatis的全局配置文件mybatis-config.xml中添加懒加载配置-->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
discriminator鉴别器
假设只有年龄为11的时候去查老师,就可以用到这个
<resultMap type="User" id="MySimpleEmp">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
<discriminator javaType="int" column="age">
<case value="11" resultType="Teacher">
<association property="teacher" select="namespace+id"column="id">
</association>
</case>
</discriminator>
</resultMap>
动态sql
mapper.xml中涉及到特殊字符,需要转义
不过如果sql中涉及很多特殊字符,可以使用<![CDATA[ 这里写sql]]>,放入这里面就不会进行转义;
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.kk.mapper.UserMapper">
1: where +if
<select id="getUser" resultType="User">
select * from user
<where>
<!-- test:判断表达式(OGNL),从参数中取值进行判断,遇见特殊符号应该去写转义字符-->
<if test="id!=null">
id=#{id}
</if>
<if test="lastName!=null and lastName!=""">
and last_name like #{lastName}
</if>
</where>
</select>
2:where+choose+when+otherwise,类似switch case
<select id="getUser" resultType="User">
select * from user
<where>
<!-- 如果带了id就用id查,如果带了lastName就用lastName查;只会进入其中一个!! -->
<choose>
<when test="id!=null">
id=#{id}
</when>
<when test="email!=null">
email = #{email}
</when>
<otherwise>
gender = 0
</otherwise>
</choose>
</where>
</select>
3:update+if
<update id="updateEmp">
update user
<set>
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email},
</if>
</set>
where id=#{id}
</update>
4:批量保存 foreach
<insert id="addUser">
insert into user(
<include refid="insertColumn"></include>
)
values
<foreach collection="emps" item="emp" separator=",">
(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
</foreach>
</insert>
<!-- 这种方式需要数据库连接属性allowMultiQueries=true;
如:jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true
这种分号分隔多个sql可以用于其他的批量操作(删除,修改) -->
<insert id="addUser">
<foreach collection="emps" item="emp" separator=";">
insert into tbl_employee(last_name,email,gender,d_id)
values(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
</foreach>
</insert>
5:内置参数
_databaseId:如果配置了databaseIdProvider标签。
_databaseId就是代表当前数据库的别名
<select id="getUser" resultType="User">
<if test="_databaseId=='mysql'">
select * from user where last_name like #{lastName}
</if>
<if test="_databaseId=='oracle'">
select * from user where last_name like #{_parameter.lastName}
</if>
</select>
6:抽取可重用的sql片段。方便后面引用
<insert id="addUser">
insert into user(
<include refid="insertColumn"></include> refid:这里引用下面的sql
)
values
<foreach collection="emps" item="emp" separator=",">
(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
</foreach>
</insert>
<sql id="insertColumn">
<if test="_databaseId=='oracle'">
employee_id,last_name,email
</if>
<if test="_databaseId=='mysql'">
last_name,email,gender,d_id
</if>
</sql>
</mapper>
Mybatis缓存:
一级缓存和二级缓存。
1、默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。
2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
3、为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存:
不能被关闭, 但可以调用 clearCache() 来清空本地缓存
• 同一次会话期间只要查询过的数据都会保存在当
前SqlSession的一个Map中 • key:hashCode+查询的SqlId+编写的sql查询语句+参数
一级缓存失效的四种情况
1、不同的SqlSession对应不同的一级缓存
2、同一个SqlSession但是查询条件不同
3、同一个SqlSession两次查询期间执行了任何一次增
删改操作
4、同一个SqlSession两次查询期间手动清空了缓存
二级缓存:
二级缓存默认不开启,需要手动配置
二级缓存在 SqlSession 关闭或提交之后才会生效
缓存实现要求
POJO实现Serializable接口
使用步骤
1、全局配置文件Mybatis.xml中开启二级缓存
<setting name="cacheEnabled" value="true"/>
2、需要使用二级缓存的映射文件mapper.xml处使用cache配置缓存
<mapper namespace="com.kk.mapper.UserMapper">
<cache eviction="FIFO" flushInterval="5000" readOnly="false" size="1024"></cache>
</cache>
3、注意:POJO需要实现Serializable接口
public class User implements Serializable{
}
缓存相关属性
eviction=缓存回收策略
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。
Mybatis也可以整合第三方缓存,例如EhCache
1:引入EhCachejar包
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.0</version>
</dependency>
2:导入与第三方缓存整合的适配包;官方有
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="D:\kk\ehcache" />
<defaultCache
maxElementsInMemory="10000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
3:在mapper.xml中配置自定义cache
<mapper namespace="com.kk.mapper.UserMapper">
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
顺带提下Mybatis-Plus
其实和mybati基本没区别,多了些功能,主要是面对简单的业务场景,可以不用些mapper.xml了,框架帮你写好了,你主要继承提供的BaseMapper即可,还提供了分页,具体可以看看。
public interface EmplopyeeDao extends BaseMapper<Employee> {
}