Mybatis

目录

mybatis简介

mybatis是什么?

mybatis搭建1.0

1.创建一个maven项目,添加mybatis,mysql依赖的jar

2.创建本地的git仓库

 3.创建一个数据库表,以及一个应的java模型类

 4.创建mybatis全局配置文件,配置数据库连接信息​编辑

5.创建sql映射文件  定义一个与接口方法名相同的查询语句,配置sql映射文件

 6.创建一个访问接口  定义一个方法

7.测试mybatis

 mybatis搭建2.0

1.环境

 2.事务管理

3.配置数据源 

pool数据库连接池(缓冲池)

数据库连接池思想

 4.1创建并导入配置属性文件

 4.2调整设置

 4.3别名

 5.TestAdmin 解释每一操作步骤的含义

6.进行一个简单的封装处理

 API说明

mybatis测试

增删改

添加

 修改

 删除

结果处理 

一般查询结果

 无条件查询结果

对象映射

自动映射

手动映射

 多表关联处理

准备工作

关联查询

嵌套查询

 注解方式

mybatis的动态SQL

if

where,set

trim

choose,when

foreach

补充:

 mybatis的一级缓存和二级缓存

引入

mybatis一级缓存


【前言】本文只作为作者的学习记录,如果能帮到各位技术萌新,是我的荣幸,如果有专业失误,也烦请大佬指出。

mybatis简介

mybatis是什么?

原来是Apache的一个 开源项目iBatis,2010年转投谷歌,从ibatis3.0开始更名为MyBatis。

是一个优秀的数据持久层(dao)

这里牵扯到java后端的一个三层架构

servlet(web) 接受请求,调用其他java程序处理,响应

service 业务逻辑层

dao 数据访问/持久

mybatis是对jdbc进行一个轻量级封装

提供一些自己定义的类和接口来实现功能

提供专门xml文件来进行配置,以及可以自动的对查询结果进行封装

是一个ORM(java对象与数据库映射)实现的数据持久层的框架

支持动态sql,以及数据缓存

这里附上Mybatis中文网站

mybatis搭建1.0

前三步可视为准备工作

1.创建一个maven项目,添加mybatis,mysql依赖的jar

 下载到本地仓库(更新)

2.创建本地的git仓库

 3.创建一个数据库表,以及一个应的java模型类

 创建过滤文件(忽略)

 4.创建mybatis全局配置文件,配置数据库连接信息

5.创建sql映射文件  定义一个与接口方法名相同的查询语句,配置sql映射文件

 6.创建一个访问接口  定义一个方法

7.测试mybatis

 接口代码:

import com.ffyc.mybatis.model.Admin;

public interface AdminDao {

    public Admin FindAdminByid(int id);
}

测试类代码:mybatis搭建(第一代)

import com.ffyc.mybatis.dao.AdminDao;
import com.ffyc.mybatis.model.Admin;
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.Reader;
public class TestAdmin {
    public static void main(String[] args) throws IOException {
        //1.读取配置文件
        Reader reader = Resources.getResourceAsReader("mybatis.xml");
        //2.创建 SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        //3.创建 SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //4.获得接口代理对象
        AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
        //5.使用这个对象去调用对应方法
        Admin admin = adminDao.FindAdminByid(1);
        System.out.println(admin);
        //6.关闭SqlSession
        sqlSession.close();
    }
}

 mybatis搭建2.0

1.环境

指定环境俩个实现方式

1.创建SqlSession时指定

代码:

SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader,"development");

 2.配置环境是指定为默认环境

代码:

<environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/><!--事物管理类型-->
            <dataSource type="POOLED"><!--配置数据源   type="POOLED" 是否使用数据库连接池-->
                <property name="driver" value="${driveName}" />
                <property name="url" value="${url}" />
                <property name="username" value="${uname}" />
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
        <environment id="work">
            <transactionManager type="JDBC"/><!--事物管理类型-->
            <dataSource type="POOLED"><!--配置数据源   type="POOLED" 是否使用数据库连接池-->
                <property name="driver" value="${driveName}" />
                <property name="url" value="${url}" />
                <property name="username" value="${uname}" />
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

 2.事务管理

数据库事务

是对一次数据库操作过程中的多条执行sql进行管理控制

保证一次执行中的多条sql能够作为一个整体,要么都执行成功,要么都执行失败。

