mybatis

<一>:创建核心配置文件mybatis-config.xml(官方推荐名)

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

  • 首先读取在 properties 元素体内指定的属性。
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
  • 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。

因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configuratio        
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"       
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心配置文件--><configuration>
<properties resource="db.properties"/> //外部关联和内部属性同时存在时,优先外部关联    <typeAliases>
<!--<typeAlias type="com.whily.pojo.Emp" alias="Emp"/> 限定这个类的别名--> <package name="com.whily.pojo"/><!--将这个包下的类的首字母小写作为别名--> </typeAliases> <!--default-设置默认加载的环境  --> <environments default="development"> <environment id="development">
      <!-- 设置事务管理器 -->
<transactionManager type="JDBC"/>
          <!-- 配置数据源 -->
<dataSource type="POOLED">
          <!-- 配置数据源的属性 -->
<property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${uid}"/> <property name="password" value="${pwd}"/> </dataSource> </environment> </environments> <!--每一个Mapper都需要在Mybatis核心配置文件中注册--> <mappers> <mapper resource="EmpMapper.xml"/> </mappers> </configuration>

<二>:创建工具类

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 java.io.IOException;
import java.io.InputStream;

//sqlSessionFactory--> sqlSession
public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
             sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();

    }
}

 

<三>:创建实体类以及Dao接口

import org.apache.ibatis.type.Alias;

@Alias(value = "Emp")   //注解别名
public class Emp {
    private int empid;
    private String name;
    private String username;
    private String password;
    private int deptid;

    public Emp(int empid, String name, String username, String password, int deptid) {
        this.empid = empid;
        this.name = name;
        this.username = username;
        this.password = password;
        this.deptid = deptid;
    }
    public Emp() {
    }
    public int getEmpid() {
        return empid;
    }
    public void setEmpid(int empid) {
        this.empid = empid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public int getDeptid() {
        return deptid;
    }
    public void setDeptid(int deptid) {
        this.deptid = deptid;
    }
    @Override
    public String toString() {
        return "emp{" +
                "empid=" + empid +
                ", name='" + name + '\'' +
                ", username='" + username + '\'' +
                ", pwd='" + password + '\'' +
                ", deptid=" + deptid +
                '}';
    }
}

<四>:创建sql语句Mapper.xml

<?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">
<!--namespace绑定一个对应的Dao/Mapper接口-->

<mapper namespace = "com.whily.dao.EmpDao">
<select id="getEmpList" resultType="Emp">
    select * from employee;
</select>

    <select id="getEmpByid" parameterType="int" resultType="Emp">
select * from employee where empid=#{id};
    </select>

    <insert id="addEmp" parameterType="Emp" >
        insert into employee(empid,name,username,password,deptid)values (#{empid},#{name},#{username},#{password},#{deptid});
    </insert>
    <update id="update" parameterType="Emp">
        update employee set name=#{helloname},username=#{hellousername},password=#{hellopassword},deptid=#{hellodeptid} where empid=#{helloempid};
    </update>

    <!--万能Mapper-->
    <delete id="delete"  parameterType="map" >
        delete from employee where empid=#{Helloid};
    </delete>

    <select id="getEmpLike" parameterType="map" resultType="Emp">
        select * from employee where name like "%"#{value}"%";
    </select>
</mapper>

<五>:创建测试类

增删改一定要提交事务

 sqlSession.commit();
        sqlSession.close();
package com.whily.dao;

import com.whily.pojo.Emp;
import com.whily.utils.MybatisUtils;

import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class EmpDaoTest {
    @Test
    public void test() {
        //获取SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //执行SQL

        //方式一:getMapper
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        List<Emp> empList = mapper.getEmpList();
        for (Emp emp : empList) {
            System.out.println(emp);
        }
        System.out.println("******************");
        //方式二:
        List<Emp> emps = sqlSession.selectList("com.whily.dao.EmpDao.getEmpList");
        for (Emp emp : emps) {
            System.out.println(emp);
        }
        sqlSession.close();
    }

    @Test
    public void getEmpByid() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        Emp emp = mapper.getEmpByid(196);
        System.out.println(emp);
        sqlSession.close();
    }

    @Test
    public void addEmp() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        int emps = mapper.addEmp(new Emp(500, "qqq", "hahaha", "1111", 5));
        if (emps > 0) {
            System.out.println("成功");
        }
        sqlSession.commit();
        sqlSession.close();
    }

