MyBatis从入门到入土!

第一节 MyBatis简介


这只是我之前的学习笔记,并不一定全面,如有异议,别喷。。。

1 框架简介

1.1 框架的概念

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

  • 框架能够很好地提高开发效率。

1.2 常用框架

  • MVC框架:简化了Servlet的开发步骤

  • Stuts2

  • SpringMVC

  • 持久层框架:完成数据库相关操作的框架

  • apache DButils

  • Hibernate

  • Spring JPA

  • MyBatis

  • 胶水框架:Spring

一些框架的简称 :
SSM : Spring+SpringMVC+MyBatis
SSH : Spring+SpringMVC+Hibernate

2 MyBatis介绍

MyBais是一个**半自动 ORM**框架
ORM(Object Relational Mapping) : 对象关系映射,将Java中的一个对象与数据表中一行记录一一对应。
ORM框架提供了实体类与数据库的映射关系,通过映射文件的配置,实现对象的持久化。
2.1 MyBatis的特点
支持自定义SQL,存储过程
对原有的JDBC进行封装,几乎消除了JDBC的所有代码,开发者只需要关注SQL本身
支持XML和注解配置的方式,自定义完成ORM操作,实现结果映射

第二节 MyBatis框架部署


框架部署就是将框架引入到项目当中去

1 创建Maven项目

Maven:
Java项目
Web项目

2 在Maven项目中添加MyBatis依赖

在pom.xml中添加依赖
MyBatis
mysql driver

2.1 maven依赖

Maven Repository 官网:
Maven Repository: Search/Browse/Explore (mvnrepository.com)
  • mysql driver

<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.0.31</version>
</dependency>
  • MyBatis

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.11</version>
</dependency>
  • 在pom.xml 中添加包的说明以后,需要到Maven中刷新一下,将jar包安装进来

3 创建MyBatis配置文件

为了配置数据库连接

3.1 MyBatis配置文件模板

  • 需要先定义MyBatis配置模板,其他的模板也可以在这里进行配置

  • 选择resources----右健New----Edit File Templates

模板内容:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd" >
<configuration>
</configuration>
将上面的xml复制粘贴到以下文本框

3.2 定义配置文件

点击刚刚定义的模板

在mybatis-config.xml中添加数据库连接信息
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd" >
<configuration>
<!--  environments配置数据库连接信息  -->
<!--    environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置-->
<!--    default属性,用来执行使用哪个environment 标签,-->
    <environments default="mysql">
        <environment id="mysql">
<!--      transactionManager用于配置数据库管理方法      -->
            <transactionManager type="JDBC"></transactionManager>
<!--            dataSource标签用来配置数据库连接信息-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql"/>
                <property name="url" value="jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

第三节 MyBatis框架的使用


案例 : 学生信息的数据库insert操作

1 创建数据表

tb_students
CREATE TABLE tb_students(
	stu_id int PRIMARY key auto_increment,
	stu_num CHAR(5) not null UNIQUE,
	stu_name VARCHAR(20) not null,
	stu_gender CHAR(2) not null,
	stu_age int not null
);

2 创建实体类

Lombok

导入Lombok的maven依赖

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

创建实体类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
    private Integer stuId;
    private String stuNum;
    private String stuName;
    private String stuGender;
    private Integer stuAge;
}

3 创建DAO接口

public interface StudentDAO {
    int insertStudent(Student student);
    int deleteStudent(String stuNum);
}

4 创建DAO接口的映射文件

4.1 创建映射文件模板

步骤与创建MyBatis配置文件模板相同

以下是模板内容

<?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">
<mapper namespace="">

</mapper>
在resources 目录下,新建mappers文件夹
在mappers 中新建StudentMapper.xml 的文件,这个可以根据上面的模板进行创建,注意这里的StudentMapper 名称建议跟DAO联系起来
在映射文件中,对 DAO中的方法进行实现

4.2 配置映射文件

以下是配置文件中的一些内容