3.配置数据源 

pool数据库连接池(缓冲池)

现在每与数据库交互一次,创建一次数据库连接对象(Connection,SqlSession),用完就会关闭销毁

下一次需要,重复出现这过程,

问题:频繁创建销毁对象,需要开销(内存,效率)

池的概念:可以理解为一个集合

数据库连接池思想

可以在启动时设置一个容器,在里面初始化好一些数据库连接对象

有请求到来时,可以不用每次都创建销毁,可以重复使用。

减少了频繁创建销毁连接对象的开销

一般的设置

初始连接对象的数量

最大连接对象的数量

最大等待时间

 4.1创建并导入配置属性文件

创建属性文件,多组键值对 

 配置属性文件

 4.2调整设置

代码:

<settings>
    <!--日志功能-->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

 4.3别名

 5.TestAdmin 解释每一操作步骤的含义

1.

//读取mybatis核心配置文件
Reader reader =  Resources.getResourceAsReader("mybatis.xml");

2. 

//创建SqlSessionFactory
//SqlSessionFactory封装了所有的配置信息,
//SqlSessionFactory负责生成一个数据库连接的会话对象SqlSession对象
//SqlSessionFactory创建开销比较大,所以在整个项目中只需要创建一次即可,不用销毁
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);

3.

//创建SqlSession  session会话,一次与数据库交互,类似于之前使用的Connection
//SqlSessionFactory中的openSession()方法用来创建一个SqlSession对象,默认无参的默认设置事物提交为false(手动提交)
SqlSession sqlSession =  sessionFactory.openSession();

4.

//创建访问接口的代理对象
//现在mybatis建议使用接口化访问, 先定义一个接口(规范),把每个接口与sql映射文件进行绑定
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);

5.

//使用代理对象访问接口中对应的方法,本质是调用的是接口对应的sql映射文件中的那个sql
Admin admin =   adminDao.findAdminById(2);

6.

//关闭与数据库连接会话对象
//
sqlSession.close();

6.进行一个简单的封装处理

将读取配置文件,创建SqlSessionFactory以及创建一个SqlSession对象封装成一个类

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.Reader;