    @Test
    public void update() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("helloname", "qqq");
        map.put("hellousername", "qqq");
        map.put("hellopassword", "www");
        map.put("hellodeptid", 1);
        map.put("helloempid", 196);
        int emp = mapper.update(map);
        if (emp > 0) {
            System.out.println("成功");
        }
        sqlSession.commit();
        sqlSession.close();
    }

    @Test
    public void delete() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("Helloid", 500);
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        mapper.delete(map);
        sqlSession.commit();
        sqlSession.close();
    }

    @Test
    public void getEmpLike() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);

        List<Emp> emps = mapper.getEmpLike("%qq%");
        for (Emp emp : emps) {
            System.out.println(emp);
        }
        sqlSession.close();
    }
}

 

六、生命周期

 

 

 

每一个Mapper都是一个业务

 

 生命周期和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题。

SqlSessionFactoryBulider:

  • 一旦创建了SqlSessionFactoryBulider,就不需要他了
  • 局部变量

SqlSessionFactory:

  • 说白了可以想象为数据库连接池;
  • SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由放弃或重新创建
  • 因此SqlSessionFactory的最简作用域是应用作用域;
  • 最简单的就是单例模式或者静态单例模式。

SqlSession:

  • 连接到连接池的一个请求
  • SqlSession的实例不是线程安全的,因此不是共享的,所以他的作用域是请求或方法作用域
  • 用完之后要赶紧关闭,否则资源被占用!

 

字段和属性名不一致

  • 起别名

7、ResultMap映射:结果集映射

 <resultMap id="EmpMap" type="Emp">  
        <!--column数据库的字段  property实体类的属性-->
        <result column="empid" property="empid"/>
        <result column="username" property="username"/>
        <result column="deptid" property="deptid"/>
        <result column="name" property="name"/>
        <result column="password" property="pwd"/>  //
    </resultMap>

<select id="getEmpList" resultMap="EmpMap" //对应ResultMap的id值>
    select * from employee;
</select>
  • resultMap元素是MyBatis中最重要最强大的元素
  • ResultMap的设计思想是,对于简单的语句根本不需要配置显示的结果映射,而对于复杂一点的语句只需要描述他们的关系就行
  • ResultMap最优秀的地方在于,虽然你已经对他相当了解了,但根本就不需要显示地用到他们

设置

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

8、日志

8.1 日志工厂

如果一个数据库操作出现了异常,我们需要拍错。日志就是最好的帮手

以前:sout、debug

现在:日志工厂!    logImpl    指定Mybatis所用日志的具体表现,未指定时将自动查找。

 

  • SLF4J
  • LOG4J  (掌握)
  • LOG4J2  
  • JDK_LOGGING
  • STDOUT_LOGGING  (掌握)
  • NO_LOGGING
  • COMMONS_LOGGING

具体用使用哪一个日志实现,在设置中设定!

 

STDOUT_LOGGING

 <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>  //必须规范书写,按官方书写,不能有空格
    </settings>


@Test
public void getEmpLike() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
EmpDao mapper = sqlSession.getMapper(EmpDao.class);

List<Emp> emps = mapper.getEmpLike("%qq%");
for (Emp emp : emps) {
System.out.println(emp);
}
sqlSession.close();
}
 

Opening JDBC Connection
Created connection 385337537.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@16f7c8c1]
==> Preparing: select * from employee where name like "%"?"%";
==> Parameters: %qq%(String)
<== Columns: empid, name, username, password, deptid
<== Row: 229, qaaqq, 1111, dsa, 2
<== Row: 231, qqaqq, dsad5, dsa, 7
<== Total: 2
emp{empid=229, name='qaaqq', username='1111', pwd='dsa', deptid=2}
emp{empid=231, name='qqaqq', username='dsad5', pwd='dsa', deptid=7}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@16f7c8c1]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@16f7c8c1]
Returned connection 385337537 to pool.

 

log4j:

1、导入依赖:

 <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

2、mybatis-config.xml设置

   <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>

3、创建log4j.properties文件

log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/whily.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

4、测试类

public class EmpDaoTest {
    static Logger logger = Logger.getLogger(EmpDaoTest.class);

    @Test
      public void getEmpLike() {
    logger.info("测试:进入getEmpLike方法成功");
SqlSession sqlSession = MybatisUtils.getSqlSession();
EmpDao mapper = sqlSession.getMapper(EmpDao.class);

List<Emp> emps = mapper.getEmpLike("%qq%");
for (Emp emp : emps) {
System.out.println(emp);
}
logger.info("info:进入了testLog4j"); 
logger.error("error:进入了testLog4j");
logger.debug("debug:进入了testLog4j");
    sqlSession.close();
}
 }