<?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">
<!--mapper文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)-->
<!-- copy reference-->
<mapper namespace="com.hqj.dao.StudentDAO">
<!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
    <insert id="insertStudent" parameterType="com.hqj.pojo.Student">
        <!--SQL语句,#{}代表对象的属性-->
         insert into tb_student(stu_num,stu_name,stu_gender,stu_age)
         values(#{stuNum},#{stuName},#{stuGender},#{stuAge})
    </insert>
    <delete id="deleteStudent">
        delete  from  tb_student where stu_num = #{stuNum}
    </delete>
</mapper>

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

将StudentMapper.xml 添加到mybatis-config.xml
使用<mappers></mappers> <mapper></mapper>标签
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd" >
<configuration>
<!--  environments配置数据库连接信息  -->
<!--    environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置-->
<!--    default属性,用来执行使用哪个environment 标签,-->
    <environments default="mysql">
        <environment id="mysql">
<!--      transactionManager用于配置数据库管理方法      -->
            <transactionManager type="JDBC"></transactionManager>
<!--            dataSource标签用来配置数据库连接信息-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

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

</configuration>

第四节 单元测试


测试类往往是在测试类名后加 Test,测试方法往往是在测试方法名前加 Test

1 添加单元测试依赖 junit

mavenpom.xml中添加 Junit依赖
 <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
            <scope>test</scope>
        </dependency>

2 创建测试类

在被测试接口类名后面 使用快捷键 alt + insert ——> test

3 进行测试

测试代码如下:
package com.hqj.dao;

import com.liguoqing.pojo.Student;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import javax.annotation.Resource;

import java.io.IOException;
import java.io.InputStream;

import static org.junit.Assert.*;

public class StudentDAOTest {

    @Test
    public void insertStudent() {

        try {
            //加载mybatis配置文件
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            //创建builder
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //会话工厂,连接工厂
            SqlSessionFactory factory = builder.build(is);
            //sqlsession 代表数据库的连接,也代表数据库的连接对象
            //会话(连接)
            SqlSession sqlSession = factory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            //System.out.println(studentDAO);
            int i = studentDAO.insertStudent(new Student(0, "10002", "灿", "女", 18));
            //需要手动提交事务
            sqlSession.commit();
            System.out.println(i);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Test
    public void deleteStudent() {
    }
}
测试结果如下,测试成功!

第五节 MyBatis 的CRUD操作


案例 : 学生信息的增删查改

1 添加操作

1.1 添加

添加操作请参考 第四节 单元测试的案例

1.2 添加并回填生成的主键

在StudentMapper.xml
在insert标签中 useGeneratedKeys 设置添加操作是否需要回填生成的主键 keyProperty 设置回填的位置
<!--    useGeneratedKeys 设置添加操作是否需要回填生成的主键-->
<!--    keyProperty 设置回填的位置-->
    <insert id="insertStudent" parameterType="com.liguoqing.pojo.Student" useGeneratedKeys="true" keyProperty="stuId">
        insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
        values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
    </insert>

2 删除操作


2.1 在StudentDAO中定义删除方法

代码如下:

public interface StudentDAO {
    int insertStudent(Student student);
    int deleteStudent(String stuNum);
}

2.2 在StudentMapper.xml中对接口方法进行实现

StudentMapper.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">
<mapper namespace="com.hqj.dao.StudentDAO">

	<!--添加方法-->
    <insert id="insertStudent" >
        insert into tb_students(stu_num,stu_name,stu_gender,stu_age)
        values (#{stuNum},#{stuName},#{stuGender},#{stuAge})
    </insert>

	<!--删除方法-->
    <delete id="deleteStudent">
        delete from tb_students where stu_num=#{stuNum}
    </delete>

</mapper>

2.3 在StudentDAO的测试类中添加测试方法

测试类和方法如下:
package com.hqj.dao;

import com.liguoqing.pojo.Student;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import javax.annotation.Resource;

import java.io.IOException;
import java.io.InputStream;

import static org.junit.Assert.*;

public class StudentDAOTest {

    @Test
    public void insertStudent() {

        try {
            //加载mybatis配置文件
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            //创建builder
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //会话工厂,连接工厂
            SqlSessionFactory factory = builder.build(is);
            //sqlsession 代表数据库的连接,也代表数据库的连接对象
            //会话(连接)
            SqlSession sqlSession = factory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            // System.out.println(studentDAO);
            int i = studentDAO.insertStudent(new Student(0, "10002", "灿", "女", 18));
            //需要手动提交
            sqlSession.commit();
            System.out.println(i);
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    @Test
    public void testDeleteStudent() {
        try {

            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            //SqlSessionFactory表示mybatis的会话工厂,工厂模式
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
            //SqlSession 对象是mybatis和数据库之间的连接,也就是会话,创建了连接,可以得到所有的mapper对象(映射关系)
            SqlSession sqlSession = sqlSessionFactory.openSession();
            //通过SqlSession 对象调用getMapper方法获取DAO接口对象
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            //调用被测试方法
            int i = studentDAO.deleteStudent("10002");
            //提交事务
            sqlSession.commit();
            System.out.println(i);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
测试结果如下:

至此,删除操作成功实现并通过测试!

3 修改操作

根据学生学号修改其他信息

3.1 在StudentMapper.xml中对接口方法进行实现

代码如下:
public interface StudentDAO {
     int insertStudent(Student student);
     int deleteStudent(String stuNum);
     int updateStudent(Student student);
}

3.2 在StudentMapper.xml中对接口方法进行实现

StudentMapper.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">
<mapper namespace="com.hqj.dao.StudentDAO">

    <insert id="insertStudent" >
        insert into tb_students(stu_num,stu_name,stu_gender,stu_age)
        values (#{stuNum},#{stuName},#{stuGender},#{stuAge})
    </insert>

    <delete id="deleteStudent">
        delete from tb_students where stu_num=#{stuNum}
    </delete>
    <update id="updateStudent">
        update tb_students set
            stu_name = #{stuName},
            stu_gender = #{stuGender},
            stu_age = #{stuAge}
        where
            stu_num = #{stuNum}
    </update>
</mapper>

3.3 在StudentDAO的测试类中添加测试方法

测试类同上,这里只展示test方法
    @Test
    public void testUpdateStudent(){
        try {
            InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //回话工厂
            SqlSessionFactory factory = builder.build(stream);
            SqlSession sqlSession = factory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);

            //System.out.println(studentDAO);
            int i = studentDAO.updateStudent(new Student(0, "10002", "刘灿", "女", 18));
            sqlSession.commit();
            System.out.println(i);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
修改前数据表中的信息


IDEA中测试运行结果


修改后数据表中的信息


可以看到,表中将stu_num10002的信息按照Java程序中的操作做出了改变

至此,测试成功通过!

4 查询操作


4.1 查询所有

步骤与上述类似,这里只给出 核心代码
  1. 定义接口方法

public interface StudentDAO {
    int insertStudent(Student student);

    int deleteStudent(String stuNum);

    int updateStudent(Student student);
    
	//查询所有
    List<Student> listStudent();
}
  1. 实现接口方法

<!--    resultType执行查询结果,封装的对象的实体类-->
<!--    resultSets指定当前操作返回的集合类型(可省略)-->
<!--    resultType返回的类型-->

<!--resultMap标签用于定义实体类和数据表的映射关系(ORM)-->
<resultMap id="studentMap" type="com.liguoqing.pojo.Student">
    <id column="sid" property="stuId"/>
    <result column="stu_num" property="stuNum"/>
    <result column="stu_name" property="stuName"/>
    <result column="stu_gender" property="stuGender"/>
    <result column="stu_age" property="stuAge"/>
</resultMap>

<!--    resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略-->
<select id="listStudents" resultMap="studentMap">
    select sid,
           stu_num,
           stu_name,
           stu_gender,
           stu_age
    from tb_students
</select>
  1. 测试方法

    @Test
    public void tsetlistStudent(){
        try {
            InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //回话工厂
            SqlSessionFactory factory = builder.build(stream);
            SqlSession sqlSession = factory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);

            //System.out.println(studentDAO);
            List<Student> students = studentDAO.listStudent();
            for (Student student : students) {
                System.out.println(student);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
表中的所有数据


Java程序查询到的数据


测试成功通过!


4.2 查询一条记录

案例:根据学号查询一个学生信息
  1. 定义接口方法

public interface StudentDAO {
    int insertStudent(Student student);

    int deleteStudent(String stuNum);

    int updateStudent(Student student);

    List<Student> listStudent();

    Student queryStudent(String stuNum);
}
  1. 实现接口方法

以下是 StudentMapper.xml中关于查询的代码
    <select id="queryStudent" resultMap="hqj">
        select
               stu_id,
               stu_num,
               stu_name,
               stu_gender,
               stu_age
        from tb_students
        where stu_num = #{stuNum}
    </select>
  1. 测试方法

    @Test
    public void testQueryStudent(){
        try {
            InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //回话工厂
            SqlSessionFactory factory = builder.build(stream);
            SqlSession sqlSession = factory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);

//            System.out.println(studentDAO);
            Student student = studentDAO.queryStudent("10001");
            System.out.println(student);

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

查询结果

测试成功!


4.3 多参数查询


4.3.1 分页查询
参数 start , pageSize
在StudentDAO中定义操作方法,如果方法有多个参数,使用**@Param** 注解声明参数的别名
import com.liguoqing.pojo.Student;
import org.apache.ibatis.annotations.Param;
import java.util.HashMap;
import java.util.List;

public interface StudentDAO {
    /*
    * 在MyBatis进行操作:
    * 1:如果操作方法只有一个简单类型或者字符串类型的参数,
    * 在Mapper配置中可以直接通过#{key}获取,这个key的占位符号可以随便写
    *
    * 2:如果操作方法有一个对象类型的参数,
    * 在Mapper配置中可以直接通过#{attrName}获取对象的指定属性值(attrName必须是参数对象的属性)
    *
    * 3:如果操作方法有一个Map类型的参数,
    * 在Mapper配置中可以直接通过#{key}获取key的指定value值
    *
    * 4:如果操作方法有多个参数,该如何处理呢?
    * 通过MyBatis自带的参数 arg0 arg1 .. 来获取相应的参数
    * 也可以通过@Param("key") 给参数添加注解的方式
     */
    public int insertStudent(Student student);
    public int deleteStudent(String stuNum);
    public int updateStudent(Student student);
    public List<Student> listStudents();
    public Student queryStudent(String stuNum);
//    public List<Student> listStudentsByPage(HashMap<String,Integer> map);
//    public List<Student> listStudentsByPage(int start,int pageSize);
    List<Student> listStudentsByPage(@Param("start") int start , @Param("pageSize") int pageSize);
}
StudentMapper.xml配置sql时,使用#{}获取到指定的参数
<select id="listStudentsByPage" resultMap="hqj">
        select
            stu_id ,
            stu_num ,
            stu_name ,
            stu_gender ,
            stu_age
        from tb_students
        limit #{start} , #{pageSize}
    </select>
注意 :如果DAO操作方法,没有通过@Param指定参数别名,在SQL中也可以通过MyBatis自带的arg0 ,arg1... 或者param1,param2....获取参数

4.3.2 查询总记录数
  1. 定义接口方法

public interface StudentDAO {
    int insertStudent(Student student);

    int deleteStudent(String stuNum);

    int updateStudent(Student student);

    List<Student> listStudents();

    Student queryStudent(String stuNum);

    List<Student> listStudentsByPage(@Param("start") int start,@Param("pageSize") int pageSize);

    int getCount();
}
  1. StudentMapper,xml

<select id="getCount" resultType="int">
        select count(1) from  tb_students
    </select>
  1. 测试方法

 @Test
    public void testGetCount(){
        try {
            InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //回话工厂
            SqlSessionFactory factory = builder.build(stream);
            SqlSession sqlSession = factory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);

//            System.out.println(studentDAO);
            System.out.println(studentDAO.getCount());

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

测试结果正确!


第六节 MyBatis测试类封装


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

public class MyBatisUtil {

    private static SqlSessionFactory factory;//单例的
    private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的

    static {
        try {
            //加载myBatis配置文件,创建会话工厂
            factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //得到sqlSession对象
    public static SqlSession getSqlSession() throws IOException {
        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) throws IOException {
        SqlSession sqlSession = getSqlSession();
        T dao = sqlSession.getMapper(C);
        return dao;
    }

    public static SqlSessionFactory getFactory() throws IOException {
        if (factory == null){
            factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        }
        return factory;
    }


}
当不需要用到事务提交时,直接获取Mapper进行操作
当需要用到事务提交时,先获取sqlSession,在获取到Mapper进行操作,并提交事务

第七节 事务管理


对于 增删改操作,需要用到事务管理

对于SQLSession对象有着两个功能:

getMapper(DAO.class) 获取Mapper(DAO接口的实例)
事务管理

1 手动提交事务

sqlSession中两个关于事务的方法:
sqlSession .commit() 提交事务
sqlSession.rollback() 事务回滚

以insert操作为例

	@Test
    public void testInsertStudent() {
        //获取sqlSession时,默认自动开启事务
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        try {
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            studentDAO.insertStudent(new Student(0, "10008", "灿~cbv~", "女", 18));
            //操作完成,手动提交
            sqlSession.commit();
        } catch (Exception e) {
            //操作出现异常,则回滚
            sqlSession.rollback();
        }
    }

2 自动提交事务

对于那些单操作,可用自动提交

sqlSession = factory.openSession(true);//这里的参数(isAutoCommit),如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务

3 优化MyBatisUtils

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;

public class MyBatisUtil {

    private static SqlSessionFactory factory;//单例的
    private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的

    static {
        try {
            //加载myBatis配置文件,创建会话工厂
            factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    //得到sqlSession对象
    private static SqlSession getSqlSession(boolean isAutoCommit) throws IOException {
        SqlSession sqlSession = local.get();
        if (sqlSession == null){
            sqlSession = factory.openSession(isAutoCommit);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务
            local.set(sqlSession);
        }
        return sqlSession;
    }

    //手动事务管理
    public static SqlSession getSqlSession() throws IOException {
        return getSqlSession(false);
    }

    //自动事务管理
    public static <T extends Object>T getMapper(Class<T> C) throws IOException {
        SqlSession sqlSession = getSqlSession(true);
        T dao = sqlSession.getMapper(C);
        return dao;
    }

    public static SqlSessionFactory getFactory() throws IOException {
        if (factory == null){
            factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        }
        return factory;
    }

}

第八节 MyBatis主配置文件


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

1 各个标签的顺序

标签的顺序不能颠倒
properties
settings
typeAliases
typeHandlers
objectFactory
plugins
enveronments
databaseIdProvider
mappers

2 properties标签


可以定义键值对 (一般不推荐使用)
可以引用属性文件

2.1 定义键值对

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd" >
<configuration>
<!---->
    <properties>
        <property name="mysql_url" value="jdbc:mysql://localhost:3306/mybatis-practice?characterEncoding = utf-8"/>
    </properties>

    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="${mysql_url}"/>
                <property name="username" value="root"/>
                <property name="password" value="hqj20030107"/>
            </dataSource>
        </environment>
        

    </environments>
    
</configuration>

2.2 引用属性文件

  • 在resource 文件夹下创建 jdbc.properties 文件,配置键值对如下:

mysql_driver=com.mysql.jdbc.Driver
mysql_url=jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8
mysql_username=root
mysql_password=123456
  • mybatis-config.xml中通过properties标签引用 jdbc.properties 文件,引入后 在配置environment时,可以直接使用jdbc.properties 的key获取value

3 setting标签

<!--    设置mybatis的属性-->
    <settings>
<!--        启动二级缓存-->
        <setting name="cacheEnable" value="true"/>
<!--        启用延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>

4 typeAliases标签

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

5 plugins标签

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

6 environment标签

<!--  environments配置数据库连接信息  -->
<!--    environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置-->
<!--    default属性,用来执行使用哪个environment 标签,-->
    <environments default="mysql">
        <environment id="mysql">
<!--      transactionManager用于配置数据库管理方法     type="JDBC" 可以进行事务的提交和回滚操作,type="MANAGED" 事务的提交和回滚由容器进行控制 -->
            <transactionManager type="JDBC"></transactionManager>
<!--            dataSource标签用来配置数据库连接信息 POOLED|UNPOOLED 是否使用连接池-->
            <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>

7 mappers标签

<!--    mappers用于载入映射文件,载入DAO类-->
    <mappers>
        <mapper resource="mappers/StudentMapper.xml"></mapper>
    </mappers>

第九节 映射文件


1 MyBatis初始化

2 Mappers根标签

mapper文件相当与DAO接口的实现类, namespace属性要执行实现 DAO的全限定名(Copy Reference)
<mapper namespace="com.hqj.dao.StudentDAO"><mapper>

3 insert标签

功能及常见属性

声明添加操作(sql: insert....)
常用属性:
id属性,绑定对应DAO中的方法
parameterType属性,用以指定接口中对应方法的参数类型(可省略)
useGeneratedKeys属性,用以设置添加操作是否需要回填生成的主键
keyProperty属性,指定回填的id设置到参数对应中的哪个字段中
timeout属性,设置超时时间,如果不设置会一直等待,如果设置了值并到时如果还没有执行,那么会报错

3.2 主键回填

方式一 : 通过设置参数

<insert id="insertStudent" parameterType="com.liguoqing.pojo.Student" useGeneratedKeys="true" keyProperty="stuId">
    insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
    values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
</insert>

方式二 :selectkey标签

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

4 deleted标签

声明删除操作
<delete id="deleteStudent">
        delete
        from tb_students
        where stu_num = #{stuNum}
    </delete>

5 update标签

声明修改操作
 <update id="updateStudent">
        update tb_students
        set stu_name   = #{stuName},
            stu_gender = #{stuGender},
            stu_age    = #{stuAge}
        where stu_num = #{stuNum}
    </update>

6 select标签

声明查询操作

select标签的一些属性

id属性:指定绑定方法的方法名
resultType属性:期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
resultMap属性:对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。
parameterType属性:将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
useCache属性:指定查询操作是否使用缓存
timeout属性:设置超时时间
... 其他的属性可以查看MyBatis文档

7 resultMap标签

resultMap标签用于定义实体类和数据表的映射关系(ORM)
<!--resultMap标签用于定义实体类和数据表的映射关系(ORM)-->
<resultMap id="studentMap"  type="Student">
    <id column="sid" property="stuId"/>
    <result column="stu_num" property="stuNum"/>
    <result column="stu_name" property="stuName"/>
    <result column="stu_gender" property="stuGender"/>
    <result column="stu_age" property="stuAge"/>
</resultMap>

8 cache标签

设置当前dao进行数据库操作时的缓存属性设置
<cache type="" size="" readOnly="false"></cache>

9 sql 和 include

sql片段可以在select中引用
<sql id="sqlpianduan">
		  sid,
           stu_num,
           stu_name,
           stu_gender,
           stu_age
</sql>

<!--    resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略-->
<select id="listStudents" resultMap="studentMap">
    select 
		<include refid="sqlpianduan"/>
    from tb_students
</select>

第十节 分页插件

分页插件是一个独立于MyBatis之外的第三方插件

1 添加分压插件的依赖:PageHelper

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

2 配置插件

在MyBatis的主配置文件中mybatis-config.xml 中通过 plugins标签进行配置,注意标签的位置

<!--    plugins主要用于配置MyBatis插件,例如分页插件-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>

3 分页实例

对学生信息进行分页查询
@Test
public void testGetStudentByPage() throws IOException {
    StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);//sqlSession
    PageHelper.startPage(1,4);//分页,从第1页开始,往后的4条数据
    List<Student> studentList = studentDAO.listStudents();//查全量就可以,分页组件会自动将数据进行分页
    PageInfo<Student> pageInfo = new PageInfo<Student>(studentList);
    //返回的时候只需要将pageInfo 返回就可以,因为pageInfo中就包含了分页以及数据信信息
}

4 带条件的分页查询

import com.liguoqing.pojo.Student;
import org.apache.ibatis.annotations.Param;

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

public interface StudentDAO {

    public List<Student> listStudentsByGender(String gender);

}
————————————————————————————————————————————————————————————————————————————————
<select id="listStudentsByGender" resultMap="studentMap">
    select <include refid="sqlpianduan"/>
        from tb_students where stu_gender = #{stuGender}
</select>
—————————————————————————————————————————————————————————————————————————————————
   @Test
    public void testGetStudentByPageGender() throws IOException {
        StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
        PageHelper.startPage(1,4);
        List<Student> girls = studentDAO.listStudentsByGender("女");
        PageInfo<Student> pageInfo = new PageInfo<Student>(girls);
        //pageInfo中就包含了分页以及数据信息
        for(Student s : pageInfo.getList()){
            System.out.println(s);
        }
    }

第十一节 关联映射

1 实体关系

实体——数据实体,实体关系指的就是数据和数据之间的关系
例如:用户和角色,房屋和楼栋,订单和商品

实体关系分为以下四种:

### 1.1 一对一关联

一对一关联:
人和身份证、学生和学生证

用户基本信息和详情信息

用户基本信息(少量字段)

用户详细信息(大量字段)

用户ID,账号,密码,姓名,登陆时间

用户ID,手机号,住址......

用户登录:根据基本信息来查询,当字段很多时,会严重影响速度

建立数据表关系:

主键关联(用户表主键和详情表主键相同时,表示是匹配的数据)

用户基本信息(少量字段)

用户详细信息(大量字段)

用户ID,账号,密码,姓名,登陆时间

用户ID,手机号,住址......

进行关联时,通过用户ID作为两个表的主键关联,基本表和详细表相同时,则是匹配的,否则就不匹配

唯一外键关联

用户基本信息(少量字段)

用户详细信息(大量字段)

UID(唯一外键)

用户ID,账号,密码,姓名,登陆时间

用户ID,手机号,住址......

null

1 张三

手机号 13745921594

2

2 李四

手机号 13745354523

3

3 王五

手机号 15938466858

1

查询王五的手机号时,UID为3,则为对应结果,表中用粗体标识

1.2 一对多、多对一关联

案例:
一对多:班级和学生、类别和商品
多对一:学生和班级、商品和类别

数据表关系:

在多的一端添加外键和一的一端进行关联

1.3 多对多

案例:
用户和角色、角色和权限、房屋和业主、学生和社团、订单和商品、接口和实现类

数据表关系:

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

用户ID(user_id)

中间表(uid,rid)

角色表(role_id)

2 关系映射实操

  1. 创建Maven项目

这是Maven的知识!

  1. 部署MyBatis框架

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.11</version>
</dependency>

2.1 一对一关联

2.1.1 关联插入
  • 创建数据表

#用户信息表
create table users(
    user_id int primary key auto_increment,
    user_name varchar(20) not null unique,
    user_pwd varchar(20) not null,
    user_realname varchar(20) not null,
    user_img varchar(100) not null
);


#用户详情表
create table details(
    detail_id int primary key auto_increment,
    user_addr varchar(50) not null,
    user_tel char(11) not null,
    user_desc varchar(200),
    uid int not null unique
);
  • 创建实体类

User类
package com.hqj.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**
 * @author 语心
 * @version 1.0
 * @Date 2022/12/21 21:53
 * 
 * create table users(
 *     user_id int primary key auto_increment,
 *     user_name varchar(20) not null unique,
 *     user_pwd varchar(20) not null,
 *     user_realname varchar(20) not null,
 *     user_img varchar(100) not null
 * );
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
    private Integer userId;
    private String userName;
    private String userPwd;
    private String userRealName;
    private String userImg;
}
Detial类
package com.hqj.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**
 * @author 语心
 * @version 1.0
 * @Date 2022/12/21 21:57
 * <p>
 * #用户详情表
 * create table details(
 * detail_id int primary key auto_increment,
 * user_addr varchar(50) not null,
 * user_tel char(11) not null,
 * user_desc varchar(200),
 * uid int not null unique
 * );
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Detail {
    private Integer detailId;
    private String userAddr;
    private String userTel;
    private String userDesc;
    private Integer uid;
}
  • 创建DAO接口,定义操作方法

UserDAo
public interface UserDAO {
    //添加一个用户
    int insertUser(User user);

}
DetailDAO
public interface DetailDAO {
    int insertDetail(Detail detail);
}
  • 在各自的mapper.xml中对接口方法进行实现

UserMapper.xml
 <insert id="insertUser" useGeneratedKeys="true" keyProperty="userId">
        insert into users(user_name, user_pwd, user_realname, user_img)
        values (#{userName}, #{userPwd}, #{userRealName}, #{userImg})
    </insert>
DetailMapper.xml
 <insert id="insertDetail">
        insert into details(user_addr, user_tel, user_desc, uid)
        values (#{userAddr}, #{userTel}, #{userDesc}, #{uid})
    </insert>
  • 进行测试

测试代码
  @Test
    public void testInsertUser() {
        SqlSession SqlSession = MyBatisUtils.getSqlSession();
        UserDAO userDAO = SqlSession.getMapper(UserDAO.class);
        DetailDAO detailDAO = SqlSession.getMapper(DetailDAO.class);

        try {

            User user = new User(0, "王9", "12312312300", "李四", "01.jpg");
            int i = userDAO.insertUser(user);
            Integer userId = user.getUserId();//有主键回填
            Detail detail = new Detail(0, "湖北武汉", "123123589", "手握日月摘星辰", userId);
            int j = detailDAO.insertDetail(detail);

            System.out.println(i);
            System.out.println(j);

            SqlSession.commit();
        } catch (Exception e) {
            e.printStackTrace();
            SqlSession.rollback();
        }
    }
2.1.2 关联查询
在查询用户的同时关联查询出与之对应的详情

User

Detail

UserMapper.xml中做出相应修改

连接查询:

<resultMap id="User" type="com.hqj.pojo.User">
        <id column="user_id" property="userId"></id>
        <result column="user_name" property="userName"></result>
        <result column="user_pwd" property="userPwd"></result>
        <result column="user_realname" property="userRealName"></result>
        <result column="user_img" property="userImg"></result>
        <result column="detail_id" property="detail.detailId"></result>
        <result column="user_addr" property="detail.userAddr"></result>
        <result column="user_tel" property="detail.userTel"></result>
        <result column="user_desc" property="detail.userDesc"></result>
    </resultMap>

----------------------------------------------------------------------------------------------------

<select id="queryUser" resultMap="User">
        SELECT
            user_id,user_name,user_pwd,
            user_realname,user_img,detail_id,
            user_addr,user_tel,user_desc
        FROM users INNER JOIN details
        ON users.user_id = details.uid
        where users.user_name = #{userName};
    </select>

子查询:

<!--DetailMapper:-->
 <resultMap id="detailMap" type="com.hqj.pojo.Detail">
        <id column="detail_id" property="detailId"></id>
        <result column="user_addr" property="userAddr"></result>
        <result column="user_tel" property="userTel"></result>
        <result column="user_desc" property="userDesc"></result>
    </resultMap>

 <select id="queryDetail" resultMap="detailMap">
        select detail_id , user_addr, user_tel , user_desc
        from details where uid=#{uid}
    </select>

<!--UserMapper-->
<resultMap id="User" type="com.hqj.pojo.User">
        <id column="user_id" property="userId"></id>
        <result column="user_name" property="userName"></result>
        <result column="user_pwd" property="userPwd"></result>
        <result column="user_realname" property="userRealName"></result>
        <result column="user_img" property="userImg"></result>

        <association property="detail" select="com.hqj.DAO.DetailDAO.queryDetail" column="user_id"></association>
    </resultMap>

 <select id="queryUser" resultMap="User">
        SELECT
            user_id,user_name,user_pwd,
            user_realname,user_img
        FROM users
        where user_name = #{userName};
    </select>

2.2 一对多关联

案例:班级——学生
2.2.1关联查询
创建数据表
#创建班级信息表
CREATE TABLE classes(
cid int PRIMARY key auto_increment,
cname VARCHAR(30) not null unique,
cdesc VARCHAR(100)
);

#创建学生信息表,scid与上表主键关联
CREATE TABLE students(
sid CHAR(5) PRIMARY key,
sname VARCHAR(20) not null,
sage int not null,
scid int not null 
);
创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Clazz {
    private Integer classId;
    private String className;
    private String classDesc;
    private List<Student> studentList;
}

------------------------------------------------------------------------------------
    
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
    private String stuId;
    private String stuName;
    private Integer stuAge;
    private Integer stuClassId;
}
当查询一个班级的时候,要关联查询出这个班级下的所有学生
  • 连接查询

<mapper namespace="com.hqj.DAO.ClassDAO">

<resultMap id="classMap" type="com.hqj.pojo.Clazz">

    <id column="cid" property="classId"/>
    <result column="cname" property="className"/>
    <result column="cdesc" property="classDesc"/>

	<!--集合需要使用collection标签-->
	<!--ofType声明集合中元素的类型-->
    <collection property="studentList" ofType="com.hqj.pojo.Student">
        <result column="sid" property="stuId"/>
        <result column="sname" property="stuName"/>
        <result column="sage" property="stuAge"/>
        <result column="scid" property="stuClassId"/>
    </collection>

</resultMap>

    <select id="queryClass" resultMap="classMap">
        SELECT
            cid, cname, cdesc, sid, sname,sage,scid
        from classes c INNER JOIN students s
        on c.cid=s.scid where c.cid=#{classId}
    </select>

</mapper>
  • 子查询

<resultMap id="classMap" type="com.hqj.pojo.Clazz">

    <id column="cid" property="classId"/>
    <result column="cname" property="className"/>
    <result column="cdesc" property="classDesc"/>

    <collection property="studentList" select="com.hqj.DAO.StudentDAO.listStudentByClassId" column="cid"/>

</resultMap>

    <select id="queryClass" resultMap="classMap">
        SELECT
            cid, cname, cdesc
        from classes
        where cid=#{classId}
    </select>
        
-------------------------------------------------------------------------------------------------
        
 <resultMap id="StudentMapper" type="com.hqj.pojo.Student">
        <id column="sid" property="stuId"/>
        <result column="sname" property="stuName"/>
        <result column="sage" property="stuAge"/>
        <result column="scid" property="stuClassId"/>
    </resultMap>

    <select id="listStudentByClassId" resultMap="StudentMapper">
        select sid, sname, sage, scid
        from students
        where scid = #{classId}
    </select>

2.3 多对一关联

当查询一个学生的时候,关联查询这个学生所在的班级信息
实体类

连接查询

子查询

和一对一查询类似

2.4 多对多关联

学生——课程
2.4.1创建数据表
-- 学生信息表如上

-- 课程信息表
CREATE TABLE courses(
    course_id int PRIMARY KEY auto_increment,
    course_name varchar(50) not null
);

-- 选课信息表/成绩表
-- 学号,课程号,分数
CREATE TABLE grades(
	sid char(5) not null,
	cid int not null,
	score int not NULL
);

实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Course {
    private int courseId;
    private String courseName;
    private List<Student> students;
}

---------------------------------------------------------------------

public class Student {
    private String stuId;
    private String stuName;
    private Integer stuAge;
}
2.4.2 关联查询
查询课程时,同时查询出选择该课程的学生
 <resultMap id="CourseMap" type="Course">
        <id column="course_id" property="courseId"/>
        <result column="course_name" property="courseName"/>
        <collection property="students" ofType="Student">
            <result column="sid" property="stuId"/>
            <result column="sname" property="stuName"/>
            <result column="sage" property="stuAge"/>
        </collection>
    </resultMap>
    <select id="queryCourseById" resultMap="CourseMap">
        SELECT course_id, course_name,s.sid,s.sname,s.sage
        from courses c INNER JOIN grades g INNER JOIN students s
        on c.course_id=g.cid and g.sid=s.sid
        where c.course_id=#{courseId}
    </select>

第十二节 动态SQL

不同的需求筛选条件不同,需要动态的拼接sql,用户的筛选条件不同,我们完成筛选执行的sql也不一样,我们可以通过穷举来意义完成不同条件的筛选,但是这种实现思路过于繁琐和复杂,MyBatis就提供了动态SQL的配置方式来实现多条件的查询。

1 什么是动态SQL

根据搜索条件,动态完成SQL的拼接

2 动态SQL案例

2.1 创建数据库

CREATE TABLE members(
	member_id int PRIMARY key auto_increment,
	member_nick VARCHAR(20) not null UNIQUE,
	member_gender char(2) not null,
	member_age int not null,
	member_city varchar(30) not null
);

2.2 创建实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Member {
    private Integer memberId;
    private String memberNick;
    private String memberGender;
    private Integer memberAge;
    private String memberCity;
}

2.3 创建DAO接口

public interface MemberDAO {
    //多田间查询时,如果条件不确定,可以使用HashMap作为参数
    List<Member> searchMember(HashMap<String, Object> params);

}

2.4 在MemberMapper中实现

<!--MemberMapper.xml-->
<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 member_id,member_nick,member_gender,member_age,member_city
    from members
    where 1 = 1
    <if test="gender != null"><!--gender 就是参数对象的属性、参数Map的key-->
        and member_gender = #{gender}
    </if>
    <if test="minAge != null"><!--> 大于号  -->
        and member_age >= #{minAge}
    </if>
    <if test="maxAge != null"><!--< 小于号  -->
        and member_age <= #{maxAge}
    </if>
    <if test="city != null">
        and member_city != #{city}
    </if>
</select>

3 各个标签的作用和语法

3.1 if标签

如果满足test属性中的条件,则执行标签体里面的SQL语句
 <if test="gender != null">
        and member_gender = #{gender}
    </if>

3.2 where标签

只有一个及以上 标签,才会将SQL插入
首个有效的标签不会将AND写入,自动去除
 <select id="searchMember" resultMap="MemberMap">

        select member_id, member_nick, member_gender, member_age, member_city
        from members

        <where>
            <if test="gender != null">
                and member_gender = #{gender}
            </if>
            <if test="minAge != null">
                and member_age >= #{minAge}
            </if>
            <if test="maxAge != null">
                and member_age <= #{maxAge}
            </if>
            <if test="city!=null">
                and member_city = #{city}
            </if>
        </where>
    </select>

3.3 trim标签

可添加前缀和后缀
<select id="searchMember" resultMap="MemberMap">

        select member_id, member_nick, member_gender, member_age, member_city
        from members

        <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 >= #{minAge}
            </if>
            <if test="maxAge != null">
                and member_age <= #{maxAge}
            </if>
            <if test="city!=null">
                and member_city = #{city}
            </if>
        </trim>
    </select>

3.4 foreach标签

<select id="searchMemberByCity" resultMap="MemberMap">
        select member_id, member_nick, member_gender, member_age, member_city
        from members where member_city in
       <!--  collection:集合类型 只能写list或者collection
         item:元素别名
         separator:分隔符
         open:开始符
         close:结束符-->
        <foreach collection="list" item="cityName" separator="," open="(" close=")">
            #{cityName}
        </foreach>
    </select>

第十三节 模糊搜索

根据昵称查询会员信息(模糊匹配 like)

1 DAO接口

public interface MemberDAO {

//根据昵称查询用户信息-- 模糊查询
//模糊查询需要使用 ${} 取值,与SQL进行拼接
//在使用${}时,即使只有一个参数也需要使用@Param注释声明参数的key (非String类型不需要)
    public List<Member> searchMemberByNick(@Param("keyWord") String keyWord);
//也可以使用HashMap
    public List<Member> searchMemberByNick(HashMap<String,Object> params);
}


--------------------------------------------------------------------------------------
    

2 映射文件

<!--如果参数是String 类型,需要使用parameterType声明参数类型-->
<select id="searchMemberByNick" parameterType="java.lang.String" resultMap="memberMap">
    select member_id,member_nick,member_gender,member_age,member_city
    from members
    where member_nick like '%${keyWord}%'
</select>

3 测试

  @Test
    public  void testSearchMemberByNick(){
        MemberDAO memberDAO = MyBatisUtils.getMapper(MemberDAO.class);
//        HashMap<String, Object> params = new HashMap<>();
//        params.put("keyWord","小");
        List<Member> members = memberDAO.searchMemberByNick("小");
        for (Member member : members) {
            System.out.println(member);
        }
    }

4 #{} 和${}的区别

${keyWord} 表示获取参数,先获取参数的值,拼接到SQL语句中,再编译执行SQL语句 可能引起SQL注入问题。
#{keyWord} 表示获取参数,先完成SQL的编译(预编译),预编译之后再将获取的参数设置到SQL中 可以避免SQL注入问题。

第十四节 MyBatis日志配置

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

1 添加日志框架依赖

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

2 添加日志配置文件

在 resources目录下创建名为 log4j.properties 文件,名称必须如此
log4j.properties 文件中配置日志输出的方式
#声明日志的输出级别及输出格式
log4j.rootLogger=DEBUG,stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output ...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#定义日志的打印格式    %t表示 线程名称    %5p 日志级别 %msg 日志信息  \:%m%n 换行
log4j.appender.stdout.layout.ConversionPattern=[%t] %5p -%msg \:%m%n

3 日志信息的级别

级别

说明

DEBUG

调试

INFO

提示

WARN

警告

ERROR

一般

FATAL

致命

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

MyBatis作为一个ORM框架,在进行数据库操作时是需要和数据库连接的,MyBatis支持基于数据库连接池的连接创建方式。
当我们配置MyBatis数据源时,只要配置了dataSource标签的type属性值为POOLED时,就可以使用MyBatis内置的连接池管理连接。
如果我们想要使用第三方的数据库连接池,则需要进行自定义配置。

1 常见的连接池

DBCP
C3P0
Druid 性能比较好,提供了比较便捷的监控系统
Hikari 性能最好

功能

dbcp

druid

c3p0

HikariCP

是否支持PSCache

监控

jmx

jmx/log/http

jmx,log

jmx

扩展性

sql拦截及解析

支持

代码

简单

中等

复杂

简单

更新时间

2015.8.6

2015.10.10

2015.12.09

2015.12.3

特点

依赖于common-pool

阿里开源,功能全面

历史久远,代码逻辑复杂,且不易维护

优化力度大,功能简单,起源于boneCP

连接池管理

LinkedBlockingDeque

数组

threadlocal+CopyOnWriteArrayList

2 添加Druid依赖

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

3 创建Druid连接池工厂

public class DruidDataSourceFactory extends PooledDataSourceFactory {

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

4 将DruidDataSourceFactory 配置给MyBatis数据源

<environments default="mysql">
        <environment id="mysql">
            <transactionManager type="jdbc"></transactionManager>
            <!--POOLED 使用MyBatis内置的连接池实现   -->
            <!--mybatis 需要一个连接池工厂  这个工厂可以产生数据库连接池 PooledDataSourceFactory -->
            <!--使用了多态-->
            <dataSource type="com.guoqing.utils.DruidDataSourceFactory">
                <property name="driverClass" value="${driver}"/>
                <property name="jdbcUrl" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

第十六节 MyBatis缓存

MyBatis是基于JDBC的封装,使数据库操作更加便捷MyBatis除了对JDBC操作步骤进行封装之外也对其性能进行了优化:
在MyBatis引入了缓存机制,用于提升MyBatis的检索效率
在MyBatis引入延迟加载机制,用于减少对数据库不必要的访问

1 缓存的工作原理

缓存,就是存储数据的内存

2 MyBatis的缓存

MyBatis的缓存分为一级缓存和二级缓存

2.1 一级缓存

一级缓存也叫作SqlSession级缓存,为每个SqlSession单独分配缓存,无需手动开启默认直接使用,多个SqlSession的缓存是不共享的。
特性:
如果多次查询使用的是 同一个SqlSession对象,则第一次查询之后数据会放到缓存,后续的查询则直接访问缓存中存储的数据。
如果第一次查询完成之后,对查询的对象进行修改(次修改会影响到缓存),第二次查询会直接访问缓存(修改过的),造成 第二次查询的结果与数据库不一致
当进行再次查询时想要跳过缓存直接查询数据库,则可以通过 sqlSession.clearCache()来清楚当前sqlSession的缓存
如果第一次查询之后进行第二次查询,使用当前 sqlSession执行了修改操作,此操作会使第一次查询并缓存的数据失效,因此第二次查询会直接访问数据库。

测试代码:

@Test
    public void testQueryMemberById() {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class);
        Member member = memberDAO.queryMemberById(1);
        System.out.println(member);
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        member.setMemberAge(29);
        sqlSession.clearCache();
        
        MemberDAO memberDAO1 = sqlSession.getMapper(MemberDAO.class);
        Member member1 = memberDAO1.queryMemberById(1);
        System.out.println(member1);
    }

2.2 两次查询与数据库数据不一致问题

2.3 二级缓存

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

<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>
  • 在需要使用二级缓存的Mapper文件中配置cache标签使用二级缓存

<cache/>  

//该标签中有一些属性可以自行进行配置
//例如
淘汰策略属性: eviction = "FIFO"  先进先出
更新频率属性: flushInterval = "6000" 间隔多长时间刷新一次缓存
缓存区的大小属性:size = "223" 多少个对象的引用
只读属性: readOnly = "true" 只能读不能改
  • 被缓存的实体类实现序列化接口

public class Member implements Serializable {
    private Integer memberId;
    private String memberNick;
    private String memberGender;
    private Integer memberAge;
    private String memberCity;
}
  • 测试

@Test
    public void testQueryMemberById() throws IOException {
        SqlSessionFactory factory = MyBatisUtil.getFactory();
        //1:多个sqlsession对象来自于同一个sqlsessionFactory
        SqlSession sqlSession = factory.openSession(true);
        SqlSession sqlSession2 = factory.openSession(true);

        MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class);
        Member member1 = memberDAO.queryMemberById(1);
        System.out.println(member1);
//        sqlSession.clearCache();
        sqlSession.commit(); //2:第一次查询之后,执行sqlSession.commit() 会将当前sqlsession的查询结果缓存到二级缓存

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

        MemberDAO memberDAO2 = sqlSession2.getMapper(MemberDAO.class);
        Member member2 =memberDAO2.queryMemberById(1);
        System.out.println(member2);
    }

2.4 查询操作的缓存开关

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值