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> -->
        

 

posted @ 2017-09-16 17:31  0101无限的空间  阅读(482)  评论(0编辑  收藏  举报