简单使用:

1.在使用Log4j的类中,导入import org.apache.log4j.Logger;

2.日志对象,参数为当前类的class:static Logger logger = Logger.getLogger(EmpDaoTest.class);

3.日志级别:

logger.info("info:进入了testLog4j");
logger.debug("debug:进入了testLog4j");
logger.error("error:进入了testLog4j");

9、分页

分页可以减少数据的处理量

 

使用Limit分页:

语法:

select * from tableName limit startIndex,pageSize;

select * from employee limit 3;   #[0,n]

 9.1:Limit分页

 1.接口

分页   List<Emp> getEmpLimit(Map<String,Object> map);

 2.Mapper.xml
<select id="getEmpLimit" parameterType="map" resultMap="EmpMap">
        select * from employee limit #{i},#{n};
    </select>
 3.测试 
 @Test
    public void getEmpLimit() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("i", 1);
        map.put("n", 3);
        List<Emp> empLimit = mapper.getEmpLimit(map);
        for (Emp emp:empLimit){
            System.out.println(emp);
        }
        sqlSession.close();
    }

9.2:使用RowBounds(不推荐,因为需要使用selectList(),官方放弃了这个方法)


1.接口 :List<Emp> getEmpRowBounds();


2.Mapper.xml

<select id="getEmpLimit" parameterType="map" resultMap="EmpMap">
        select * from employee;
    </select>

 

3.测试 


@Test
public void getEmpRowBounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//RowBounds实现
RowBounds rowBounds = new RowBounds(1,3);
List<Emp> emps = sqlSession.selectList("com.whily.dao.EmpDao.getEmpRowBounds",null,rowBounds);
for (Emp emp:emps){
System.out.println(emp);
}
sqlSession.close();
}

9.3:插件分页

1.到依赖

 

<dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.2</version>
        </dependency>

 

2.导入插件

<plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <property name="helperDialect" value="mysql"/>
            <property name="reasonable" value="true"/>
        </plugin>
    </plugins>

3.Dao

List<Emp> listByHelper();  //条件查询是需要传入参数

4.Mapper.xml

 

<select id="listByHelper" resultMap="EmpMap">
    select * from employee      
</select>

sql语句后面不能加“;”

 

 

5.测试

@Test
    public void ss() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);

        PageHelper.startPage(2, 2);
        List<Emp> emps = mapper.listByHelper();
        PageInfo<Emp> info = new PageInfo<Emp>(emps);
        emps = info.getList();
        for (Emp emp : emps) {
            System.out.println(emp);
        }
    }

10.注解开发

 

修饰在接口方法上,然后加相应的sql语句

@Select
            @Select("select * from emp")
            public List<Emp> listAnn(Emp emp); 
@Insert
            @Insert("insert into emp (ename) values (#{ename})")

            public int saveAnn(Emp emp);
@Update
            @Update("update emp set ename=#{ename} where empno=#{empno}")
            public int editAnn(Emp emp);
@Delete
            @Delete("delete from emp  where empno=#{empno}")
            public int deleteAnn(Emp emp);

@Results
            @Results(value={
                    @Result(property = "sal", column = "salary")
            })
            @Select("select * from emp")
            public List<Emp> listAnn(Emp emp); 

#{}和${}:

where role_id = #{roleId};

#{}:传来参数时,sql在解析时会家伙是哪个“ ”,当成字符串来解析,防止sql注入。

${}:传入数据直接显示在生成的sql中,解析式就成了role_id = roleId,运行时会报错。

所以尽可能用#{},除非Mybatis排序时使用order by动态参数时需要使用${}而不是#{}

11、 连表查询

一:多对一映射

1、创建mybatis-config.xml  实体类   接口(Dao或Mapper)

