MyBatis学习与实践

MyBatis学习与实践

一、MyBatis简介

== 千锋涛哥 039==

1.1 框架概念

框架,就是软件的半成品,完成了软件开发过程中的通用操作,程序员只需要很少或者不用进行加工就能够实现特定的功能,从而简化开发人员在软件开发中的步骤,提高开发效率。

1.2 常用的框架

前端框架 jQuery,React
服务端框架:servlet 框架 springMVC

  • MVC框架,简化了Servlet的开发步骤。
    – Sturts
    – Struts2 不是Struts是升级,而是另一个框架
    – SrpingMVC
  • 持久层框架:完成数据库操作的框架
    – apache DBUtils
    – Hibernate
    – Spring JPA
    – Mybatis
  • 胶水框架:Spring
    SSM框架= Spring + SpringMVC + MyBatis三个字母的缩写。
    ==040 ==

1.3 MyBatis介绍

MyBatis是一个半自动的ORM框架,先出iBatis,后有Hibernate全自动的框架,但效率不高。随着互联网项目(高可用,高性能)的出现,逐渐暴露出一些缺点。大家又想到了MyBatis,还是半自动的还好,可以人为参与修改。
ORM Object Relational Mapping,对象关系映射,将java中的一个对象与数据库表中一行记录一一对应。ORM框架提供了实体类与数据表的映射关系,通过映射文件的配置,实现对象的持久化。

  • MyBatis前身是iBatis,是Apache软件基金会提供的一个开源项目。
  • 2010年迁移到Google code,正式更名为MyBatis.
  • 2013年迁移到Github托管。
  • MyBatis特点。
    – 支持自定义SQL,存储过程。
    – 对原有的JDBC进行了封闭,几乎消除了所有JDBC代码。
    – 支持XML和注解配置,自动完成ROM操作,实现结果映射。
    == 041 ==

二、MyBatis框架部署

框架部署就是将框架引入到我们的项目中。

2.1 创建maven项目

  • Java工程和web工程均可

2.2 添加项目依赖

  • 在项目pom文件中添加依赖
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>

2.3 创建MyBatis配置文件

  • 在resources文件夹下创建MyBatis配置文件
    <!--    在environments配置数据库连接信息-->
    <!--    在environments标签中可以定义多个environment,每个environment标签可以定义一套连接配置-->
    <!--    default属性,用来指定使用哪个environment标签 -->
    <environments default="mysql">
        <environment id="mysql">
            <!-- transactionManager 标签用于配置数据管理方式-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- <dataSource用于配置数据库连接信息-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

== 042==

三、MyBatis框架使用

3.1 创建数据表

CREATE TABLE `student` (
`sid`  int(10) NULL COMMENT 'ID' ,
`stu_num`  varchar(30) NULL COMMENT '编号' ,
`stu_name`  varchar(30) NULL COMMENT '姓名' ,
`stu_gender`  varchar(30) NULL COMMENT '性别' ,
`stu_age`  int(10) NULL COMMENT '年龄' 
);

3.2 创建实体类

导入lombok依赖,以便通过注解实现实体类的,get,set,tostring。

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.22</version>
    <scope>provided</scope>
</dependency>

com.zhang.bean.Student.java

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {

    private  int stuId;
    private String stuNum;
    private String stuName;
    private String stuGender;
    private int stuAge;
}

3.3 创建DAO,定义操作方法

  • com.zhang.dao.StudentDAO.java
public interface StudentDAO {
    public int insertStudent(Student student); // 添加
    public int deleteStudent(String stuNum);    // 删除
}

3.4 创建DAO接口的映射文件

  • 在resources目录下,新建名为mappers文件夹
  • 创建mappers中新建名为StudentMapper.xml的映射文件。(根据模板创建文件)
  • 在映射文件中对DAO中定义的方法进行实现。
