mybatis 学习的总结
什么是mybatis?
他是java基于持久层的框架(和数据库交互的一层),它支持定制化 SQL、存储过程以及高级映射(不像hibernate把发送sql作为暗箱操作,而他是可以完全看出来的可以看的到的sql语句).MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
mybatis有什么用?
定制化sql,存储过程以及高级映射。
mybatis怎么使用?
重要对象
SqlSessionFactory
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。
怎么构建一个SqlSessionFacrory?
String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession
sqlsession表示与数据库之间的会话,用完必须关闭,与conection一样都是非线程安全(使用非线程安全对象将会导致bug);
mybatis采用的是接口式编程
一个dao接口对应一个高级映射配置文件
虽然这个接口没有实现类,但是mybaits会为其生成一个代理对象.
@Test public void getEmployees2() { SqlSession session=null; try { session=getSqlSessionFactory().openSession(); //得到的是一个接口实现类代理对象 EmployeeMapper mapper=session.getMapper(EmployeeMapper.class); System.out.println(mapper.getEmployeeById(1).toString()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ session.close(); } }
核心配置文件的详解
properties
使用这个标签来引入外部的properties文件
resource属性:引入类路径下的资源
url:是引入网络路径或者磁盘路径下的资源
1 <properties resource="dbconfig.properties"></properties>
setting
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为
<settings> <!-- 设置是否使用驼峰命名法 形如last_name lastName --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
typealiases
这个标签视为一些复杂的名称对象取别名来简化书写 默认为类名首字母小写
如果一个一个取别名,那么就会显得很累!所以又有一个批量取别名的标签
<typeAliases> <!--给类名起别名 --> <typeAlias type="com.jdztc.mybatis.javabean.Employee" alias="employee"/> <!-- 批量取别名 给某个包极其子包所有的类取别名 默认为类名首字母小写 但是别名是不区分大小写的 --> <package name="com.jdztc.mybatis.javaBean"/> <!-- 会遇到一个问题 如果父包与子包的类名一致这时就会报错 解决方案:使用@Alias解决 --> </typeAliases>
typehandlers
类型处理器
将java中的sting类型转化为数据库中的varchar()类型 要想自定义类型处理器 需要实现typeHandler接口或者继承basetypeHandler
和上面的标签一样,引入你自定义的类型处理器 还有批量引入自定义的类型处理器 默认的类型处理器很多
自定义类型处理器
// ExampleTypeHandler.java @MappedJdbcTypes(JdbcType.VARCHAR) public class ExampleTypeHandler extends BaseTypeHandler<String> { @Override public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, parameter); } @Override public String getNullableResult(ResultSet rs, String columnName) throws SQLException { return rs.getString(columnName); } @Override public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return rs.getString(columnIndex); } @Override public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return cs.getString(columnIndex); } }
然后引入
plugins
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
通过实现拦截器接口通过@inceptor注解来配置拦截的sql语句方法
// ExamplePlugin.java @Intercepts({@Signature( type= Executor.class, method = "update", args = {MappedStatement.class,Object.class})}) public class ExamplePlugin implements Interceptor { public Object intercept(Invocation invocation) throws Throwable { return invocation.proceed(); } public Object plugin(Object target) { return Plugin.wrap(target, this); } public void setProperties(Properties properties) { } } <!-- mybatis-config.xml --> <plugins> <plugin interceptor="org.mybatis.example.ExamplePlugin"> <property name="someProperty" value="100"/> </plugin> </plugins>
environments
配置运行环境,比如事务管理器等
<environments default="development"> <!-- <environment id=""> 事务管理器 要想自定义事务管理器 实现TransactionFactory接口 Managed <transactionManager type="JDBC"></transactionManager> 不使用连接池技术 还有一个JDNI技术 <dataSource type="UNPOOLED"></dataSource> </environment> --> <environment id="development"> <transactionManager type="JDBC" /> <!--使用连接池技术 --> <dataSource type="POOLED"> <property name="driver" value="${Jdbc.driver}" /> <property name="url" value="${Jdbc.url}" /> <property name="username" value="${Jdbc.username}"/> <property name="password" value="${Jdbc.password}" /> </dataSource> </environment> </environments>
databaseisProvider
数据库厂商配置
得到数据库厂商的标识
<databaseIdProvider type="DB_VENDOR"> <property name="MySQL" value="mysql"/> </databaseIdProvider>
mappers
导入映射文件
class属性 直接引用接口
接口与其对应的sql映射文件在一块并且名字要相同,class引用的全类名接口才有效
1.有接口对应的映射文件
2.没有,在接口上用注解来编写sql语句
<!--批量导入映射文件 --> <mappers> <package name="com.jdztc.mybatis.mapper"/> </mappers>
这些标签是有顺序的不能随便打乱顺序
映射文件的详解
几个主要标签
cache
命名空间的二级缓存配置
cache-ref
其他命名空间缓存配置的引用 表示和那个命名空间的缓存一致
resultmap
自定义结果集映射
sql
抽取可重用语句块
--> <sql id="insertColumn"> <if test="_databaseId=='mysql'"> <if test="_parameter!=null"> lastname,password,email,did<!-- ,${testColumn} --> </if> </if> <if test="_databaseId=='oracle'"> <if test="_parameter!=null"> lastname,password,email,did </if> </if> </sql>
insert
update
delete
select
数据库自增主键问题
mysql
mysql支持自增主键,mybatis自增主键值的获取,也是利用statement.getGeneratekeys()
insert标签的useGeneratekeys属性是使用主键获取主键值策略
keyProperty:指定对应的主键属性,也就是mybatis获取主键以后,将这个值封装javaBean哪个属性
要想让接口有返回值,直接修改接口的方法的返回值就可以了
mybati不但支持查询返回结果,还支持增删改定义以下返回值 Integer long Boolean
oracle
oracle不支持自增 oracle使用序列来模拟自增
如何获取这个序列值
before 在sql运行之前运行
after 在sql运行之后运行
<insert id="addEmp" parameterType="com.jdztc.mybatis.javabean.Employee" databaseId="oracle"> <!-- 得到next的序列值 --> <!-- 得到序列中的值来作为主键 order是因为要在下面插入之前得到主键 所以selectkey要在那个插入之前先查询--> <!-- <selectKey keyProperty="eid" order="BEFORE"> 编写查询主键的sql语句 select tb_employee_seq.nextval from dual </selectKey>
insert into tb_employee(id,lastname,password,email) values(#{eid},#{lastName},#{password},#{email}) --> <!-- order="AFTER" 如果同时插入多个记录,currval获取的可能是最后一次记录的值,不是当前的的值--> <!-- 可以的到当前的序列值 --> <selectKey keyProperty="eid" order="AFTER"> select tb_employee_seq.currval from dual </selectKey> insert into tb_employee(id,lastname,password,email) values(tb_employee_seq.nextval,#{lastName},#{password},#{email}) </insert>
参数处理
dao的方法中的参数
单个参数,mybatis不会做特殊处理
#{} 不在乎大括号内是什么
多个参数 mybatis会做特殊处理 会把多个参数封装成一个map
那怎么获取参数呢?
通过key来 注意此时的key也比较特殊是固定的param1 ....paramN
命名参数
明确指定封装参数是map的key:@param("id")
key:使用@Param注解指定的值
pojo
如果多个参数正好是我们业务逻辑的参数模型,我们就可以直接传入pojo
#{属性名}
map
如果多个参数不是我们业务逻辑的数据模型,没有对应的pojo,而且不经常用,那么我们就传入一个map
如果多个参数不是我们业务逻辑i的数据模型,但是要经常使用,那么我们可以根据参数,来建立一个数据传输对象,参数作为他的属性
他还支持级联属性
注意:如果参数是CollecctIon与数组的话,mybatis也会做特殊处理 ,也是把传入的集合或者数组封装在一个map中
key:如果参数为Collection类型的 那么key就是collection 如果是list那么就可以是list 如果是数组,那么是array
参数值的获取
#{ } ${ }
在sql中是以占位符? 在sql中是值直接拼装的
以预编译的形式,将参数设置到sql语句中
preparedstatement:防止sql注入
原生的jdbc不支持#{} 我们可以使用${}进行取值
比如分表 按照年份分表拆分
select * from ${year}_salary where ***;
select * from tb_employee order by ${f_name};--作为条件
#{ }更丰富的用法
规定参数的一些规则
javatype jdbctype mode numericscale resultmap typehandler jdbctypeName
oracle不支持null 因为默认的为other
如果参数值为null时,会报错,但是mysql没问题
oracle 对所有的null映射的是原生jdbc的other
由于全局配置中的JdbcTypeForNull的默认值为other
所以修改全局配置
<setting name="jdbcTypeForNull" value="NULL"/>
select 返回值问题
如果返回的是一个集合,那么resulttype中为集合中元素的全类名
如果返回的是一条记录的map,key就是列名
resulttype="map";
如果返回的是多条记录的map 需要在dao中的方法上加@mapkey注解来指定那个属性来作为key 通过这个key来取值
map<Integer,employee> resulttype为返回值的全类名
主键 javaBean
//返回一条记录的map sql映射文件resultType 为map public Map<String,Employee> getEmpLike(String lastName,String lastName2); //返回多条记录的map sql映射文件的resultType为Employee 要想指定他的key为id那么使用@MapKey注解 @MapKey("eid") public Map<Integer,Employee> getEmpLikeMapMany(String lastName);
自定义结果集的映射
resultMap标签的使用
外部的resultmap的命名引用和resulttype属性不能同时使用
自定以返回结果的规则
定义主键mybatis会有优化
<resultMap type="com.jdztc.mybatis.javabean.Employee" id="myemp">
<!--column为数据库表中的字段名 property为javaBean中的属性名-->
<id column="id" property="eid"/> <result column="lastname" property="lastName"/> <result column="password" property="password"/> <result column="email" property="email"/> </resultMap> <!--public Employee getEmpById(Integer id); --> <select id="getEmpById" resultMap="myemp"> select * from tb_employee where id=#{id} </select>
关联查询
resultmap标签的使用
一对多 多对多的时候级联属性封装结果集
resultmap中有一个标签association(一对一的情况下使用这个) 可以指定联合的javabean对象
Property 指定哪个属性是联合的对象
javaType指定这个对象的类型
<resultMap type="com.jdztc.mybatis.javabean.Employee" id="MyDifEmp"> <id column="id" property="eid"/> <result column="lastname" property="lastName"/> <result column="password" property="password"/> <result column="email" property="email"/> <!--association指定联合的javaBean对象 property是指你要联合的那个属性名 javaType是它对应的类型 --> <association property="document" javaType="com.jdztc.mybatis.javabean.Document"> <id column="did" property="id"/> <result column="docuname" property="docuname"/> <result column="code" property="code"/> </association> </resultMap> <!-- public Employee getEmpDocu(Integer id) --> <select id="getEmpDocu" resultMap="MyDifEmp"> select e.id id,e.lastname lastname,e.password password,e.email email,e.did did, d.id ddd,d.code code,d.docuname docuname from tb_document as d,tb_employee as e where e.did=d.id and e.id=#{id} </select>
但是这样sql语句太过复杂,所以可以使用association进行分步查询
先查出员工,然后根据远的的外键,再根据外键到部门查询到所对应的对象 分布查询association中的3个重要属性 需要用到两个方法
Property 指定那个属性是联合的对象
select 查询id的方法
column 指定将哪一列的值传给select标签中的方法 查出部门对象赋给员工的部门属性
<!-- 使用association 进行分步查询 将上述分成两步来解决 --> <!-- public Employee getEmpByIdStep(Integer id); --> <resultMap type="com.jdztc.mybatis.javabean.Employee" id="MyDifDocuStep"> <id column="id" property="eid" /> <result column="lastname" property="lastName" /> <result column="password" property="password" /> <result column="email" property="email" /> <!--association:定义关联对象的封装规则 select:表名当前属性是调用select指定的方法查询的结果 column:指定将哪一列的值传给这个方法 使用select指定的方法查出对象,并封装给属性document --> <association property="document" column="did" select="com.jdztc.mybatis.mapper.DocumentMapper.getDocuById"></association> </resultMap> <select id="getEmpByIdStep" resultMap="MyEmpDis"> select * from tb_employee where id=#{id} </select>
<!--public Document getDocuById(Integer id); --> <resultMap type="com.jdztc.mybatis.javabean.Document" id="MyDifDocu"> <id column="id" property="id"/> <result column="docuname" property="docuname"/> <result column="code" property="code"/> </resultMap> <select id="getDocuById" resultMap="MyDifDocu"> select * from tb_document where id=#{id} </select>
分步查询可以使用到延迟加载,查询效率高
在全局配置中配置
<!-- 延迟加载 懒加载 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 禁用是会按需加载 --> <setting name="aggressiveLazyLoading" value="false"/>
association 是关联对象类型的属性的封装
一对多的时候使用collection标签
collection定义关联集合类型的属性的封装规则
同样的有复杂的sql语句的
property 集合属性名
oftype 集合中元素的全类名
<!-- public Document getDocuByIdColl(Integer id); --> <resultMap type="com.jdztc.mybatis.javabean.Document" id="MyDifDocuColl"> <id column="did" property="id"/> <result column="docuname" property="docuname"/> <result column="code" property="code"/> <collection property="emps" ofType="com.jdztc.mybatis.javabean.Employee"> <id column="id" property="eid"/> <result column="lastname" property="lastName"/> <result column="password" property="password"/> <result column="email" property="email"/> </collection> </resultMap> <select id="getDocuByIdColl" resultMap="MyDifDocuColl"> select d.id did,d.code code,d.docuname docuname,e.id id,e.lastname lastname,e.password password,e.email email from tb_document d LEFT JOIN tb_employee e ON d.id=e.did where d.id=#{id}; </select>
有分布查询的(可以使用懒加载避免浪费)与association类似
Property 集合属性名
select 查询员工的方法
column 多列的值传递过去,将多列的值封装map传递 “{key1=column1,key2=column2}” key为你要传入第二个方法中的值的key而且要与第二个方法中的参数#{}大括号中的值一样 column为你你要传入第二个方法的值,它为对应的当前这个对象表格中的主键的字段名
fetchtype="lazy" 使用懒加载 如果eager的话立即
<!-- collection 分步查询和延迟加载 --> <!-- public Document getDocuBYIdStep(Integer id); --> <resultMap type="com.jdztc.mybatis.javabean.Document" id="MyDifDocuStep"> <id column="id" property="id"/> <result column="docuname" property="docuname"/> <result column="code" property="code"/> <!--column为你要传入的字段 一搬传主键根据主键 来把对象封装在集合当中 --> <collection property="emps" select="com.jdztc.mybatis.mapper.EmployeeMapperPlus.getEmpByDocuId" column="{deptid=id}" fetchType="lazy"></collection> </resultMap> <select id="getDocuByIdStep" resultMap="MyDifDocuStep"> select id,docuname,code from tb_document where id=#{id} </select>
<!-- 根据部门id查询出所有的员工public List<Employee> getEmpByDocuId(Integer docuid); --> <select id="getEmpByDocuId" resultType="com.jdztc.mybatis.javabean.Employee"> select * from tb_employee where did=#{deptid} </select>
discriminator标签(if else)
他是一个鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
<select id="getEmpByIdStep" resultMap="MyEmpDis"> select * from tb_employee where id=#{id} </select> <resultMap type="com.jdztc.mybatis.javabean.Employee" id="MyEmpDis"> <id column="id" property="eid" /> <result column="lastname" property="lastName" /> <result column="password" property="password" /> <result column="email" property="email" /> <!-- 使用鉴别器来进行有条件的查询 javatype为字段所对应的java类型 column为你想要根据那个字段来进行判断--> <discriminator javaType="string" column="lastname">
<!--value为条件值 reslulttype为对应的返回值的全类名-->
<case value="许洁" resultType="com.jdztc.mybatis.javabean.Employee"> <association property="document" column="did" select="com.jdztc.mybatis.mapper.DocumentMapper.getDocuById"></association> </case> <!-- 如果是许洁4号那么就把他的email给他的名字 --> <case value="许洁4号" resultType="com.jdztc.mybatis.javabean.Employee"> <id column="id" property="eid" /> <result column="email" property="lastName" /> <result column="password" property="password" /> <result column="email" property="email" /> </case> </discriminator> </resultMap>
动态sql
4个对sql语句进行操作的重要标签
if
对参数中取值进行判断
<if test="id!=null"> id=#{id} </if>
查询的时候如果条件没带可能sql拼装会有问题 解决方案:
1.1=1
2.使用where标签 where只会去掉第一个多出来的and
choose
trim
foreach
属性的介绍
collection:指定要遍历的集合
list类型的参数会做特殊处理封装在map中,map的key就叫list 也可以使用@param注解来指定集合的引用
item:将当时遍历出的元素赋值给指定的变量
separator:每个元素之间的分隔符
open:以。。。开始的字符
close:以。。。结束的字符
index:索引 遍历list和map的时候的索引是key
批量插入
<!-- public void addEmp(List<Employee> emps); --> <!-- 演示批量插入 --> <!-- <insert id="addEmp"> insert into tb_employee (lastname,password,email,did) values <foreach collection="emps" separator="," item="emp"> (#{emp.lastName},#{emp.password},#{emp.email},#{emp.document.id}) </foreach> </insert> --> <insert id="addEmp"> <if test="_databaseId=='mysql'"> <foreach collection="emps" separator=";" item="emp"> insert into tb_employee( <include refid="insertColumn"> <property name="testColumn" value="abc"/> </include> ) values(#{emp.lastName},#{emp.password},#{emp.email},#{emp.document.id}) </foreach> </if> <if test="_databaseId=='oracle'"> <!-- oracle环境下批量插入的方法 --> <!-- 通过多条语句用分号分开,包括在begin 和end当中 --> <foreach collection="emps" separator=";" item="emp" open="begin" close="end;"> insert into tb_employee( <include refid="insertColumn"></include> ) values <if test="_parameter!=null"> (#{emp.lastName},#{emp.password},#{emp.email},#{emp.document.id}) </if> </foreach> </if> </insert>
oracle只有一种,mysql有两种,mysql要想一条语句允许使用“;”的话,来分隔多条语句,要在url配置?allowmultiqueries=true
利用中间表进行批量插入
就是oracle中特殊的插入方法
sql语句如下
insert into tb_employee (id,lastname,email) seleect tb_employee_seq ,lastname,email from (
--虚表作为一个中间表
select ' test_lastnam01' as lastname,'test_email01' as email form dual union ......);
内置参数
_parameter 代表整个参数
如果是传入单个参数,那个这个_parameter就是这个参数
如果传入多个参数,那么这个_parameter就是这个多个参数封装成的map
_databaseId 代码数据库的别名
bind标签的使用
bind可以将ognl表达式的值绑定到一个变量当中,方便后来引用这个变量的值
注意:
value中的lastName一定要是javaBean中的属性名,不能是随便的一个
<!-- public List<Employee> getEmpLike(Employee employee);演示bind标签 --> <select id="getEmpLike" resultType="employee">
<!---value中的lastName一定要是javaBean中的属性名,不能是随便的一个-->
<bind name="_lastname" value="'%'+lastName+'%'"/> <if test="_databaseId=='mysql'"> select * from tb_employee <if test="_parameter!=null"> where lastname like #{_lastname} </if> </if> </select>
sql标签
抽取可重用的sql片段 方便后面引用
比如1.比如你要查询的字段列 还有你要插入的字段列
2.include来引用已经抽取的
3.include中还可以自定义一些属性property sql标签内部就可以使用的属性
使用${} 不能使用#{}
<sql id="insertColumn"> <if test="_databaseId=='mysql'"> <if test="_parameter!=null"> lastname,password,email,did<!-- ,${testColumn} --> </if> </if> <if test="_databaseId=='oracle'"> <if test="_parameter!=null"> lastname,password,email,did </if> </if> </sql>
进行引用
<if test="_databaseId=='mysql'"> <foreach collection="emps" separator=";" item="emp"> insert into tb_employee( <include refid="insertColumn">
<!--自定义一些属性,但是引用的时候不能使用#{} 只能使用${}--> <property name="testColumn" value="abc"/> </include> ) values(#{emp.lastName},#{emp.password},#{emp.email},#{emp.document.id}) </foreach> </if>
缓存机制
一级缓存
默认打开的
作用范围是sqlsession的范围
与数据库同一次会话期间,查询到的数据会放在本地缓存当中,以后如果需要获取相同的数据,直接从缓存中拿,不用再去数据库查询
一级缓存失效的情况
1.不同的sqlsession
2.相同的sqlsession,不同的查询条件
3.sqlsession相同,但是两次查询之间进行了增删改(这次增删改可能改变了你要查询的)
4.sqlsesion相同,但是一级缓存被清空了
二级缓存
基于namespace级别的缓存,一个namespace对应一个二级缓存
工作机制:
1.1个会话,查询一条数据,查询到的数据会被放在一级缓存当中
2.如果会话关闭,一级缓存当中的数据会被放入到二级缓存当中,新的会话,就可以参照二级缓存中的数据
3.不同的namaspace查出的数据会放在自己的缓存当中
二级缓存的使用及细节
1.开启全程配置cacheEnabled
<setting name="cacheEnabled" value="true"/>
2.在对应的namespace文件中配置二级缓存
<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache>
cache标签
cache标签的详解
属性:
eviction:缓存的回收策略,默认值为LRU
值:
LRU 最近最少使用;移除最长时间不被使用的对象
FIFO 先进先出 ;按对象进入缓存中的顺序来移除他们
SOFT:软引用;移除基于垃圾回收器状态和软引用规则的对象
weak:弱引用;更积极地移除基于垃圾收集器状态和弱引用规则的对象
FlusHInterval:缓存刷新间隔
缓存多长事件清空一次,默认不清空,设置一个毫秒值
readonly:是否只读,默认为false
true:只读:mybatis会认为 所有从缓存中获取数据的操作都是只读操作,不会修改数据
mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户,不安全但是速度快
false:不只读 mybatis觉得数据会被修改,会利用序列化和反序列的技术克隆一份新的数据给你,安全,但是速度慢。
size:缓存当中存放多少数据
type:指定自定义缓存的全类名
要想自定义缓存,实现cache接口
3.因为readonly的默认值为false ;mybatis会利用反序列化和序列化技术克隆一份新的数据给你。所以你使用的pojo需要实现序列化接口(javaBean种的对象实现序列化接口)
public class Employee implements Serializable{
细节:不同的会话,但要相同的SqlSessionFactory
会话关闭,才会放入到二级缓存当中
查出的数据会放入一级缓存当中,只有这个一级缓存当中的会话关闭,一级缓存当中的数据才会放入二级缓存当中
package com.jdztc.mybatis.test; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import com.jdztc.mybatis.dao.EmployeeMapper; import com.jdztc.mybatis.javabean.Document; import com.jdztc.mybatis.javabean.Employee; public class MyBatisTest { public static SqlSessionFactory getSqlSessionFactory() throws IOException{ String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); return sqlSessionFactory; } @Test public void getEmpolyees() throws IOException { SqlSession session = getSqlSessionFactory().openSession(); try { Employee employee = session.selectOne("com.jdztc.mybatis.dao.EmployeeMapper.getEmployeeById", 1); System.out.println(employee.toString()); }finally { session.close(); } } //一级缓存体验 @Test public void test01() throws IOException { SqlSession session=getSqlSessionFactory().openSession(); try { //得到的是一个接口实现类代理对象 EmployeeMapper mapper=session.getMapper(EmployeeMapper.class); Employee emp01=mapper.getEmployeeById(1); Employee emp02=mapper.getEmployeeById(1); System.out.println(emp01); System.out.println(emp02); }finally{ session.close(); } } //不同的会话 SqlSession导致一级缓存失效 @Test public void getEmployees2() throws IOException { SqlSessionFactory sqlSessionFactory=getSqlSessionFactory(); SqlSession session=sqlSessionFactory.openSession(); SqlSession session1=sqlSessionFactory.openSession(); try { //得到的是一个接口实现类代理对象 EmployeeMapper mapper=session.getMapper(EmployeeMapper.class); EmployeeMapper mapper1=session1.getMapper(EmployeeMapper.class); Employee emp01=mapper.getEmployeeById(1); Employee emp02=mapper1.getEmployeeById(1); System.out.println(emp01); System.out.println(emp02); }finally{ session.close(); } } //相同的会话查询条件不一样 会导致一级缓存不一样 @Test public void test02() throws IOException { SqlSession session=getSqlSessionFactory().openSession(); try { //得到的是一个接口实现类代理对象 EmployeeMapper mapper=session.getMapper(EmployeeMapper.class); Employee emp01=mapper.getEmployeeById(1); Employee emp02=mapper.getEmployeeById(2); Employee emp03=mapper.getEmployeeById(2); System.out.println(emp01); System.out.println(emp02); System.out.println(emp03); }finally{ session.close(); } } //相同的会话两个查询之间进行了增删改 //还有一个调用清除缓存的办法 sesion.clearCache() @Test public void test() throws IOException{ SqlSession session=getSqlSessionFactory().openSession(); try { EmployeeMapper mapper=session.getMapper(EmployeeMapper.class); Employee emp01=mapper.getEmployeeById(1); List<Employee> emps=new ArrayList<Employee>(); Document document=new Document(); document.setId(1); emps.add(new Employee(null, "名邦敏", "123", "345@qq.com",document)); mapper.addEmp(emps); Employee emp02=mapper.getEmployeeById(1); System.out.println(emp01); System.out.println(emp02); } finally { // TODO: handle finally clause session.close(); } } //二级缓存体验 @Test public void testCache2() throws IOException{ //注意:要使用相同的sqlSessionFactory SqlSessionFactory sqlSessionFactory=getSqlSessionFactory(); SqlSession session=sqlSessionFactory.openSession(); SqlSession session2=sqlSessionFactory.openSession(); try { EmployeeMapper mapper=session.getMapper(EmployeeMapper.class); EmployeeMapper mapper2=session2.getMapper(EmployeeMapper.class); Employee emp01=mapper.getEmployeeById(1); System.out.println(emp01); session.close(); Employee emp02=mapper2.getEmployeeById(1); System.out.println(emp02); session2.close(); System.out.println(emp01==emp02); } finally { // TODO: handle finally clause } } };
二级缓存中的配置
1.如果在全局配置中cacheEnabled的值为false 那么只会关闭二级缓存,不会关闭一级缓存
2.每个select标签都有usercache="true" 默认为true 如果为false,那么就不使用二级缓存,使用一级缓存
3.每个增删改都有flushcache这个属性 如果他的属性值为true,那么每次增删改都会清空二级缓存,也会清空一级缓存 select 是不会清空的他的默认值为false
4.sqlsession的clearcache只是清除当前session的一级缓存
5.全局配置当中的localcachescope本地缓存作用域(一级缓存)
当前会话的所有数据保存在会话缓存当中,statement:可以禁用掉一级缓存
使用第三方缓存(以ehcache为例)
1.导入jar包
ehcache 的核心包 slf4j-api-1.6.1 slf4j-log4j-1.6.2
2.导入mybatis整合他的核心包
3.创建ehcache.xml文件
<?xml version="1.0" encoding="UTF-8" ?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> <!--磁盘保存路径 一旦超量就放到这里 --> <diskStore path="E:\aa\ehcache"/> <defaultCache maxElementsInMemory="1" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="true" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" ></defaultCache> </ehcache>
如果其他命名空间想要和他一样的缓存配置可以使用 cache-ref 标签
mybatis-spring整合
1配置事务管理器
2.开启注解事务
3.创建sessionfactoryBean 不用写那个创建sqlSessionfactory实例代码
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <!--指定全局配置文件的位置 --> <property name="configLocation" value="classpath:mybatis-config.xml"></property> <!-- 映射文件名与接口的包名不一致时可以配置这个 <property name="mapperLocations" value=""></property> --> </bean>
4.扫描所有的mapper接口,让这些mapper能够自动注入
mybatis-spring:scan 标签
也可以使用配置Bean的方式
MapperScannerConfigurer 中的属性basepackage属性值为接口包名
<!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> <!--整合mybatis 创建SqlSessionFacotry --> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <!--指定全局配置文件的位置 --> <property name="configLocation" value="classpath:mybatis-config.xml"></property> <!-- 映射文件名与接口的包名不一致时可以配置这个 <property name="mapperLocations" value=""></property> --> </bean> <!--扫描所有的mapper接口的实现,让这些mapper能够自动注入 --> <mybatis-spring:scan base-package="com.jdztc.mybatis.dao"/> <!--老一点的使mapper能够自动注入 配置MapperScannerConfigurer--> <!-- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.jdztc.mybatis.dao"></property> </bean> -->