2、Mapeer.xml文件:

       1.方式一,不使用连接查询,效率一般:

 

    <resultMap id="StudentTeacher" type="stu">
        <result property="stuid" column="stuid" javaType="int"/>
        <result property="stuName" column="stuName" javaType="string"/>
        <association property="teacher" column="tid" javaType="tea" select="getTeacher" //连接第二条查询语句  >
            <id property="tchid" column="tchid" javaType="int"></id>
            <result property="tchName" column="tchName" javaType="string"></result>
        </association>
    </resultMap>

    <select id="getStudents" resultMap="StudentTeacher">
        select * from  student
    </select>

    <select id="getTeacher" resultType="tea" parameterType="int">
    select  * from teacher where tchid =#{tchid} ;
    </select>

   

  2.方式二,使用连接查询:

    <select id="getStudents2" resultMap="StudentTeacher2">
        select  s.stuid sid,s.stuName sname,t.tchid tchid,t.tchName tname
        from student s ,teacher t
        where s.tid = t.tchid
    </select>

    <resultMap id="StudentTeacher2" type="stu">
        <result property="stuid" column="sid"/>
        <result property="stuName" column="sname"/>
        <association property="teacher" javaType="tea">
            <id property="tchid" column="tchid"/>
            <result property="tchName" column="tname"/>
        </association>
    </resultMap>

二:一对多映射

 1.方式一,不使用连接查询:

<select id="getTeacher2" resultMap="TeacherStu2">
        select  tchid,tchName from teacher where tchid = #{tchid}
    </select>
    <resultMap id="TeacherStu2" type="tea">
        <result property="tchid" column="tchid"/>
        <result property="tchName" column="tchName"/>
        <collection property="students" javaType="ArrayList" ofType="stu" column="tchid"//对应第一条语句的tchid select="getStudentByID"连接第二条语句>
            <result property="stuid" column="stuid"/>
            <result property="stuName" column="stuName"/>
            <result property="tid" column="tid"/>
        </collection>
    <select id="getStudentByID" resultType="stu">
        select stuid,stuName,tid from student where tid = #{tchid}
    </select>
</resultMap>

  2.方式二,使用连接查询:

    <select id="getTeacher" resultType="tea" resultMap="TeacherStu">
    select t.tchid ,t.tchName,s.stuid,s.stuName,s.tid from student s,teacher t where s.tid = t.tchid and tchid = #{tchid}
</select>
    <resultMap id="TeacherStu" type="tea">
        <result property="tchid" column="tchid"/>
        <result property="tchName" column="tchName"/>
        <collection property="students" ofType="stu">
            <result property="stuid" column="stuid"/>
            <result property="stuName" column="stuName"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>

 

12、动态SQL

官网描述:
  MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。
例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。
动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,
现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。

  -------------------------------
  - if
  - choose (when, otherwise)
  - trim (where, set)
  - foreach
  -------------------------------

我们之前写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL ,稍微不注意,由于引号,空格等缺失可能都会导致错误。

那么怎么去解决这个问题呢?这就要使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。

if:   接口为List<Student> queryStuIf(Map map);

 <select id="queryStuIf" parameterType="map" resultType="stu">
        select * from student where
        <if test="stuid!=null">stuid=#{stuid}</if>
        <if test="stuName!=null">and stuName=#{stuName}</if>
    </select>

完整语句为select * from student where stuid = #{stuid} and stuName = #{stuName},

当stuName为空时select * from student where stuid = #{stuid} 

但当stuid为空时语句就成为了select * from student where and stuName = #{stuName},sql语句错误。

如何解决:where

 

WHERE:接口同上

修改以上sql语句:

    <select id="queryStuIf" parameterType="map" resultType="stu">
        select * from student where
        <where>
        <if test="stuid!=null">stuid=#{stuid}</if>
        <if test="stuName!=null">and stuName=#{stuName}</if>
        </where>
    </select>
public void queryOne(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    HashMap<Object,Object> map = new HashMap<Object, Object>();
    map.put("stuid",1);
    map.put("stuName",null);
    List<Student> list = mapper.queryStuIf(map);
    System.out.println(list);
    sqlSession.close();
}

这个“where” 标签会知道如果他包含的标签中有返回值的话,太插入一个‘where’。此外,如果标签返回的内容是以AND

或 OR开头的话,则会剔除掉。

 

SET:接口为int update(Map map);

同理:上面的对于查询SQL语句包含where关键字,如果在进行更新操作时,含有set关键词,我们怎么处理

    <update id="update" parameterType="map" >
        update student
        <set>
            <if test="hobby!=null">hobby=#{hobby},</if>  //注意set与set之间需要加逗号隔开
            <if test="stuName">stuName=#{stuName}</if>
        </set>
        where stuid = #{stuid}
    </update>

 public void update(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    HashMap<String,Object> map = new HashMap<String, Object>();
    map.put("stuName","熊大");
    map.put("hobby","dance");
    map.put("stuid",1);
    mapper.update(map);
    sqlSession.commit();
    sqlSession.close();
}