public class MybatisUtil {
    static SqlSessionFactory sessionFactory = null;
    static{
        Reader reader = null;
        try {
            reader = Resources.getResourceAsReader("mybatis.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
        sessionFactory = new SqlSessionFactoryBuilder().build(reader);
    }
    public static SqlSession getSqlSession(){
        return sessionFactory.openSession();
    }
}

 测试

 API说明

#{}与${}的区别
     #{参数名}: 首先是采用预编译的方式传值,一般用于向sql中传值使用,更加的安全
     ${参数名}: '${参数名}' 使用字符串拼接方式传值,不安全,  一般可以用于动态向sql中传列名

举个例子:

购物网站的排序,需要直接读取到属性名时,直接使用'${属性名}'就行。

<select>
    select * from goods  order by ${列名}
</select>

mybatis测试

增删改

添加

代码:

    <!--增,改,删-->
    <insert id="insertAdmin" parameterType="Admin" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
        insert into admins(account,password) value (#{account},#{password})
    </insert>
    <update id="updateAdmin" parameterType="Admin">
        update admins set account=#{account},password=#{password} where id = #{id}
    </update>
    <delete id="deleteAdmin" parameterType="int">
        delete from admins where id = #{id}
    </delete>

 代码:

import com.ffyc.mybatis.model.Admin;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface AdminDao {

    public Admin findAdminByid(int id);
    //public void saveAdmin1(@Param("acc") String account, @Param("pwd") String password);
    //void saveAdmin(Admin admin);
    void insertAdmin(Admin admin);
    void updateAdmin(Admin admin);
    void deleteAdmin(int id);
    //List<Admin> findAdmin();
    //List<Admin> findAdmins();//resultMapdemo
}

 Test代码:

import com.ffyc.mybatis.dao.AdminDao;
import com.ffyc.mybatis.model.Admin;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.io.IOException;

public class TestAdmin3 {

    public static void main(String[] args) throws IOException {
        //增加
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        AdminDao adminDao = sqlSession.getMapper(AdminDao.class);

        //String account = "tom";
        //String password = "123";
        //adminDao.saveAdmin1(account,password);

        String account = "jack";
        String password = "222";
        Admin admin = new Admin();
        admin.setAccount(account);
        admin.setPassword(password);
        adminDao.insertAdmin(admin);

        sqlSession.commit();
        System.out.println(admin.getId());
        sqlSession.close();

        
    }

}

 修改

import com.ffyc.mybatis.dao.AdminDao;
import com.ffyc.mybatis.model.Admin;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.io.IOException;

public class TestAdmin3 {

    public static void main(String[] args) throws IOException {
        //修改
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
        String account = "JACK";
        String password = "000";
        Admin admin = new Admin();
        admin.setId(4);
        admin.setAccount(account);
        admin.setPassword(password);
        adminDao.updateAdmin(admin);
        sqlSession.commit();
        sqlSession.close();
    }

}

 删除

import com.ffyc.mybatis.dao.AdminDao;
import com.ffyc.mybatis.model.Admin;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.io.IOException;

public class TestAdmin3 {

    public static void main(String[] args) throws IOException {
        //删除
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
        adminDao.deleteAdmin(3);
        sqlSession.commit();
        sqlSession.close();
    }

}

结果处理 

一般查询结果

<select id="findAdminByid" resultType="int">
    select count(*) from admins
</select>

返回值一般是简单类型

 无条件查询结果

一般情况下会查询到多条数据,这是就需要集合来接收。

List<Admin> findAdmin();

对象映射

自动映射

当数据库列名与java模型中的属性不一致时,该属性值无法被封装到对象中。

解决办法:

1.将不匹配的列名as成java模型的属性名,就可以正常封装到对象中。

2.Mybatis中的对象映射同样可以解决这样的问题,当然前提是,数据库中使用标准的下划线格式像user_name,并且java模型类中使用标准的驼峰命名格式的时候,像userName。在mybatis.xml中设置自动映射即可。

代码:

<settings>
        <!--日志功能-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="autoMappingBehavior" value="PARTIAL"/>
    </settings>

 编程小tip:类名的首字母要大写,目的就是为了与创建的对象区分,还有创建类之后,如果生成了有参的构造方法,要将无参的构造方法写出来,方便后续使用,养成良好的变成习惯。

手动映射

也就是特殊定义处理

1. resutlMap 的 id 属性是 resutlMap 的唯一标识

2.resutlMap 的 id 属性是映射的 POJO 类

3.id 标签映射主键,result 标签映射非主键

4.property 设置 POJO 的属性名称,column 映射查询结果的列名称

代码:

<!--特殊处理定义 手动配置映射管理 id是唯一的 使用resultMap时会使用id type是类型-->
    <resultMap id="searchAdmin" type="Admin">
        <!--<id column="" property=""></id>--><!--id标签指针对数据库表中的主键-->
        <result column="us" property="userName"></result>
    </resultMap>

    <!--使用resultMap 的一个demo-->
    <select id="findAdmins" resultType="Admin" resultMap="searchAdmin">
        select id,account,password,user_name as us from admins
    </select>

 多表关联处理

准备工作

1.创建表

2.创建模型类

3.创建接口,定义抽象方法

关联查询

代码:

<resultMap id="searchStudent" type="Student">
        <result column="id" property="id"></result>
        <result column="num" property="num"></result>
        <result column="name" property="name"></result>
        <result column="gender" property="gender"></result>
        <association property="dorm" javaType="Dorm">
            <result column="dnum" property="num"></result>
        </association>
        <association property="admin" javaType="Admin">
            <result column="account" property="account"></result>
        </association>
    </resultMap>

    <select id="findStudentByid" resultMap="searchStudent" parameterType="int">
        SELECT
            s.id,
            s.num,
            s.name,
            s.gender,
            d.num dnum,
            a.account
            FROM student s LEFT JOIN dorm d ON s.dormid = d.id
                           LEFT JOIN admins a ON s.adminid = a.id
            WHERE s.id = #{id}
    </select>

嵌套查询

可以看做是多个单表查询,将一个多表关联查询拆分为多次查询,先查询主表数据,然后查询关联表数据。

代码:

    <resultMap id="searchStudent1" type="Student">
        <result column="id" property="id"></result>
        <result column="num" property="num"></result>
        <result column="name" property="name"></result>
        <result column="gender" property="gender"></result>
        <association property="dorm" javaType="Dorm" select="findDormByid" column="dormid"></association>
        <association property="admin" javaType="Admin" select="findAdminByid" column="adminid"></association>
    </resultMap>
    <select id="findStudentByid1" resultType="Student" parameterType="int" resultMap="searchStudent1">
        SELECT id,num,NAME,gender,dormid,adminid FROM student WHERE id = #{id}
    </select>
    <select id="findDormByid" resultType="Dorm">
        select num from dorm where id = #{dormid}
    </select>
    <select id="findAdminByid" resultType="Admin">
        select account from admins where id = #{adminid}
    </select>

<resultMap>配置自动映射,id有且唯一,type,类型这里是Student类,

<result column=" ",property = " " > column 数据库中对应的列名,property是java模型的属性

<association> 配置select 属性,column属性,进行单表查询,将结果封装到Dorm对象中,在封装到Student对象中。
select属性的值创建语句,进行单表查询。
collection 和 association 都需要配置 select 和 column 属性,两者配置方法。
<association>中的column属性与主表的外键要一致。
无条件查询List

 查询宿舍

1.一个宿舍中有多个学生

代码:

<resultMap id="searchDorm" type="Dorm">
        <id column="id" property="id"></id>
        <result column="num" property="num"></result>
        <collection property="list" javaType="list" ofType="Student">
            <result column="snum" property="num"></result>
            <result column="name" property="name"></result>
            <result column="gender" property="gender"></result>
        </collection>
    </resultMap>
    <select id="findDorm" resultMap="searchDorm" parameterType="int">
        SELECT
          d.id,
          d.num,
          s.num snum,
          s.name,
          s.gender
        FROM dorm d LEFT JOIN student s ON d.id = s.dormid WHERE d.id= #{id}
    </select>

 测试代码:

import com.ffyc.mybatis.dao.DormDao;
import com.ffyc.mybatis.model.Dorm;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.Date;
import java.util.List;

public class TestDorm {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        DormDao dormDao = sqlSession.getMapper(DormDao.class);
        Dorm dorm = dormDao.findDorm(1);
        System.out.println(dorm);
        //List<Dorm> list = dormDao.findAllDorm();
        //List<Dorm> list = dormDao.findAllDorm1();
        //System.out.println(list);
        /*
        for(Dorm dorm:list){
            System.out.print(dorm.getNum());
            for(Student student: dorm.getList()){
                System.out.println(student.getName());
            }
        }
        */
        sqlSession.close();
    }

}

2.查询所有的宿舍

嵌套查询

配置映射:

代码:

<resultMap id="searchAll" type="Dorm">
        <id column="id" property="id"></id>
        <result column="num" property="num"></result>
        <collection property="list" javaType="list" ofType="Student" select="findStudentBydormid" column="id"></collection>
    </resultMap>
    <select id="findAllDorm1" resultMap="searchAll">
        SELECT id,num FROM dorm
    </select>

    <select id="findStudentBydormid" parameterType="int" resultType="Student">
        select id,num,name,gender from student where dormid =#{id}
    </select>

 Test测试

测试代码:

import com.ffyc.mybatis.dao.DormDao;
import com.ffyc.mybatis.model.Dorm;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.Date;
import java.util.List;

public class TestDorm {
    public static void main(String[] args) {
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        DormDao dormDao = sqlSession.getMapper(DormDao.class);
        //Dorm dorm = dormDao.findDorm(1);
        //System.out.println(dorm);
        //List<Dorm> list = dormDao.findAllDorm();
        List<Dorm> list = dormDao.findAllDorm1();
        System.out.println(list);
        for(Dorm dorm:list){
            System.out.print(dorm.getNum());
            for(Student student: dorm.getList()){
                System.out.println(student.getName());
            }
        }
        sqlSession.close();
    }

}

 测试结果

通过日志可以很清楚的看到宿舍对应学生的关系

 结果:

[Dorm{id=1, num=101, list=[Student{id=1, num=1001, name='张三', gender='男', dorm=null, admin=null}, Student{id=3, num=1003, name='王五', gender='男', dorm=null, admin=null}, Student{id=5, num=1005, name='jim', gender='男', dorm=null, admin=null}]}, Dorm{id=2, num=102, list=[Student{id=2, num=1002, name='李四', gender='女', dorm=null, admin=null}]}, Dorm{id=3, num=103, list=[Student{id=4, num=1004, name='赵六', gender='男', dorm=null, admin=null}, Student{id=6, num=1006, name='tom', gender='男', dorm=null, admin=null}]}]

所有宿舍封装到一个结果中,每一个宿舍中又封装了一个学生的集合,集合中包括多个学生。

101张三
王五
jim
102李四
103赵六
tom

 注解方式

代码:

import com.ffyc.mybatis.model.Student;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Update;

import java.util.List;

public interface StudentDao_back {
    Student findStudentByid(int id);
    Student findStudentByid1(int id);
    List<Student> findAllStudent();
    @Delete("delete from student where id = #{id}")
    void deleteStudentByid(int id);
    @Insert("insert into student(id,num,name,gender) value(#{id},#{num},#{name},#{gender})")
    void addStudent(Student student);

    @Update("update student set gender=#{gender} where id = #{id}")
    void updateStudent(int id,String gender);
    
}
常用注解标签
@Insert : 插入 sql , 和 xml insert sql 语法完全一样
@Select : 查询 sql, 和 xml select sql 语法完全一样
@Update : 更新 sql, 和 xml update sql 语法完全一样
@Delete : 删除 sql, 和 xml delete sql 语法完全一样
@Param : 入参
@Results : 设置结果集合
@Result : 结果
查询所有信息
@Select("select * from t_emp")
@Results(id = "empMap",value = {
@Result(column = "emp_id",property = "empId",id = true),
@Result(column = "emp_name",property = "empName"),
@Result(column = "emp_tel",property = "empTel"),
@Result(column = "emp_education",property = "empEducation"),
@Result(column = "emp_birthday",property = "empBirthday")
})
这么做指适用于比较简单的sql操作,想多表关联查询这种的,就在.xml中配置就好了,如果要使用注解方式,那么是相当复杂的,这种情况简易用后一种方式。

mybatis的动态SQL

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

没有动态Sql之前这里会出现一个非常令人头疼的问题。

if

<select id="findAllStudent" resultType="Student" parameterType="Student">
    select id,num,name,gender 
    from student 
    where num =#{num} and name =#{name}  and gender =#{gender}
</select>

但是如果没有其中的某一个条件呢?就不会查到任何记录,if元素就可以解决这一问题。使用if元素进行判断一下。但是又出现了新的问题。

什么问题呢?请先看下面这段代码。

问题代码:

<select id="findAllStudent" resultType="Student" parameterType="Student">
    select id,num,name,gender from student
    where 
    <if test="num!=0">
        num =#{num}
    </if>
    <if test="name!=null">
        and name =#{name}
    </if>
    <if test="gender!=null">
        and gender =#{gender}
    </if>
</select>

我们要筛选数据时,通常对属性加以限制,但是又不是全部都加,当对num加以限制时,默认num!=0,这时可以正常执行sql语句,但是如果num==0时,sql语句变为

select num,name,gender from student where and name = #{name} and gender = #{gender}

这时可以很清楚的看到这条sql语句是有问题的,系统就会报错,这是就出现这样一种写法,这个问题就迎刃而解了。

代码:

<select id="findAllStudent" resultType="Student" parameterType="Student">
    select id,num,name,gender from student
    where 1=1
    <if test="num!=0">
        num =#{num}
    </if>
    <if test="name!=null">
        and name =#{name}
    </if>
    <if test="gender!=null">
        and gender =#{gender}
    </if>
</select>

就是这一个小小的1=1,解决这个大大的问题。那么mybatis又是怎么解决的呢?

where,set

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。这样就解决了这一问题。

mybatis中的动态sql就可以很好的解决这一问题。

先来看代码:

<select id="findAllStudent" resultType="Student" parameterType="Student">
    select id,num,name,gender from student
    <where>
        <if test="num!=0">
            num =#{num}
        </if>
        <if test="name!=null">
            and name =#{name}
        </if>
        <if test="gender!=null">
            and gender =#{gender}
        </if>
    </where>
</select>-->

set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号

<update id="updateStudent" parameterType="Student">
        update student
        <set>
            <if test="num!=0">
                num = #{num},
            </if>
            <if test="name!=null">
                name = #{name},
            </if>
            <if test="gender!=null">
                gender = #{gender}
            </if>
        </set>
        where id=#{id}
    </update>

mybatis还对此做了优化,也就是trim元素

trim

trim元素可以代替where元素,也就是把where设置成前缀,当 WHERE 后紧随 AND 或则 OR 的 时候,就去除 AND 或者 OR。prefix 前缀,prefixOverrides 覆盖首部指定内容。

<select id="findAllStudent" resultType="Student" parameterType="Student">
    select id,num,name,gender from student
    <trim prefix="where" prefixOverrides="and | or">
        <if test="num!=0">
            num =#{num}
        </if>
        <if test="name!=null">
            and name =#{name}
        </if>
        <if test="gender!=null">
            and gender =#{gender}
        </if>
    </trim>
</select>

当然前缀也可以设置成set,比如

    <update id="updateStudent1" parameterType="Student">
        update student
        <trim prefix="set" suffixOverrides=",">
            <if test="num!=0">
                num = #{num},
            </if>
            <if test="name!=null">
                name = #{name},
            </if>
            <if test="gender!=null">
                gender = #{gender}
            </if>
        </trim>
        where id=#{id}
    </update>

choose,when

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

    <select id="findAllStudent" resultType="Student" parameterType="Student">
        select id,num,name,gender from student
        <trim prefix="where" prefixOverrides="and | or">
            <choose>
                <when test="num!=0">
                    num = #{num}
                </when>
                <when test="name!=null">
                    and name = #{name}
                </when>
                <otherwise>
                    and gender = #{gender}
                </otherwise>
            </choose>
        </trim>
    </select>

foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:删除id在某个区间的多个学生信息。

    <delete id="deleteStudent">
        delete from student
        <where>
            <foreach collection="array" item="item" open="id in (" separator="," close=")">
                #{item}
            </foreach>
        </where>
    </delete>

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符。

你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

补充:

比如说:要删除id小于某一个值的学生信息时,正常书写 mybatis 会报错,需要对这些符号进行转义。这里的"<"与就是标签的左半部分,又能表示小于的意思,就会出错,当然有问题,肯定就有解决办法。

方法一:

特殊符号处理
    特殊字符    转义字符
        <      &lt;
        >      &gt;
        "    &quot;
        ,    &apos;
        &    &amp;

<select id="findStudents" resultType="com.ffyc.mybatis.model.Student" parameterType="int">
    select id,num,name,gender from student where id  &lt; #{id}
</select>

遇到上述特殊字符时,使用对应的转义字符即可。

方法二:

使用<![CDATA[  ]]>来包裹特殊字符
<select id="findStudentts" resultType="com.ffyc.mybatis.model.Student" parameterType="int" flushCache="true">
    select id,num,name,gender from student where id <![CDATA[<]]> #{id}
</select>
但是有个问题那就是 <if> </if> <where> </where> <choose> </choose> <trim> </trim> 等这些标签都不会被解析,所以只把有特殊字符的语句放在 <![CDATA[ ]]> 尽量缩小<![CDATA[ ]]>的范围

 mybatis的一级缓存和二级缓存

引入

首先,什么是缓存?

缓存用来存储一些常用或即将用到的数据或指令,当CPU需要这些数据或指令的时候直接从缓存中读取,这样比CPU到内存甚至硬盘中读取要快得多,能够大幅度提升CPU的处理速度(节省时间/提高效率)。

缓存有什么用处?

为让程序更快的访问到数据,同时也是为了减少数据库的访问压力,可以将数据缓存到内存,手机内存,客户端硬盘中。

举个例子:同时有100个用户访问同一个数据库,容易造成数据库崩溃,但如果将第一个用户访问到的数据加入到缓存中,后面的用户直接访问第一个用户访问拿到的数据,之后定时再从数据库中查询以更新缓存,这样就能缓解数据库访问量过大的问题,减少数据库压力,提高查询效率。

缓存是怎么实现的?

从数据库中查询出来的对象在使用完后不要销毁,而是存储在内存(缓存)中,
当再次需要获取该对象时,直接从内存(缓存)中直接获取,不再向数据库执行
select 语句,从而减少了对数据库的查询次数,因此提高了数据库的性能。

mybatis一级缓存

Mybatis 有一级缓存和二级缓存,一级缓存的作用域是同一个 SqlSession,如果在同一个SqlSession中执行两次一样的sql,那么第一次执行完毕后会将在数据库中查到的数据写入缓存,第二次查询时就会直接从缓存中查询,提高查询效率,当一个 sqlSession 结束后该 sqlSession 中的一级缓存也就不存在了。Mybatis 默认开启一级缓存。

,一级缓存只是相对于同一个 SqlSession 而言,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession 都会取出当前缓存的数据,而不会再次发送 SQL 到数据库。

 生命周期

1.SqlSession 调用了 close()方法,销毁SqlSession一级缓存将不可用。
2.如果 SqlSession 调用了 clearCache(),清除缓存一级缓存将不可用。
3.SqlSession 中执行了任何一个 update 操作(update()、delete(),insert()) ,都会清空缓存的数据,或者是设置了自动刷新  flushCache="true" 一级缓存同样不可用。
一级缓存测试
    @Test
    public void test3(){
        //测试一级缓存
        //结果只查询1次  没有设置缓存更新,也没有中间强出缓存
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
        studentDao.findStudents(13);
        studentDao.findStudents(13);
        sqlSession.commit();
        sqlSession.close();
    }

//结果只查询1次  没有设置缓存更新,也没有中间强出缓存

    @Test
    public void test2(){
        //测试一级缓存  中间清除一次缓存
        //结果只查询2次
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
        studentDao.findStudents(13);
        sqlSession.clearCache();
        studentDao.findStudents(13);
        sqlSession.commit();
        sqlSession.close();
    }

//测试一级缓存  中间清除一次缓存
//结果只查询2次

    @Test
    public void test(){
        //测试一级缓存
        //在select中设置缓存更新
        //结果只查询2次
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
        studentDao.findStudentts(13);
        studentDao.findStudents(13);
        sqlSession.commit();
        sqlSession.close();
    }

//测试一级缓存
//在select中设置缓存更新
//结果只查询2次

mybatis二级缓存

二级缓存是多个SqlSession共享的,是SqlSessionFactory级别的,如果将查询到的数据放到二级缓存中,那么就可以实现多个sqlsession共享。

如果执行两次相同的sql,第一次查询到的数据后关闭sqlsession,就会将数据放到二级缓存中。

也可以理解为根据 mapper 的 namespace 划分区域 的,相同 namespace 的 mapper 查询的数据缓存在同一个区域,如果使用 mapper 代理方法每个 mapper 的 namespace 都不同,此时可以理解为二级缓存区域是根据 mapper 划分。

每次查询会先从缓存区域查找,如果找不到则从数据库查询,并将查询到数据写入缓存。

 Mybatis 内部存储缓存使用一个 HashMap,key 为 hashCode+sqlId+Sql 语句。value 为从查询出来映射生成的 java 对象。

sqlSession 执行 insert、update、delete 等操作 commit 提交后会清空缓存区域,防止脏读(即读取到没有更新的错误数据)。

 二级缓存配置:

1.启用二级缓存

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

2.对象序列化

将所有的 POJO 类实现序列化接口 Java.io. Serializable。
3.配置映射文件

基本上就是这样。这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

提示 缓存只作用于 cache 标签所在的映射文件中的语句。

<cache flushInterval="1"></cache>
这里设置自动刷新时间为1毫秒。
    public void testTwo(){
        SqlSession sqlSession1 = MybatisUtil.getSqlSession();
        StudentDao studentDao1 = sqlSession1.getMapper(StudentDao.class);
        studentDao1.findStudents(10);
        sqlSession1.close();
        //模拟刷新时间
        int j=0;
        for(int i=0;i<=1000000;i++){
            j=j+1;
        }

        System.out.println(j);
        SqlSession sqlSession2 = MybatisUtil.getSqlSession();
        StudentDao studentDao2 = sqlSession2.getMapper(StudentDao.class);
        studentDao2.findStudents(10);
        sqlSession2.close();
    }

 这里可以看到结果查询了两次。因为第二次查询过了自动刷新时间。

我们再来看一下两次连续查询的,也就是将模拟间隔时间去掉

 这里明显只查询了一次。

总体架构:

最后附上官方解释

mybatis – MyBatis 3 | XML 映射器

【本文可能讲述的不够细致,具体可参考mybatis官网】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值