• 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:aaehcache"/>
        <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> -->
            
  • 相关阅读:
    Acdream 1174 Sum 暴力
    Acdream 1114 Number theory 莫比乌斯反演
    Acdream 1007 快速幂,模乘法
    UVa 10023
    UVa 11027
    UVa 11029
    UVa 10820
    UVa 10791
    UVa 11121
    UVa 106
  • 原文地址:https://www.cnblogs.com/fupengpeng/p/7531925.html
Copyright © 2020-2023  润新知