CHOOSE语句:List<Student> queryChoose(Map map);

有时候我们不想用到所有的查询条件,只想选择其中一个,查询条件只有一个满足即可,

使用choose标签可以解决此类问题,类似于java的switch语句

    <select id="queryChoose" parameterType="map" resultType="stu">
        select * from student
        <where>
            <choose>
                <when test="stuid">stuid=#{stuid}</when>
                <when test="stuName">stuName=#{stuName}</when>
                <when test="hobby">hobby#{hobby}</when>
                <otherwise>and view = #{view}</otherwise>
            </choose>
        </where>
    </select>
    public void queryChoose() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("stuName", "张三");
        //map.put("stuid",1);
        // map.put("hobby","lol");
        // map.put("view",1);
        List<Student> list = mapper.queryChoose(map);
        System.out.println(list);
        sqlSession.close();

 SQL片段:接口List<Student> queryFra(Map map);

有时候可能某个sql语句用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。

    <!--提取片段-->
    <sql id="if-stuid-stuName">
        <if test="stuid!=null">stuid=#{stuid}</if>
        <if test="stuName!=null">stuName=#{stuName}</if>
    </sql>
    
    <!--使用片段-->
    <select id="queryFra" parameterType="map" resultType="stu">
        select * from student
        <where>
        <include refid="if-stuid-stuName"></include>
        </where>
    </select>
 public void queryFra(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();
    //map.put("stuName", "张三");
    map.put("stuid",1);
    List<Student> list=mapper.queryFra(map);
    System.out.println(list);
    sqlSession.close();
}

注意:提取片段时,最好基于单表来定义sql片段,提高片段的可重用性;

   在sql片段中不要包括where

 

FOREACH:接口List<Student> queryForeach(Map map);

 

collection:指定输入对象中的集合属性
   item:每次遍历生成的对象
   open:开始遍历时的拼接字符串
   close:结束时拼接的字符串
   separator:遍历对象之间需要拼接的字符串
   select * from blog where 1=1 and (id=1 or id=2 or id=3)
 -->

    <select id="queryForeach" parameterType="map" resultType="stu">
        select * from student
        <where>
            <foreach collection="stuids" item="stuid" open="and (" close=")" separator=" or ">stuid=#{stuid}</foreach>//注意空格
        </where>
    </select>
    public void queryForeach(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        HashMap map = new HashMap();
        List<Integer> ids = new ArrayList<Integer>();
        ids.add(1);
        ids.add(2);
        ids.add(3);
        map.put("stuids",ids);
        List<Student> students = mapper.queryForeach(map);
        System.out.println(students);
        sqlSession.close();
    }

13、缓存

1.什么是缓存:
  • 存在内存中的临时数据。
  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
2.为什么使用缓存:
  • 减少和数据库的交互次数,减少系统开销,提高系统效率。
3.为什么使用缓存:
  • 经常查询并且不经常改变的数据。

13.1、Mybatis缓存

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
  • MyBatis系统中默认定义了两级缓存:一级缓存二级缓存
    •  默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
    •  二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
    • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

 

13.1、一级缓存

一级缓存也叫本地缓存:

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中。

  • 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

@org.junit.Test
    public void queryOne(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
       Student i = mapper.queryOne(1);
        System.out.println(i);
        Student j = mapper.queryOne(1);
        System.out.println(j);
        System.out.println(i==j);
        sqlSession.close();
    }


 

 一级缓存失效的四种情况:

一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;

一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求!

 

1.sqlSession不同:

    @org.junit.Test
    public void queryOne(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
SqlSession sqlSession1
=MybatisUtils.getSqlSession(); StudentMapper mapper1 = sqlSession1.getMapper(StudentMapper.class);
Student i
= mapper.queryOne(1); System.out.println(i);
Student j
= mapper1.queryOne(1); System.out.println(j); System.out.println(i==j); sqlSession.close(); sqlSession1.close(); }

观察结果:发现发送了两条SQL语句!

结论:每个sqlSession中的缓存相互独立


 

2.sqlSession相同,查询条件不同

3.sqlSession相同,两次查询之间执行了增删改操作!无论改的是哪个对象,增删改会刷新缓存