<mapper namespace="com.zhang.dao.StudentDAO">
<!-- 相关于DAO接口的实现类,namespace属性要的指定实现DAO接口的全限定名-->
    <insert id="insertStudent" parameterType="com.zhang.bean.Student">
        insert into student(sid,stu_num,stu_name,stu_gender,stu_age)
        values(#{stuId},#{stuNum},#{stuName},#{stuGender},#{stuAge});
    </insert>

    <insert id="deleteStudent" parameterType="com.zhang.bean.Student">
        delete from student where stu_num = #{stuName};
    </insert>

</mapper>
  • mapper文件相当于接口文件的实现类。

3.5 将映射文件添加到主配置文件

  • resources/mybatis-config.xml中处理。
    <mappers>
        <mapper resource="mappers/StudentMapper"></mapper>
    </mappers>

==043 ==

四、单元测试

4.1 添加依赖

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

4.2 创建单元测试类

在被测试类右键选择generate --选择test

public class StudentDAOTest {

    @org.junit.Test
    public void insertStudent() {

        try {
            // 加载mybatis配置文件
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            // 连接工厂
            SqlSessionFactory factory = builder.build(is);
            // 会话(连接)
            SqlSession sqlSession = factory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            System.out.println(studentDAO);

            // 测试StudentDAO中的方法
            int i = studentDAO.insertStudent(new Student(0, "001", "张恒超", "nan", 23));
            // 此处需要手动提交
            sqlSession.commit();
            System.out.println(i);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @org.junit.Test
    public void deleteStudent() {
    }
}

问题坑1
测试时报错,java.sql.SQLException: Incorrect string value: '\xE5\xB0‘
原因是数据库表和字段的字符集为latin1,改为UTF-8即可解决问题。参考链接

问题坑2
测试时报:You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true
原因是数据库连接时,得配置说明SSL属性。具体在mybatis-config.xml中添加 useSSL=false 即可解决问题,具体参见如下。

     <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>

044

五、MyBatis的CRUD操作

5.1 添加操作

5.2 删除操作-根据学号删除

  • 在StudentDAO中定义删除方法。
public interface StudentDAO {
    public int insertStudent(Student student); // 添加
    public int deleteStudent(String stuNum);    // 删除
}
  • 在StudentMapper.xml中对接口方法进行实现。
<detete id="deleteStudent" parameterType="com.zhang.bean.Student">
        delete from student where stu_num = #{stuName};
    </detete>
  • 在测试类中创建测试方法
@org.junit.Test
    public void eleteStudent() {
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            //SqlSessionFactoryBuilder
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            // SqlSessionFactory表示MyBatis的会话工厂
            SqlSessionFactory factory = builder.build(is);
            // SqlSession表示MyBatis与数据库之间的会话,通过工厂方法设计模式。
            SqlSession sqlSession = factory.openSession();
            // 通过SqlSession对象调用 getMapper方法获取DAO接口对象
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            // 调用被测试方法
            int i = studentDAO.deleteStudent("002");
            System.out.println(i);
            // 此处需要手动提交
            sqlSession.commit();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

==45 ==

5.3 修改操作

根据学号修改其他字段信息,一般不修改学号,ID信息
– DAO中添加方法

public interface StudentDAO {
    public int insertStudent(Student student); // 添加
    public int deleteStudent(String stuNum);    // 删除
    public int updateStudent(Student student);    // 修改
}
  • mapper中添加实现
    <update id="updateStudent">
        update student
        set stu_name = #{stuName},
            stu_gender = #{stuGender},
            stu_age = #{stuAge}
        where stu_num = #{stuNum}
    </update>
  • 测试类中添加测试方法
    @org.junit.Test
    public void updateStudent(){
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory factory = builder.build(is);
            SqlSession sqlSession = factory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            int i = studentDAO.updateStudent(new Student(1, "001", "李文浩", "女", 32));
            sqlSession.commit();
            assertEquals(1,i);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

== 46 ==

5.4 查询所有

  • DAO中定义方法
public List<Student> listStudents();             // 查询所有
  • Mapper中实现方法
 <!--    resultType指定查询结果封装的对象的实体类,不能省略-->
    <!--    resultSets指定当前操作返回的集合类型,可省略-->
    <!--    方法一,指定数据库表字段与对象属性的对应关系
    <select id="listStudents" resultType="com.zhang.bean.Student" resultSets="java.util.List">
        select sid stuId,stu_num stuNum,stu_name stuName
            ,stu_gender stuGender,stu_age stuAge
        from student;
    </select>
    -->

    <!--  方法二   resultMap 标签用于定义对象与数据表的映射关系-->
    <resultMap id="studentMap" type="com.zhang.bean.Student">
        <id column="sid" property="stuId"></id>
        <result column="stu_num" property="stuNum"></result>
        <result column="stu_name" property="stuName"></result>
        <result column="stu_gender" property="stuGender"></result>
        <result column="stu_age" property="stuAge"></result>
    </resultMap>

    <!-- resultMap用于引用一个实体的映射关系,有了上面resultMap定义及引用,resultType就可以省略了-->
    <select id="listStudents" resultType="com.zhang.bean.Student" resultMap="studentMap">
        select sid,stu_num,stu_name,stu_gender,stu_age
        from student;
    </select>
  • 测试类中实现测试方法
@org.junit.Test
    public void listStudents(){
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory factory = builder.build(is);
            SqlSession sqlSession = factory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            List<Student> students = studentDAO.listStudents();
            sqlSession.commit();
            for (Student stu:students){
                System.out.println(stu);
            }
            // assertNotNull(students);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

== 047==

5.5 根据主键查询一条记录

  • Dao中定义方法
    public Student queryStudent(String stuNum);      // 查询一条记录

  • Mapper定义实现
     <select id="queryStudent" resultMap="studentMap">
        select sid,stu_num,stu_name,stu_gender,stu_age
        from student
        where stu_num = #{stuNum}
    </select>
  • 单元测试
@org.junit.Test
    public void queryStudent(){
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory factory = builder.build(is);
            SqlSession sqlSession = factory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            Student stu = studentDAO.queryStudent("001");
            sqlSession.commit();
            System.out.println(stu);
            // assertNotNull(students);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

== 048==

5.7 查询总计录数

  • DAO方法
    public int getCount();  // 查询记录数

  • Mapper实现
    <select id="getCount" resultType="int">
        select count(1)
        from student;
    </select>
  • 测试方法测试
    @org.junit.Test
    public void getCount(){
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory factory = builder.build(is);
            SqlSession sqlSession = factory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            int count = studentDAO.getCount();
            sqlSession.commit();
            System.out.println(count);
            // assertNotNull(students);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

== 49==

5.6 查询操作-多参数查询

分页查询(需要多个参数的情况)

  • DAO中定义操作方法,如果方法有多个参数,使用@Param注解声明参数的别名。
    // 分页录数,多参数可以通过@Param-参数注解给参数别名的方式
    public List<Student> listStudentsByPages(@Param("start") int start,@Param("pageSize") int pageSize);  // 分页录数
  • Mapper中定义实现
    <!-- 多参数分页查询 -->
    <select id="listStudents"  resultMap="studentMap">
        select sid,stu_num,stu_name,stu_gender,stu_age
        from student
        limit #{start},#{pageSize}
    </select>

注意:如果DAORR操作方法没有通过@Param指定参数别名,在SQL中也可以通过arg0,arg1 或param1,param2…获取参数。

  • 测试方法并测试
@org.junit.Test
    public void listStudentsByPages(){
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory factory = builder.build(is);
            SqlSession sqlSession = factory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            List<Student> students = studentDAO.listStudentsByPages(0,1);
            sqlSession.commit();
            for (Student stu:students){
                System.out.println(stu);
            }
            // assertNotNull(students);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }在这里插入代码片

050

5.8 添加操作回填生成的主键id

  • mapper.xml insert操作标签中,添加useGeneratedKeys,keyProperty
        <!--    useGeneratedKeys表示添加操作是否需要回填生成的主键-->
        <!--    keyProperty设置回填的主键值赋值到参数对象的哪个属性-->
    <insert id="insertStudent" useGeneratedKeys="true" keyProperty="stuId" parameterType="com.zhang.bean.Student">
        insert into student(sid,stu_num,stu_name,stu_gender,stu_age)
        values(#{stuId},#{stuNum},#{stuName},#{stuGender},#{stuAge});
    </insert>
  • 测试验证
    在这里插入图片描述

051

六、工具类的封装

  • 建包 com.zhang.utils
  • 建工具类 MyBatisUtil.java
public class MyBatisUtil {
    private static SqlSessionFactory factory;
    private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>();
    static{
        try {
            // 加载mybatis配置文件
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            // 连接工厂
            factory = builder.build(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static SqlSession getSqlSession(){
        SqlSession sqlSession = local.get();
        if(sqlSession == null){
            sqlSession = factory.openSession();
            local.set(sqlSession);
        }
        return sqlSession;
    }
    public static <T extends Object>T getMapper(Class<T> c){
        SqlSession sqlSession = getSqlSession();
        return sqlSession.getMapper(c);
    }
}
  • 调用工具类
    需要事务管理的insert方法中
public void insertStudent() {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
        System.out.println(studentDAO);

无需事务管理的select方法中

 public void listStudents(){
        StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
        List<Student> students = studentDAO.listStudents();
        for (Student stu:students){
            System.out.println(stu);
        }

052

SqlSession对象作用

  • getMapper(DAO.class)可以获取Mapper(Dao接口的实例).
  • 事务管理。
    当我们获取sqlSession对象时,就默认开启了事务。
    操作完成之后中,需手动提交。

7.1 手动提交事务

操作完成后提交事务,异常失败时sqlSession commmit();,回滚事务sqlSession rollback().

7.2 自动提交事务

  • 修改MyBatisUtil.xml
public class MyBatisUtil {
    private static SqlSessionFactory factory;
    private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>();
    static{
        try {
            // 加载mybatis配置文件
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            // 连接工厂
            factory = builder.build(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSessionFactory getFactory(){
        return factory;
    }

    private static SqlSession getSqlSession(boolean isAutoCommit){
        SqlSession sqlSession = local.get();
        if(sqlSession == null){
            // SqlSessionFactory调用openSession方法获取sqSession对象时,可以通过参数设置事务是否自动提交
            // 默认为false,事务不自动提交,true则自动提交
            sqlSession = factory.openSession(isAutoCommit);
            local.set(sqlSession);
        }
        return sqlSession;
    }
    // 手动事务提交
    public static SqlSession getSqlSession(){
        return getSqlSession(false);
    }

    // 自动事务提交
    public static <T extends Object>T getMapper(Class<T> c){
        SqlSession sqlSession = getSqlSession(true);
        return sqlSession.getMapper(c);
    }
}

== 053==

八、MyBatis主配置文件

mybatis-config.xml是mybatis框架的主配置文件,主要用于配置mybaits数据源及属性信息。

8.1 properties标签

  • properties标签,用于设置键值对,或引用属性文件,引用之后中,在配置时就可以获取属性文件中的键值对了。
    1)在resources目录下创建 jdbc.properties文件,创建键值对。
mysql_driver=com.mysql.jdbc.Driver
mysql_url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false
mysql_username=root
mysql_password=root

2)在mybatis-config.xml中通过propertits标签引用properties文件中的键值对

<!-- properties标签   1.可以定义键值对,2.可以引用属性文件-->
    <properties resource="jdbc.properties">

3)修改environment

<dataSource type="POOLED">
                <property name="driver" value="${mysql_driver}"/>
                <property name="url" value="${mysql_url}"/>
                <property name="username" value="${mysql_username}"/>
                <property name="password" value="${mysql_password}"/>
            </dataSource>

8.2 settings标签

 <settings>
        <!--  设置启动二级缓存 -->
        <setting name="cacheEnabled" value="true"/>  
        <!--  启动延迟加载-->
        <setting name="lazyLoadEnabled" value="true"/>  
    </settings>

8.3 typeAliases标签

类型别名

    <!-- typeAliases用于给实体类取别名,在映射文件中可以直接使用别名来代替实体类的全限定名-->
    <typeAliases>
        <typeAlias type="com.zhang.bean.Student" alias="Student"></typeAlias>
    </typeAliases>

8.4 plugins标签

    <!-- plugins配置插件,用于配置mybatis插件,例如分页插件-->
    <plugins>
        <plugin interceptor=""></plugin>
    </plugins>

8.5 environments标签

配置数据连接环境

<!--    在environments配置数据库连接信息-->
    <!--    在environments标签中可以定义多个environment,每个environment标签可以定义一套连接配置-->
    <!--    default属性,用来指定使用哪个environment标签 -->
    <environments default="mysql">
        <environment id="mysql">
            <!-- transactionManager 标签用于配置数据管理方式-->
            <!-- transactionManager type=JDBC可以进行事务的提交和回滚操作
            transactionManager type=MANAGED 依赖于容器进行事务管理,本身不进行事务的提交和回滚操作-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- <dataSource用于配置数据库连接信息-->
            <dataSource type="POOLED">
                <property name="driver" value="${mysql_driver}"/>
                <property name="url" value="${mysql_url}"/>
                <property name="username" value="${mysql_username}"/>
                <property name="password" value="${mysql_password}"/>
            </dataSource>
        </environment>
    </environments>

8.6 mapper标签

用于载入映射文件,项目中有多少个映射文件,就得在此标签配置多少行。

    <mappers>
        <mapper resource="mappers/StudentMapper.xml"></mapper>
    </mappers>

==054 ==

九、映射文件mapper

mapper文件都得按mybatis的规范进行配置。

9.1 MyBatis初始化

在这里插入图片描述
== 055==

9.2 mapper根标签

mapper文件相当于DAO接口的实现类,namespace属性指定要实现DAO接口的全限定名。

9.3 insert标签

声明添加操作(sql:insert)
常用属性
id属性,绑定对应 DAO接口的方法。
parameterType属性,用以指定接口中对应方法的参数类型(可省略)。
useGeneratedKeys属性,设置添加操作是否需要回填生成的主键。
keyProperty属性,设置回填的id设置到参数对象中的哪个属性。
timeout属性,设置此操作的超时时间,如果不设置则一直等待。
主键回填的两种方式
方式一

    <insert id="insertStudent" >
        <selectKey keyProperty="stuId" resultType="java.lang.Integer">
            select last_insert_id()
        </selectKey>
        insert into student(sid,stu_num,stu_name,stu_gender,stu_age)
        values(#{stuId},#{stuNum},#{stuName},#{stuGender},#{stuAge});
    </insert>

方式二

        <!--    useGeneratedKeys表示添加操作是否需要回填生成的主键-->
        <!--    keyProperty设置回填的主键值赋值到参数对象的哪个属性-->
    <insert id="insertStudent" useGeneratedKeys="true" keyProperty="stuId" parameterType="com.zhang.bean.Student">
        insert into student(sid,stu_num,stu_name,stu_gender,stu_age)
        values(#{stuId},#{stuNum},#{stuName},#{stuGender},#{stuAge});
    </insert>

9.4 delete标签

声明删除操作

9.5 updage标签

声明修改操作

9.6 select标签

声明查询操作

  • id指定绑定方法的方法名
  • parameterType属性,设置参数类型
  • resultType 指定当前sql返回数据封装的对象类型
  • resultMap 指定从数据表到实体类和属性的对应关系
  • useCache 指定查询操作是否需要缓存
  • timeout 设置超时时间

9.7 resultMap标签

用于声明实体属性与实体类表中的映射关系。

    <!--  方法二   resultMap 标签用于定义对象与数据表的映射关系-->
    <resultMap id="studentMap" type="com.zhang.bean.Student">
        <id column="sid" property="stuId"></id>
        <result column="stu_num" property="stuNum"></result>
        <result column="stu_name" property="stuName"></result>
        <result column="stu_gender" property="stuGender"></result>
        <result column="stu_age" property="stuAge"></result>
    </resultMap>

9.8 cache标签

设置当前DAO进行数据操作时的缓存属性设置

    <cache size="" readOnly="">       
    </cache> 

9.9 sql和include标签

sql标签,定义sql片段,然后通过include引用。

  • 定义sql标签
  <sql id="stuitem">
        sid,stu_num,stu_name,stu_gender,stu_age
    </sql>
  • include引用sql片段
    <select id="listStudents" resultType="com.zhang.bean.Student" resultMap="studentMap">
        select <include refid="stuitem"/>
        from student;
    </select>

056

十、分页插件

分页插件是一个独立于myBatis框架之外的第三方插件。

10.1 添加分页插件的依赖

PageHelper

<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.3.0</version>
</dependency>

10.2 配置插件

在mybatis-config.xml中进行配置

<plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

10.3 分页实例

对学生信息进行分页查询

    @Test
    public void listStudentByPages(){
        StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
        PageHelper.startPage(1,4);
        List<Student> students = studentDAO.listStudents();
        PageInfo<Student> pageInfo = new PageInfo<Student>(students);
        // pageInfo中包含了数据及分页信息
        List<Student> list = pageInfo.getList();
        for(Student stu:list){
            System.out.println(stu);
        }
    }

** 带条件分页**
实际上与不带条件的差异在于mapper.xml中的查询语句。

==057 ==

十一、关联映射

11.1 实体关系

实体–数据实体,实体关系指的就是数据与数据之间的关系。
** 一对一关联**
学生对学生证,人和身份证,用户基本信息和详情之间的关系。

  • 主键关联,通过主键ID关联,例如用户表主键和详情表中主键相同时,表示是匹配的数据。
    一对多关联 等于多对一关联
    实例:班级对学生,学生对班级
    数据表关系:在多的一端添加外键和一的一字段进行关联。
    多对多关联
    用户和角色

建立第三张关系表添加两个外键,分别与两张表主键进行关联。
058

11.2 创建项目,部署MyBatis项目

  • 新建maven项目
  • pom.xml中设置打包方式为war war
  • 创建web.xml
    在这里插入图片描述
  • 添加web支持,两种方式 一是如下图的方式,二是添加jsp,servlet依赖的方式。

在这里插入图片描述

  • maven记得要刷新一下
    在这里插入图片描述

  • 添加服务器配置
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

  • 部署MyBatis框架
    – pom中添加MyBatis 依赖
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>

– resources文件夹中新建jdbc.properties,mybatis-config.xml
在这里插入图片描述
– 创建com.zhang.utils.MyBatilUtil.java封装工具类
在这里插入图片描述
下面的代码要熟练掌握,为什么这么写。

package com.zhang.utils;

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;

public class MyBatisUtil {
    private static SqlSessionFactory factory;
    private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>();
    static{
        try {
            // 加载mybatis配置文件
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            // 连接工厂
            factory = builder.build(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSessionFactory getFactory(){
        return factory;
    }

    private static SqlSession getSqlSession(boolean isAutoCommit){
        SqlSession sqlSession = local.get();
        if(sqlSession == null){
            // SqlSessionFactory调用openSession方法获取sqSession对象时,可以通过参数设置事务是否自动提交
            // 默认为false,事务不自动提交,true则自动提交
            sqlSession = factory.openSession(isAutoCommit);
            local.set(sqlSession);
        }
        return sqlSession;
    }
    // 手动事务提交
    public static SqlSession getSqlSession(){
        return getSqlSession(false);
    }

    // 自动事务提交
    public static <T extends Object>T getMapper(Class<T> c){
        SqlSession sqlSession = getSqlSession(true);
        return sqlSession.getMapper(c);
    }
}

059

11.3 一对一关联

11.3.1创建表

-- 用户账号表
CREATE TABLE `user` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
  `user_name` varchar(20) COLLATE utf8_bin NOT NULL COMMENT '用户名',
  `user_pwd` varchar(20) COLLATE utf8_bin NOT NULL COMMENT '用户密码',
  `user_realname` varchar(20) COLLATE utf8_bin NOT NULL COMMENT '用户姓名',
  `user_img` varchar(100) COLLATE utf8_bin NOT NULL COMMENT '用户图像',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- 用户信息详情表
CREATE TABLE `user_info` (
`detail_id`  int NOT NULL AUTO_INCREMENT COMMENT '用户信息id' ,
`user_addr`  varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '用户地址' ,
`user_tel`  char(11) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '用户电话' ,
`user_desc`  varchar(200) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '用户描述' ,
`user_id`  int NOT NULL UNIQUE  COMMENT '用户id' ,
PRIMARY KEY (`detail_id`)
)
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_bin;

11.3.2 创建实体类

com.zhang.pojo.User.java
com.zhang.pojo.UserInfo.java

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
    private int userId;
    private String userName;
    private String userPwd;
    private String userRealname;
    private String userImg;

}


@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class UserInfo {
   
    private int detailId;
    private String userAddr;
    private String userTel;
    private String userDesc;
    private int userId;
}

11.3.3 创建DAO

public interface UserDao {
    public int insertUser(User user);
    public User queryUser(String userName);
}

060

    @Test
    public void testInsertUser() {

        User user = new User(0,"lisi","123123","王五","01.jpg",null);
        UserInfo userInfo = new UserInfo(0,"陕西西安","18710056688","有个性才能成长",0);
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        try {
            UserDao userDao = sqlSession.getMapper(UserDao.class);
            int i = userDao.insertUser(user);
            System.out.println(i);
            userInfo.setUserId(user.getUserId());
            UserInfoDao userInfoDao = sqlSession.getMapper(UserInfoDao.class);
            int j = userInfoDao.insertUserInfo(userInfo);
            System.out.println(j);
            sqlSession.commit();
        }catch (Exception e){
            e.printStackTrace();
            sqlSession.rollback();
        }
    }

061

11.3.4 关联查询

在查询用户的同时关联查询出与之对应的详情

  • 实体
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
    private int userId;
    private String userName;
    private String userPwd;
    private String userRealname;
    private String userImg;
    private UserInfo userInfo;  // 将userinfo 作为user的属性
}
  • 映射文件
<resultMap id="userMap" type="User">
        <id column="user_id" property="userId"/>
        <result column="user_name" property="userName"/>
        <result column="user_pwd" property="userPwd"/>
        <result column="user_realname" property="userRealname"/>
        <result column="user_img" property="userImg"/>
        <result column="detail_id" property="userInfo.detailId"/>
        <result column="user_addr" property="userInfo.userAddr"/>
        <result column="user_desc" property="userInfo.userDesc"/>
        <result column="user_tel" property="userInfo.userTel"/>

    </resultMap>

    <select id="queryUser" resultMap="userMap">
        select a1.user_id,a1.user_name,a1.user_pwd,a1.user_realname,a1.user_img
             ,a2.detail_id,a2.user_addr,a2.user_desc,a2.user_tel
        from user a1 left join user_info a2 on a1.user_id = a2.user_id
        where a1.user_name = #{userName}
    </select>

11.4 一对多

案例:班级对学生

11.4.1 创建数据表

CREATE TABLE `classes` (
  `cid` int(11) NOT NULL AUTO_INCREMENT COMMENT '班级id',
  `cname` varchar(30) COLLATE utf8_bin NOT NULL COMMENT '班级名',
  `cdesc` varchar(100) COLLATE utf8_bin NOT NULL COMMENT '班级描述', 
  PRIMARY KEY (`cid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

CREATE TABLE `students` (
  `sid` int(11) NOT NULL AUTO_INCREMENT COMMENT '学生id',
  `sname` varchar(30) COLLATE utf8_bin NOT NULL COMMENT '学生姓名',
  `sage` varchar(100) COLLATE utf8_bin NOT NULL COMMENT '学生年龄', 
  `scid` int(11) NOT NULL COMMENT '学生所属班级id', 
  PRIMARY KEY (`sid`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

11.4.2 创建实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Classes {
    private int cId;
    private String cName;
    private String cDesc;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Students {
    private int sId;
    private String sName;
    private int sAge;
    private int sCid;
}

11.4.3 关联查询

关联查询出一个班级下所有的学生

改造 classes实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Classes {
    private int cId;
    private String cName;
    private String cDesc;
    private List<Students> stus; //存储班级下的所有学生信息
}
  • 连接查询mapper
    mybatis-config.xml中增加别名配置
 <typeAlias type="com.zhang.pojo.Classes" alias="Classes"></typeAlias>
        <typeAlias type="com.zhang.pojo.Students" alias="Students"></typeAlias>

增加映射文件配置

<mapper resource="mappers/ClassMapper.xml"></mapper>

ClassMapper.xml中完善查询逻辑

	<resultMap id="classMap" type="Classes">
		<id column="cid" property="cId"/>
		<result column="cname" property="cName"/>
		<result column="cdesc" property="cDesc"/>
		<!-- classes对象的stus属性是一个List集合,需要使用collection标签-->
		<!-- collection标签的ofType属性声明集合中元素的类型-->
		<collection property="stus" ofType="Students">
			<result column="sid" property="sId"/>
			<result column="sname" property="sName"/>
			<result column="sage" property="sAge"/>
		</collection>
	</resultMap>
	<select id="queryClass" resultMap="classMap">
		select a1.cid,a1.cname,a1.cdesc,a2.sid,a2.sname,a2.sage
		from classes a1 left join students a2 on a1.cid = a2.scid
		where a1.cid = #{cId}
	</select>
  • 测试类
public class ClassDaoTest {

    @Test
    public void testQueryClass() {
        ClassDao classDao = MyBatisUtil.getMapper(ClassDao.class);
        Classes classes = classDao.queryClass(1);
        System.out.println(classes);
    }
}在这里插入代码片
  • 子查询
    063

11.5 多对一关联

查询学生信息时,并查询学生所属班级信息

11.5.1 实体类信息修改

学生所在班级属性用实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Students {
    private int sId;
    private String sName;
    private int sAge;
    private Classes classes; // 学生所在班级属性用实体类
}

需要将班级类中的关联学生实体类信息注释掉

// private List<Students> stus; //存储班级下的所有学生信息

11.5.2 关联查询

  • mybatis-config.xml
    确保 <typeAlias <mapper中正确配置,如下
        <typeAlias type="com.zhang.pojo.Classes" alias="Classes"></typeAlias>
        <typeAlias type="com.zhang.pojo.Students" alias="Students"></typeAlias>
..................................
        <mapper resource="mappers/StudentMapper.xml"></mapper>
  • mapper
<resultMap id="StudentMap" type="Students">
		<id column="sid" property="sId"/>
		<result column="sname" property="sName"/>
		<result column="sage" property="sAge"/>
		<result column="cid" property="classes.cId"/>
		<result column="cname" property="classes.cName"/>
		<result column="cdesc" property="classes.cDesc"/>
	</resultMap>
	<select id="queryStudentById" resultMap="StudentMap">
		select a1.sid,a1.sname,a1.sage,a1.scid,a2.cid,a2.cname,a2.cdesc
		from  students a1 left join classes a2 on a1.scid = a2.cid
		where a1.sid = #{sId}
	</select>
  • test 类
    @Test
    public void queryStudentById() {
        StudentDao studentDao = MyBatisUtil.getMapper(StudentDao.class);
        Students students = studentDao.queryStudentById(1001);
        System.out.println(students);
    }

064

11.6 多对多关联

实例:学生与课程之间的关系

11.6.1 创建数据表

CREATE TABLE `courses` (
  `course_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '课程id',
  `course_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '课程名称',
  PRIMARY KEY (`course_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='课程信息表';

CREATE TABLE `grades` (
  `sid` int(11) NOT NULL COMMENT '学生id',
  `course_id` int(11) NOT NULL COMMENT '课程id',
  `score` int(11) NOT NULL COMMENT '成绩'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

11.6.2 创建实体类

省略部分内容,待做实践练习。

十二、动态SQL

筛选心仪的对象,性别,年龄,城市,
电商网站,筛选商品时,筛选条件可多可少。

12.1 什么是动态sql

根据查询条件动态完成SQL的拼接。

12.2 动态sql案例

复制项目,源文件复制后改名
只保留pom.xml和src文件夹。
更改pom文件中的 mybatis-demo3改成需要的名称
用IDEA打开新复制的项目

12.2.1 创建实体表

CREATE TABLE `members` (
  `member_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '会员id',
  `member_nick` varchar(30) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '呢称',
  `member_gender` char(2) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '性别',
  `member_age` int(11) NOT NULL  COMMENT '年龄',
  `member_city` varchar(30) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '城市',
  PRIMARY KEY (`member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员信息表';

12.2.2 创建实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Member {
    private int memberId;
    private String memeberNick;
    private String memeberGender;
    private int memberAge;
    private String memberCity;
}

066

12.2.3 创建DAO接口

在DAO接口中创建多条件查询的接口

public interface MemberDAO {
    public Member queryMemberByNick(String nick);

    // 在多条件查询中,如果查询条件不确定,可以直接使用HashMap作为参数
    // 优点:HashMap 无需单独定义传递查询条件的类
    public List<Member> searchMember(HashMap<String,Object> params);

    // 也可以专用定义用于查询条件的实体类,用于存放查询参数
    // 缺点:需要单独定义一个实体类,用于封装参数
    public List<Member> searchMember(MemberSearchConfition params);
}

-67

12.3 if标签用法

    <resultMap id="memberMap" type="Member">
        <id column="member_id" property="memberId"/>
        <result column="member_nick" property="memberNick"/>
        <result column="member_gender" property="memberGender"/>
        <result column="member_age" property="memberAge"/>
        <result column="member_city" property="memberCity"/>
    </resultMap>

    <select id="searchMember" resultMap="memberMap">
        Select a1.member_id,a1.member_nick,a1.member_gender,a1.member_age,a1.member_city
        from members a1
        <trim prefix="where" prefixOverrides="and | or" suffix="order by member_age">
            <if test="gender != null">
                and member_gender = #{gender}
            </if>
            <if test="minAge != null">
                and member_age &gt;= #{minAge}
            </if>
            <if test="maxAge != null">
                and member_age &lt;= #{maxAge}
            </if>
            <if test="city != null">
                and member_city = #{city}
            </if>
        </trim>
    </select>
  • 动态sql中

12.4 where标签

<where></where> -- 标签会自动将第一个动态条件的and剔除掉
  • trim标签如下使用,会自动将第一个and 或者 or 拿掉
<trim prefix="where" prefixOverrides="and | or" suffix="order by member_age">

12.5 foreach标签

test类

    @Test
    public void searchMemberByCity(){
        ArrayList<String> cities = new ArrayList<>();
        cities.add("武汉");
        cities.add("西安");
        MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
        List<Member> members = memberDAO.searchMemberByCity(cities);
        for (Member m:members){
            System.out.println(m);
        }
    }

mapper实现

    <select id="searchMemberByCity" resultMap="memberMap">
        Select a1.member_id,a1.member_nick,a1.member_gender,a1.member_age,a1.member_city
        from members a1
        where member_city in
            <foreach collection="list" item="cityName" separator="," open="(" close=")">
                #{cityName}
            </foreach>
    </select>

== 069==

十三、模糊查询${}与#{}

通过昵称查询会员信息

    // 根据呢称查询用户信息--模糊查询
    // 模糊查询需要使用${key}取值,与sql进行拼接
    // 在使用${key}时,即使只有一个参数也需要使用@Param注解声明参数key(非String对象参数时可以不用声明)
    public List<Member> searchMemberByNick(@Param("keyword") String keyword);

13.1 通过HashMap

    @Test
    public void searchMemberByNick(){
        MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
        HashMap<String,Object> params = new HashMap<String,Object>();
        params.put("keyword","花");
        List<Member> members = memberDAO.searchMemberByNick(params);
        for (Member m:members){
            System.out.println(m);
        }
    }
    <!-- ${key} 表示获取参数,先获取参数的值拼接到sql语句中,再编译执行SQL语句,可能引起SQL注入问题-->
    <!-- #{key} 表示获取参数,先完成SQL编译(预编译),预编译之后再将获取的参数设置到SQL语句中,可以避免SQL注入问题-->
    <select id="searchMemberByNick" resultMap="memberMap">
        Select a1.member_id,a1.member_nick,a1.member_gender,a1.member_age,a1.member_city
        from members a1
        where member_nick like "%${keyword}%"
    </select>

13.2 通过String字符串

    @Test
    public void searchMemberByNick(){
        MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
        List<Member> members = memberDAO.searchMemberByNick("花");
        for (Member m:members){
            System.out.println(m);
        }
    }

特别注意:需加parameterType="java.lang.String"

    <select id="searchMemberByNick" parameterType="java.lang.String" resultMap="memberMap">
        Select a1.member_id,a1.member_nick,a1.member_gender,a1.member_age,a1.member_city
        from members a1
        where member_nick like "%${keyword}%"
    </select>

== 070==

十四、MyBatis日志配置

MyBatis作为一个封装好的ORM框架,其运行的过程没有办法跟踪,为了让开发者了解MyBatis执行流程及每个执行步骤所完成的工作,MyBatis框架本身支持log4j日志框架,对运行过程进行跟踪记录。我们只需对MyBatis进行相关的日志配置,就可以看到MyBatis运行过程中的日志信息。

14.1 添加日志框架依赖

<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

14.2 添加日志配置文件

  • resources目录下创建名为 log4j.properties文件
  • 日志输出的方式
# Global logging configuration
# 声明日志的输出级别,及输出方式stdout 表示输入到控制台
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 定义日志的打印格式 %t表示线程名称 %5p表示输出日志级别
log4j.appender.stdout.layout.ConversionPattern=[%t] %5p  - %msg \:%n%m

14.3 日志信息的级别

级别说明
DEBUG输出调试信息
INFO输出提示信息
WARN输出警告信息
ERROR一般性错误信息
FATAL致命性错误信息

071

十五、配置数据库连接池-整合Druid

MyBatis作为一个ORM框架,在进行数据库操作时是需要和数据连接的,频繁创建和销毁数据库连接会消耗一定的资源。
当我们配置MyBatis数据源时,只要配置了datasource标签的type属性为POOLED时,就可以使用MyBatis内置的连接池管理连接。
如果我们想要使用第三方的数据库连接,则需进行自定义配置。

15.1 常见连接池

  • DBCP
  • C3P0 使用简单,效率比较低
  • Druid 效率是比较高的,基于配置,企业中应用的比较多,提供了比较便捷的监控系统。
  • Hikari 基于日语翻译过来的,是性能最高的,

15.2 添加druid依赖

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.8</version>
</dependency>

15.3 创建druid连接池工厂

public class DruidDateSourceFactory extends PooledDataSourceFactory {

    public DruidDateSourceFactory() {
        this.dataSource = new DruidDataSource();
    }
}

15.4 将DruidDateSourceFactory 配置给MyBatis数据源

            <environment id="mysql">
			<transactionManager type="JDBC"></transactionManager>
            <!-- <dataSource用于配置数据库连接信息-->
            <!-- POOLED使用MyBatis内置的连接池-->
            <!-- MyBatis需要一个连接池工厂,这个工厂可以产生数据库连接池 PooledDataSourceFactory -->
            <dataSource type="com.zhang.utils.DruidDateSourceFactory">
                <property name="driverClass" value="${mysql_driver}"/>
                <property name="jdbcUrl" value="${mysql_url}"/>
                <property name="username" value="${mysql_username}"/>
                <property name="password" value="${mysql_password}"/>
            </dataSource>
        </environment>

十六、MyBatis缓存

MyBatis是基于JDBC的封装,使数据库操作更加便捷,MyBatis除了对JDBC操作步骤进行封装之外,也对其性能进行了优化。

  • 在MyBatis引入缓存机制,用于提升MyBatis的检索效率。
  • 在MyBatis引入 延迟加载机制,用于减少对数据库不必要的访问。

16.1 缓存的工作原理

缓存实际就是存储数据的内存。
在这里插入图片描述

16.2 MyBatis缓存分一级缓存和二级缓存

16.2.1 一级缓存

一级缓存也叫SqlSession级缓存,为每个SqlSession单独分配的缓存内存,无需手动开户可直接使用,多个SqlSession的缓存是不共享的。
特性
1、如果多次查询使用的是同一个SqlSesson对象,则第一次查询之后数据会存放到缓存,后续的查询则直接访问缓存中的数据。
2、如果第一次查询之后中,如果对对象进行了修改,此修改会影响到缓存,第二次查询,则查询是的缓存中的数据,但此时数据还没有持久化到数据库。这样第一次查询的结果与第二次的不一致,与数据库中的实际值也不一致。
3、当我们再次查询时,如果想要路过缓存时,则可以通过sqlSession.clearCache()清除缓存。
4、如果第一查询之后第二次查询之前,使用当前的sqlsession执行了修改操作,此修改操作会使第一次查询并缓存的数据失效,因此,第二次查询会再次访问数据库,重新查询数据。

     @Test
    public void searchMemberById(){
         SqlSession sqlSession = MyBatisUtil.getSqlSession();
         MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class);
         Member members1 = memberDAO.searchMemberById(1);
         System.out.println(members1);
         members1.setMemberAge(99);
         //sqlSession.clearCache();
         System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
         Member members2 = memberDAO.searchMemberById(1);
         System.out.println(members2);
     }

– 第二查询之前,修改了数据的情况

     @Test
    public void searchMemberById(){
         SqlSession sqlSession = MyBatisUtil.getSqlSession();
         MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class);
         Member members1 = memberDAO.searchMemberById(1);
         System.out.println(members1);
         members1.setMemberAge(99);
         //sqlSession.clearCache();
         memberDAO.updateMemberById(1,99);
         sqlSession.commit();  //commit()后会导致缓存失败,第二次查询时重查数据库
         System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
         Member members2 = memberDAO.searchMemberById(1);
         System.out.println(members2);
     }

074

16.2.2 二次查询与数据库数据不一致问题

在这里插入图片描述
问题:
第一次查询之后,进行了修改操作,数据库数据已经被修改,但是第二次查询的时候依然显示的修改前的数据。

分析:
修改操作和查询操作不是同一个线程,因此使用不同的对(使用不同的sqlsession),因此修改操作不会导致查询操作的缓存失效,第二次查询时依然访问的是缓存而没有查数据库。

解决方案:
1、让修改操作和查询操作使用相同的sqlsession.(不合理)
2、让每次进行查询操作之后,清空缓存。让再次查询的时候直接查数据库。
75
MyBatis缓存存在一定的局限性,可以生效,但我们一般不用。
并发访问,一个线程,多线程时导致资源竞争的问题。每个线程用自己的session。
二级缓存,

16.2.3 二级缓存

二级缓存也称SqlSessionFactory级缓存,通过同一个factory对象获取的sqlsession可以共享二级缓存。在应用服务器中,SqlSessionFactory本身是单例的,因此二级缓存可以实现全局共享。
特性:
1、二级缓存默认没有开启,需要在mybatis-config.xml中的setting标签中开启。
2、二级缓存只能缓存实现了序列化接口的对象
3、

  • 在mybatis-config.xml中的setting标签中开启。
<settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
  • 在需要使用二级缓存的Mapper文件中配置cache标签使用。
<cache/> 
  • 被缓存的实体类,实现序列化接口。
public class Member implements Serializable {
    private int memberId;
    private String memberNick;
    private String memberGender;
    private int memberAge;
    private String memberCity;
}
  • 测试二级缓存
     @Test
    public void searchMemberById(){
         SqlSessionFactory factory = MyBatisUtil.getSqlSessionFactory();
         // 多个sqlSession对象必须来自于同一个sqlSessionFactory
         SqlSession sqlSession1 = factory.openSession(true);
         SqlSession sqlSession2 = factory.openSession(true);
         MemberDAO memberDAO1 = sqlSession1.getMapper(MemberDAO.class);
         Member members1 = memberDAO1.searchMemberById(1);
         System.out.println(members1);
         sqlSession1.commit(); //第一次查询之后执行commit,会将当前查询结果缓存到二级缓存。

         System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");

         MemberDAO memberDAO2 = sqlSession1.getMapper(MemberDAO.class);
         Member members2 = memberDAO2.searchMemberById(1);
         System.out.println(members2);
     }

16.3 查询操作的缓存标签

只有selects标签可以使用。
useCache=“false” 强制当前操作不使用缓存信息 true则强制使用缓存。

    <select id="searchMemberById" resultMap="memberMap" useCache="false">
        Select a1.member_id,a1.member_nick,a1.member_gender,a1.member_age,a1.member_city
        from members a1
        where member_id =  #{mid}
    </select>

76

十七、延迟加载机制(只在子查询中有效果)

延迟加载-- 如果在MyBatis开启了延迟加载,则在执行了子查询(至少查询二次以上),默认只执行第一次查询,当用到子查询的查询结果时,才会触发子查询的执行;如果无需使用子查询结果,则子查询不会执行。

– fetchType=“lazy” 延迟加载标签

<resultMap id="classMap" type="Classes">
        <id column="cid" property="cId"/>
        <result column="cname" property="cName"/>
        <result column="cdesc" property="cDesc"/>
        <collection property="stus" select="com.zhang.dao.StudentDAO.queryStudentsByCid" column="cid" fetchType="lazy"/>
    </resultMap>
    <select id="queryClassById" resultMap="classMap" >
        select cid,cname,cdesc
        from classes
        where cid = #{cid}
    </select>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值