4.sqlSession相同,手动清除一级缓存

    @org.junit.Test
    public void queryOne(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student i = mapper.queryOne(1);
        System.out.println(i);
        
        sqlSession.clearCache();//手动清除缓存
        Student j = mapper.queryOne(1);
        System.out.println(j);
        System.out.println(i==j);
        sqlSession.close();
    }

一级缓存就是一个map

 

 

13.2 、二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存

  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;

  • 工作机制

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;

    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;

    • 新的会话查询信息,就可以从二级缓存中获取内容;

    • 不同的mapper查出的数据会放在自己对应的缓存(map)中;

 使用步骤:

1、开启全局缓存【mybatis-config.xml】

<setting name="cacheEnabled" value="true"/>

2、去每个mapper.xml中配置使用二级缓存,这个配置非常简单;【xxxMapper.xml】

<mapper namespace="com.whily.dao.StudentMapper">
// 在当前Mapper.xml中使用二级缓存
<cache/> //开启二级缓存 <select id="queryOne" resultType="stu"> select * from student where stuid=#{stuid} </select> </mapper>
<cache/>

官方示例=====>查看官方文档
<cache
 eviction="FIFO"
 flushInterval="60000"
 size="512"
 readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,
而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突

3.测试

    @org.junit.Test
    public void queryOne(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        SqlSession sqlSession1 = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        StudentMapper mapper1 = sqlSession.getMapper(StudentMapper.class);
        Student i = mapper.queryOne(1);
        System.out.println(i);

        Student j = mapper1.queryOne(1);
        System.out.println(j);
        System.out.println(i==j);
        sqlSession.close();
        sqlSession1.close();
    }
org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[com.whily.dao.StudentMapper]-Cache Hit Ratio [com.whily.dao.StudentMapper]: 0.0
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 11902257.
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@b59d31]
[com.whily.dao.StudentMapper.queryOne]-==>  Preparing: select * from student where stuid=? 
[com.whily.dao.StudentMapper.queryOne]-==> Parameters: 1(Integer)
[com.whily.dao.StudentMapper.queryOne]-<==      Total: 1
Student{stuid=1, stuName='熊大', hobby=dance, tid=1}
[com.whily.dao.StudentMapper]-Cache Hit Ratio [com.whily.dao.StudentMapper]: 0.0
Student{stuid=1, stuName='熊大', hobby=dance, tid=1}
true

4.结论

  • 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据

  • 查出的数据都会被默认先放在一级缓存中

  • 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中

5.缓存原理图

 

 13.3、第三方缓存【EHCache】

第三方缓存实现--EhCache: 查看百度百科

Ehcache是一种广泛使用的java分布式缓存,用于通用缓存;

要在应用程序中使用Ehcache,需要引入依赖的jar包

<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
   <groupId>org.mybatis.caches</groupId>
   <artifactId>mybatis-ehcache</artifactId>
   <version>1.1.0</version>
</dependency>
在mapper.xml中使用对应的缓存即可

<mapper namespace = “org.acme.FooMapper” > 
   <cache type = “org.mybatis.caches.ehcache.EhcacheCache” /> 
</mapper>
编写ehcache.xml文件,如果在加载时未找到/ehcache.xml资源或出现问题,则将使用默认配置。
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
        updateCheck="false">
   <!--
      diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
      user.home – 用户主目录
      user.dir – 用户当前工作目录
      java.io.tmpdir – 默认临时文件路径
    -->
   <diskStore path="./tmpdir/Tmp_EhCache"/>
   
   <defaultCache
           eternal="false"
           maxElementsInMemory="10000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="259200"
           memoryStoreEvictionPolicy="LRU"/>

   <cache
           name="cloud_user"
           eternal="false"
           maxElementsInMemory="5000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           memoryStoreEvictionPolicy="LRU"/>
   <!--
      defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
    -->
   <!--
     name:缓存名称。
     maxElementsInMemory:缓存最大数目
     maxElementsOnDisk:硬盘最大缓存个数。
     eternal:对象是否永久有效,一但设置了,timeout将不起作用。
     overflowToDisk:是否保存到磁盘,当系统当机时
     timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
     timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
     diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
     diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
     diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
     memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
     clearOnFlush:内存数量最大时是否清除。
     memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
     FIFO,first in first out,这个是大家最熟的,先进先出。
     LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
     LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
  -->

</ehcache>

 

posted @ 2020-10-20 09:19  whily  阅读(210)  评论(0编辑  收藏  举报