MyBatis 数据库

2020-03-23 约 11714 字 阅读时长24 分钟

MyBatis

概述

三层架构

  1. 界面层(springmvc):和用户打交道,接受用户请求参数,显示处理结果(jsp,html,servlet)
  2. 业务逻辑层(spring):接受界面层传递数据,计算逻辑,调用数据库,获取数据
  3. 数据访问层(持久层,MyBatis):访问数据库,对数据进行查询、删除、修改

用户使用界面层—>业务逻辑层—>数据访问层(持久层)—>数据库

框架(Framework)

  1. 框架是一个模板、软件;定义好了一些可重复使用的基础功能
  2. 规定好了一些条款和内容;可以加入自己的东西
  3. 特点
    1. 框架一般不是全能的
    2. 框架是针对某一个领域有效
    3. 框架是一个软件

JDBC缺陷

  1. 代码量很多,大量的代码重复,效率较低,业务代码和数据库操作混在一起

MyBatis简述

  1. MyBatis是apache的一个开源项目,是一个开源的持久层框架,用户数据库操作
  2. MyBatis是一个sql映射框架,数据访问(DAOs);可以将数据库一行数据映射为一个java对象
  3. 功能:
    1. 提供了创建Connection、Statement、ResultSet的能力,不需要开发人员创建这些对象
    2. 提供了执行sql语句的能力
    3. 提供了循环sql,吧sql的结果转化为java对象,List集合的能力
    4. 提供了关闭资源的能力,不需要你关闭Connection、Statement、ResultSet

MyBatis框架快速入门

下载

  1. 在github上面可以直接下载,然后导入项目即可

实现步骤

  1. 新建student表

  2. 导入MyBatis包和mysql包

  3. 新建实体类Student,对应表中一行数据

  4. 创建持久层dao接口,定义操作数据库的方法

  5. 创建一个MyBatis使用的配置文件 叫做sql映射文件,一般一个表对应一个映射文件 这个文件是xml文件

    xml
     1<?xml version="1.0" encoding="UTF-8" ?>
     2<!DOCTYPE mapper
     3        PUBLIC "-//MyBatis.org//DTD Mapper 3.0//EN"
     4        "http://MyBatis.org/dtd/MyBatis-3-mapper.dtd">
     5<mapper namespace="com.lei.dao.StudentDao">
     6    <!--
     7        id:是你要执行sql语句的唯一标识,MyBatis会根据你这个id值来找到你需要执行的sql语句,
     8        你可以自定义,但要求使用接口中的方法名
     9        resultType:表示sql语句执行后,返回什么类型,值写的是类型的全限定名称
    10        -->
    11    <select id="selectStuds" resultType="com.lei.domain.Student">
    12        select * from t_student order by id
    13    </select>
    14</mapper>
    15<!--
    161.指定约束文件为MyBatis-3-mapper.dtd
    17    <!DOCTYPE mapper
    18        PUBLIC "-//MyBatis.org//DTD Mapper 3.0//EN"
    19        "http://MyBatis.org/dtd/MyBatis-3-mapper.dtd">
    202.mapper当前文件根标签
    21    <mapper namespace="test"></mapper>
    22    namespace叫做命名空间,唯一值的,要求使用dao接口的全限定名称
    233.在当前文件使用特定标签,表述数据库的特定操作
    24    <select id="selectBlog" resultType="Blog">
    25        select * from Blog where id = #{id}
    26    </select>
    27
    28    <select>:表示查询,select语句
    29    <update>:表示数据库更新操作
    30    <inster>:表示插入,放的是insert语句
    31    <delete>:表示删除,放的是delete语句
    32-->
  6. 创建MyBatis主配置文件: 一个项目就一个主配置文件 主配置文件提供了数据库连接信息和sql文件映射的位置信息

    xml
     1<?xml version="1.0" encoding="UTF-8" ?>
     2<!DOCTYPE configuration
     3        PUBLIC "-//MyBatis.org//DTD Config 3.0//EN"
     4        "http://MyBatis.org/dtd/MyBatis-3-config.dtd">
     5<configuration>
     6    <!--MyBatis设置,控制其全局行为-->
     7    <settings>
     8		<!--设置MyBatis输出日志-->
     9        <setting name="logImpl" value="STDOUT_LOGGING"/>
    10    </settings>
    11    <!-- 和spring整合后 environments配置将废除-->
    12    <environments default="development">
    13        <environment id="development">
    14            <!-- type="JDBC" 代表使用JDBC的提交和回滚来管理事务 -->
    15            <transactionManager type="JDBC" />
    16            <!-- MyBatis提供了3种数据源类型,分别是:POOLED,UNPOOLED,JNDI -->
    17            <!-- POOLED 表示支持JDBC数据源连接池 -->
    18            <!-- UNPOOLED 表示不支持数据源连接池 -->
    19            <!-- JNDI 表示支持外部数据源连接池 -->
    20            <dataSource type="POOLED">
    21                <property name="driver" value="com.mysql.jdbc.Driver"/>
    22                <property name="url" value="jdbc:mysql://localhost:3306/mydatabase"/>
    23                <property name="username" value="root"/>
    24                <property name="password" value="123456"/>
    25            </dataSource>
    26
    27        </environment>
    28    </environments>
    29    <mappers>
    30        <mapper resource="com\lei\dao\StudentDao.xml"/>
    31    </mappers>
    32</configuration>
    33<!--
    34    MyBatis的主配置文件,定义了数据库配置以及sql映射文件配置
    35    <configuration>
    36          <environment>
    37                环境配置
    38          </environment>
    39          <mappers>
    40                一个mapper标签指定一个文件的位置
    41                从类路径开始的路径信息
    42          </mappers>
    43    </configuration>
    44-->
  7. 创建使用的MyBatis类 通过MyBatis访问数据库

    java
     1public static void main(String[] args) throws IOException {
     2    String resource = "MyBatis-config.xml";
     3    // 通过流将核心配置文件读取进来
     4    InputStream inputStream = null;
     5    inputStream = Resources.getResourceAsStream(resource);
     6    // 通过核心配置文件创建会话工厂
     7    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
     8    SqlSessionFactory factory=builder.build(inputStream);
     9    // 通过会话工厂创建会话
    10    SqlSession session = factory.openSession();
    11    //找到对应的sql语句,并执行
    12    String sqlId="com.lei.dao.StudentDao"+"."+"selectStuds";
    13    List<Student> studs=session.selectList(sqlId);
    14    for(Student s:studs){
    15       System.out.println(s);
    16    }
    17    session.close();
    18}

插入操作

StudentDao.xml

xml
1<insert id="insertStud">
2    insert into t_student values(#{id},#{name},#{age})
3</insert>

调用

java
 1try {
 2    SqlSessionFactory factory=Mybatis.getSqlSessionFactory();
 3    session=factory.openSession();
 4    res=session.insert("com.lei.dao.StudentDao.insertStud",stud);
 5} catch (IOException e) {
 6    e.printStackTrace();
 7}finally {
 8    if (session!=null){
 9        session.commit();//MyBatis默认不自动提交事务
10        session.close();
11    }
12}

使用 SqlSession

在 MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession。 一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session。 使用 MyBatis-Spring 之后,你不再需要直接使用 SqlSessionFactory 了,因为你的 bean 可以被注入一个线程安全的 SqlSession,它能基于 Spring 的事务配置来自动提交、回滚、关闭 session

SqlSessionTemplate

当调用 SQL 方法时(包括由 getMapper() 方法返回的映射器中的方法),SqlSessionTemplate 将会保证使用的 SqlSession 与当前 Spring 的事务相关

MyBatis框架Dao代理

主要类的介绍

  1. Resources:MyBatis中的一个类,负责读取配置文件

    InputStream in=Resources.getResourceAsStream(“MyBatis.xml”)

  2. SqlSessionFactoryBuilder:负责创建SqlSessionFactoryBuilder对象

    SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder()

    SqlSessionFactory factory=builder.build(in)

  3. SqlSessionFactory 重量级对象,创建时间长,消耗资源多;整个程序有一个就够了

    SqlSessionFactory:接口;DefaultSqlSessionFactory:SqlSessionFactory 接口实现类

    SqlSessionFactory 作用:获取SqlSession对象;SqlSession sqlsession=factory.openSession();openSession()无参,获取非自动提交事务的SqlSession对象;openSession(true),获取事务自动提交的SqlSession对象

  4. SqlSession接口:定义了操作数据的方法;例如:selectOne()、selectList()、insert()、commit()、rollback()等

    SqlSession接口实现类:DefaultSqlSession

    使用要求:SqlSession对象不是线程安全的,需要在方法内部使用,在执行sql语句之前,先获取SqlSession对象,执行完后关闭SqlSession它,这样可以保证线程安全

MyBatis工具类的创建

java
 1public class MybatisUtil {
 2    private static SqlSessionFactory factory=null;
 3    static {
 4        String resource = "MyBatis-config.xml";
 5        // 通过流将核心配置文件读取进来
 6        InputStream inputStream = null;
 7        try {
 8            inputStream = Resources.getResourceAsStream(resource);
 9        } catch (IOException e) {
10            e.printStackTrace();
11        }
12        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
13        factory=builder.build(inputStream);
14    }
15
16    /**
17     * 非自动提交事务的sqlsession对象
18     * @return sqlsession对象
19     */
20    public static SqlSession getSqlSession()  {
21        SqlSession sqlSession=null;
22        if (factory!=null){
23            // 通过核心配置文件创建会话工厂
24            sqlSession=factory.openSession();
25        }
26        return sqlSession;
27    }
28    /**
29     * 自动提交事务的sqlseesion对象
30     * @return sqlsession对象
31     */
32    public static SqlSession getSqlSession(boolean flag)  {
33        SqlSession sqlSession=null;
34        if (factory!=null){
35            // 通过核心配置文件创建会话工厂
36            sqlSession=factory.openSession(flag);
37        }
38        return sqlSession;
39    }
40}

MyBatis动态代理

  1. 因为mapper的命名空间与dao接口的全限定名称对应、同时相关sql语句id与dao接口中的方法对应,

    以上两中对应是一种规定,命名时注意;根据返回值类型,调用对应的方法,如果是List,调用sqlSession.selectList()方法;如果返回值是int,则查看mapper文件中的标签,从而执行对应的方法,

  2. MyBatis根据dao方法调用,获取执行sql语句信息;MyBatis根据你得dao接口,创建出一个dao接口实现类,并创建这个类的对象;完成SqlSession调用方法去访问数据库

  3. 不要使用重载方法,因为sql语句id根据方法名命名

java
 1/**
 2* 通过MyBatis的动态代理,sqlSession.getMapper(StudentDao.class)
 3* MyBatis能通过上述代码,取得StudentDao接口的实例对象(像创建实现类,再创建接口)
 4* 执行查询操作,注意mapper中的namespace与sql语句id  对应  Dao接口的全限定名称和方法名
 5*/
 6public static void selectStud(){
 7    SqlSession sqlSession= Mybatis.getSqlSession();
 8    StudentDao dao=sqlSession.getMapper(StudentDao.class);
 9    //查询操作
10    List<Student> studs=dao.selectStuds();
11    for (Student stud:studs){
12        System.out.println(stud);
13    }
14}
15public static void insertStud(){
16    SqlSession sqlSession= Mybatis.getSqlSession();
17    StudentDao dao=sqlSession.getMapper(StudentDao.class);
18    //  插入操作
19    int res=dao.insertStud(new Student("A0020","刘备",32));
20    System.out.println(res);
21}

Mybatis传参

  1. parameterType:写在mapper文件中的一个属性,表示dao接口中方法的参数的数据类型; parameterType 是dao接口中方法参数的数据类型,它的值是java数据类型的全限定名称或者MyBatis定义的别名(MyBatis文档可以看到,都是数据类型的小写); 这个值可以没有,不是强制的,MyBatis通过反射机制能够发现接口参数的数据类型

  2. 简单类型参数:MyBatis把java的基本数据类型和string都看作简单类型 在mapper中获取一个简单类型参数的值,使用**#{任意字符}** MyBatis:创建JDBC连接对象–>PrepareStatement对象(预编译)–>设置sql语句中占位符值,并执行sql–>将查询结果封装返回

  3. 多个参数,使用**@Param**命名参数(推荐使用)

    java
    1//通过注解,定义方法时写上对应参数的名字
    2List<Student> selectStuds(@Param("id") String id,@Param("age") Integer age);

    mapper文件中

    xml
    1<!--写上方法中对应参数名-->
    2<select id="selectStuds" resultType="com.lei.domain.Student">
    3    select * from t_student where id=#{id} or age=#{age}
    4</select>
  4. 多个参数,使用对象方式**#{属性名,javaType=类型名称,jdbcType=数据类型}**

    ​ javaType:指java类型中属性的数据类型

    ​ jdbcType:指在数据库中的数据类型(MyBatis文档可查)

    ​ 例如:#{paramName,javaType=java.lang.String,jdbcType=VARCHAR}

    以上时完整的,平常开发使用简化版,#{属性名},可以通过反射获取javaType,jdbcType的值,不需要指定类型

    java
    1/*创建java类,通过传递参数到mapper*/
    2public class Student {
    3    private String id;
    4    private String name;
    5    private Integer age;
    6    //。。。。
    7}

    mapper文件中

    xml
    1<!--写类中对应属性名,作为参数-->
    2<select id="selectStuds" resultType="com.lei.domain.Student">
    3    select * from t_student where id=#{id} or age=#{age}
    4</select>
  5. 多个参数,按位置顺序传参(了解);MyBatis3.4版本后#{arg0},#{arg1}等,获取参数

  6. map传参,通过key获取value值(了解);#{key1},#{key2}

#和$的区别

#:可以防止sql注入,占位符,使用预编译对象PrepareStatement执行sql,安全 $:字符串的连接与替换,直接拼接到sql语句中,使用对象Statement执行sql,可以被sql注入,效率低

封装MyBatis输出结果

  1. MyBatis执行sql语句,得到java对象

  2. resultType结果类型,指sql语句执行结束后,数据转化为java对象,这个java类型是任意的

    1. MyBatis执行sql语句,然后MyBatis调用类的无参构造,创建对象
    2. MyBatis把resultSet指定列的值赋值给对象同名属性
    3. resultType值有两种:MyBatis规定的别名、全限定名称
  3. 定义resultType 自定义类型的别名

    1. 在MyBatis的主配置文件中,<configuration>标签下定义,使用时直接给resultType值就行
    xml
     1<!--方式1:type是全限定名称,alias是定义的别名-->
     2<configuration>
     3	<typeAliases>
     4    	<typeAlias type="java类型的全限定名称" alias="stu" />
     5    </typeAliases>
     6</configuration>
     7<!--方式2;name是包名,然后该包下所有的类名就是别名-->
     8<configuration>
     9	<typeAliases>
    10    	<package name="com.lei.domain"/>
    11    </typeAliases>
    12</configuration>
    13
    14<!--在mapper中使用-->
    15<select id="selectId" resultType="stu">
    16	select * from t_student where id=#{id}
    17</select>
  4. resultType 返回map

    1. 列值是map的value
    2. 且只能查询一条记录返回,多行记录返回会报错
  5. resultMap:结果映射,指定列名和java对象属性对应关系

    1. 自定义列值赋值给对象的哪个属性
    2. reslutMap与resultType不能一起使用
    3. 当你对象属性名与列名不一样时,一定使用resultMap
  6. 先定义resultMap;然后再mapper标签中引用定义resultMap的id就可

    xml
     1<!--mapper文件-->
     2<!--定义resultMap-->
     3<resultMap id="map1" type="java类型的全限定名称">
     4    <!--主键列-->
     5    <id colum="列名" property="属性名"/>
     6     <!--非主键列-->
     7    <result colum="列名" property="属性名"/>
     8</resultMap>
     9
    10<!--引用resultMap,此时MyBatis根据mapper对应映射关系-->
    11<select id="selectId" restypeMap="map1">
    12	select * from t_student
    13</select>
  7. 列名与属性名不一致的第二种解决办法(第一种是定义resultMap)

    1. resultType默认原则,同名的列值赋给同名的属性
    2. 可以使用别名方式查询,别名应该是java类型的属性名

like模糊查询

  1. 方式一:在java代码中指定like的内容,推荐

    java
    1//java代码中准备好like模糊查询的的内容
    2//name应该为拼接好的like值,例如:%李%
    3List<Student> selectLike(String name);
    4
    5//mapper中内容应该为
    6<select id="selectAll" resultMap="studentMap">
    7    select * from t_student like #{name}
    8</select>
  2. 方式二:在mapper文件中来拼接like内容

    xml
    1<!--此时name传入的值应该为: 李  然后再mapper中拼接-->
    2<select id="selectAll" resultMap="studentMap">
    3    select * from t_student like "%" #{name} "%"
    4</select>

多表联查

domain不能代表返回值,解决方案:map 或者 vo

  1. 可以使用map返回值类型,sql语句中可以取别名
  2. 可以使用vo,作为多表联查返回值类型;需要创建出一个类,类中属性由我们自己定义,属性会包含所需要展示的信息

MyBatis框架动态SQL

简介

  1. 动态sql,指的是sql语句是变化的,可以根据条件获取不同的sql语句
  2. 动态sql语句的实现,使用的是MyBatis提供的标签 <if>,<where>,<foreach>
  3. 动态sql必须使用java对象作为参数

<if>标签

  1. <if>是判断条件的,语法如下

    xml
     1<!--test中语法:属性=xxx值 -->
     2<if test="判断java对象的属性值">
     3	部分sql语句
     4</if>
     5
     6<!--mapper文件中实例
     7	where 后代码:where 1=1  防止sql语法错误
     8-->
     9<select id="selectAll" resultType="com.lei.domain.Student">
    10    select * from t_student where 1=1
    11    <if test="name !=null and name!=' ' ">
    12        name=#{name} 
    13    </if>
    14    <if test=" age>0 ">
    15       and age>#{age}
    16    </if>
    17</select>

<where>标签

  1. <where>用来包含多个<if>的,当多个if有一个成立,<where>标签会自动增加一个where关键字,并去掉最后sql中多余的or,and 防止语法错误

    xml
     1<!--mapper文件中实例
     2	where 后代码:where 1=1  防止sql语法错误
     3-->
     4<select id="selectAll" resultType="com.lei.domain.Student">
     5    select * from t_student 
     6    <where>
     7        <if test="name !=null and name!=' ' ">
     8            name=#{name} 
     9        </if>
    10        <if test=" age>0 ">
    11           and age>#{age}
    12        </if>
    13    </where>
    14</select>

<foreach>标签

  1. 循环标签,用户循环java中数组、List集合的。主要用于sql的in语句中

  2. 参数意义

    1. collection:表示接口中的方法参数类型,如果是数组使用array,如果是list集合使用list
    2. item:自定义的,表示数组和集合成员的变量
    3. open:循环开始时的字符
    4. close:循环结束时的字符
    5. separator:集合成员之间的分隔符
    xml
     1<!--
     2	学生id是:1001,1002,1003的三个学生
     3	select * from student where id in (1001,1002,1003)
     4
     5	public List<Student> selectFor(List<Integer> idList);
     6
     7	List<Integer> idList=new ...;
     8	idList.add(1001);
     9	idList.add(1002);
    10	idList.add(1003);
    11	dao.selectFor(idList);
    12
    13	<foreach>可以将list集合中内容循环遍历,并拼接为sql语句
    14-->
    15
    16<!--foreach使用1,List<Integer> -->
    17<select id="selectAll" resultType="com.lei.domain.Student">
    18    select * from t_student where id in
    19    <foreach collection="list" item="myid" open="("  separator="," close=")" > #{myid} </foreach>
    20</select>
    21
    22<!--foreach使用2,List<Object>,拿到属性名-->
    23<select id="selectAll" resultType="com.lei.domain.Student">
    24    select * from t_student where id in
    25    <foreach collection="list" item="obj" open="("  separator="," close=")" > #{obj.id} </foreach>
    26</select>

动态sql的代码片段

  1. 复用sql语句
  2. 首先使用<sql id=“唯一id”>标签定义一个代码片段
  3. 然后使用<include refid=“自定义片段的id " >标签引用代码片段

配置文件介绍

属性配置文件

  1. 在根目录下定义一个属性配置文件,xxx.properties,例如:jdbc.properties

    properties
    1jdbc.driver=com.mysql.jdbc.Driver
    2jdbc.url=jdbc:mysql://192.168.220.130:3306/mytest?characterEncoding=utf-8
    3jdbc.user=root
    4jdbc.password=123456
  2. 在mybaties主配置文件中,使用<properties>标签指定文件位置;然后在需要使用值的地方:${key}

引入mapper文件

  1. 方式一:mapper

    xml
    1 <mappers>
    2    <mapper resource="com\lei\dao\StudentDao.xml"/>
    3    <mapper resource="com\lei\dao\OderDao.xml"/>
    4</mappers>
  2. 方式二:package(mapper与dao文件同一目录且文件名相同)

    xml
    1 <mappers>
    2     <!--
    3	这个包中所有xml文件可以一次加载给MyBatis
    4	要求:dao接口与mapper文件名一样且在同一目录
    5	-->
    6     <package resource="包名"/>
    7</mappers>

主配置文件

xml
 1<?xml version="1.0" encoding="UTF-8" ?>
 2<!DOCTYPE configuration
 3        PUBLIC "-//MyBatis.org//DTD Config 3.0//EN"
 4        "http://MyBatis.org/dtd/MyBatis-3-config.dtd">
 5<configuration>
 6    <!--    属性配置文件位置-->
 7    <properties resource="jdbc.properties"></properties>
 8
 9    <!--    
10		MyBatis设置,控制其全局行为,与数据库交互环境(二级缓存,加载策略...)
11		对于海量级别数据,如何提高查询效率
12		基础操作:
13			对于常用查询字段,设置索引(加目录)
14		高级操作
15 			使用nosql数据库,redis
16		专业操作
17			针对于电商行业,搜索引擎(Elasticsearch和Solr)
18	-->
19    <settings>
20        <!--        设置MyBatis输出日志-->
21        <setting name="logImpl" value="STDOUT_LOGGING"/>
22    </settings>
23	
24    <!-- 为mapper映射文件的domain起别名 -->
25    <typeAliases>
26    	<!--
27			方式1:为指定的类分别起别名,别名的命名有我们自己决定
28				<typeAlias type="com.lei.domain.Student" alias="Student"/>
29				type:指定为那个domain起别名
30				alias:别名名字
31			方式2:使用package标签,批量起别名,别名是MyBatis默认取好的,命名不由我们自己决定
32				<package name="com,lei,domain" />
33				name:表示在该包下,所有domain自动起好了别名,别名就是类名
34		-->
35    </typeAliases>
36    
37    <!--    插件配置-->
38    <plugins>
39        <plugin interceptor="com.github.pagehelper.PageInterceptor" />
40    </plugins>
41
42    <!-- 和spring整合后 environments配置将废除-->
43    <environments default="development">
44        <environment id="development">
45            <!-- type="JDBC" 代表使用JDBC的提交和回滚来管理事务 -->
46            <transactionManager type="JDBC" />
47            <!--
48				dataSource:表示数据源,在java体系中,规定实现javax.sql.DataSource接口的都是数据源
49				MyBatis提供了3种数据源类型,分别是:POOLED,UNPOOLED,JNDI
50            		POOLED 表示支持JDBC数据源连接池
51          			UNPOOLED 表示不支持数据源连接池;每次执行sql,都会先创建链接,执行sql,关闭连接
52           			JNDI 表示支持外部数据源连接池,java命名和目录(类似windows注册表)
53 			-->
54            <dataSource type="POOLED">
55                <property name="driver" value="${jdbc.driver}" />
56                <property name="url" value="${jdbc.url}"/>
57                <property name="username" value="${jdbc.user}"/>
58                <property name="password" value="${jdbc.password}"/>
59            </dataSource>
60
61        </environment>
62    </environments>
63    <!--指定mapper映射文件-->
64    <mappers>
65        <mapper resource="com\lei\dao\StudentDao.xml"/>
66        <!--
67			方式1:使用resource属性,指定mapper映射文件
68				<mapper resource="com\lei\dao\StudentDao.xml"/>
69			方式2:使用class属性,找到dao层接口的全路径
70				<mapper class="com.lei.dao.StudentDao"/>
71			方式3:使用package 批量注册
72				<package name="" />
73				name:指向dao层包,表示该包下所有mapper映射文件加载
74-->
75    </mappers>
76</configuration>
77        <!--
78            MyBatis的主配置文件,定义了数据库配置以及sql映射文件配置
79            <configuration>
80                  <environment>
81                        环境配置
82                  </environment>
83                  <mappers>
84                        一个mapper标签指定一个文件的位置
85                        从类路径开始的路径信息
86                  </mappers>
87            </configuration>
88        -->

mapper映射文件

xml
 1<?xml version="1.0" encoding="UTF-8" ?>
 2<!DOCTYPE mapper
 3        PUBLIC "-//MyBatis.org//DTD Mapper 3.0//EN"
 4        "http://MyBatis.org/dtd/MyBatis-3-mapper.dtd">
 5<mapper namespace="com.lei.dao.OrderDao">
 6    <!--
 7    paramterType
 8        使用简单类型的参数,在 #{} 中的名字可以随便写
 9        传递多个参数
10            1.使用在方法处使用:@Param命名参数
11                List<Student> selectStuds(@Param("id") String id,@Param("age") Integer age);
12            2.传递对象
13                #{} 中必须为对象的属性
14            3.传递map
15                #{} 中必须为map的key
16        parameterType 一般不写,MyBatis会自动识别
17    -->
18    <!--
19        #{}:占位符,可以防止sql注入
20        ${}:字符串拼接
21    -->
22    <!--
23    模糊查询
24        方式1:使用 ${} ,拼接sql(了解,不常用)
25            select * from t_student where name like '%${value}%'
26        方式2:使用 #{} ,占位符,直接传入拼接好的条件字符串
27            String value="%z%";
28            select * from t_student where name like #{value}
29        方式3:使用 #{} ,占位符,传入条件字符串,在sql中拼接,sql中空格相当于拼接
30            select * from t_student where name like % #{value} %
31    -->
32    <!--
33    resultType与resultMap
34    resultType 可以为domain类型(常用),也可以是map;
35        在domain类型封装不了结果类型时,使用map
36        例如:根据姓名分组,并返回每一个姓名对应的数量,使用map进行封装;查询字段与doamin属性不一致时 都使用map
37    resultMap domain中类型属性名与数据库表字段名一一对应
38        <resultMap id="唯一名称" type="domain类型">
39            <id property="domain中属性名" column="主键字段" />
40            <result property="domain中属性名" column="非主键字段" />
41        </resultMap>
42        <select id="" resultMap="resultMap的唯一id" />
43    -->
44    <!--
45     动态sql:有什么查询条件,在sql语句where后添加什么条件
46        <where>标签里面必有<if>标签,如果有条件,则展现where关键字,如果没有条件则不展现where关键字
47            并且<where>标签会自动屏蔽掉第一个连接符 and/or
48        <if test=" 属性名1!=null and 属性名1!='' " > </if>
49        <foreach>标签用来遍历传过来的数组,常用在sql语句的in关键字后
50
51        <where>
52            <if test="条件"> name like '%'#{name}'%' </if>
53        </where>
54        <foreach collection="array/list" item="" open="循环开始符号" close="循环结束符号" separator="元素之间分隔符" />
55    -->
56    <!--
57    sql片段:常用在重复率高且复杂的子查询,大量的sql片段会降低代码的可读性
58        定义:<sql id="sql片段的唯一标识">重复的sql代码</sql>
59        引用:<include refid="sql片段id" />
60    -->
61
62    <select id="select1" parameterType="java.lang.String" resultType="Order">
63        select * from tb_student where id=#{id}
64    </select>
65</mapper>

插件

MyBatis允许用户在已映射语句执行过程中的某一点进行拦截调用。MyBatis使用插件来拦截的方法调用,故此MyBatis插件通常称为:Mybatis拦截器。默认情况下,MyBatis允许使用插件来拦截的对象包括下面的四个:

tex
1Executor:拦截执行器的方法。
2ParameterHandler:拦截参数的处理。
3ResultHandler:拦截结果集的处理。
4StatementHandler:拦截Sql语法构建的处理。
  • @Intercepts:标识该类是一个拦截器

  • @Signature:指明自定义拦截器需要拦截哪一个类型,哪一个方法

    • type:上述四种类型中的一种;
    • method:对应接口中的哪类方法(因为可能存在重载方法)
    • args:对应哪一个方法的入参

相关注解介绍

  1. 类上使用注解@Intercepts,定义拦截信息,只有符合条件的才会进入拦截,参数为拦截点

    java
     1@Documented
     2@Retention(RetentionPolicy.RUNTIME)
     3@Target(ElementType.TYPE)
     4public @interface Intercepts {
     5    /**
     6     * 定义拦截点
     7     * 只有符合拦截点的条件才会进入到拦截器
     8     */
     9    Signature[] value();
    10}
  2. 使用@Signature注解定义拦截点,指定需要拦截哪个类对象的哪个方法

    java
     1@Documented
     2@Retention(RetentionPolicy.RUNTIME)
     3@Target({})
     4public @interface Signature {
     5  /**
     6   * 定义拦截的类 Executor、ParameterHandler、StatementHandler、ResultSetHandler当中的一个
     7   */
     8  Class<?> type();
     9
    10  /**
    11   * 在定义拦截类的基础之上,在定义拦截的方法
    12   */
    13  String method();
    14
    15  /**
    16   * 在定义拦截方法的基础之上在定义拦截的方法对应的参数,
    17   * JAVA里面方法可能重载,故注意参数的类型和顺序
    18   */
    19  Class<?>[] args();
    20}

@Intercepts注解的使用规则

java
1@Intercepts({//这里可以定义多个@Signature对多个地方拦截,都用这个拦截器
2        @Signature(
3                type = ResultSetHandler.class,
4                method = "handleResultSets", 
5                args = {Statement.class}),
6        @Signature(type = Executor.class,
7                method = "query",
8                args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
9})

拦截器实例

编写拦截器实现sql语句打印,以及执行时间打印

  1. 编写拦截器,实现Interceptor 接口

    java
     1@Intercepts(value = {
     2        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
     3        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
     4        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
     5})
     6@Slf4j
     7public class ExamplePlugin2 implements Interceptor {
     8
     9    //方法拦截
    10    @Override
    11    public Object intercept(Invocation invocation) throws Throwable {
    12        StopWatch stopWatch=new StopWatch();
    13        try {
    14            stopWatch.start("sql执行任务");
    15            return invocation.proceed();//执行sql过程
    16        }catch (Exception e){
    17            e.printStackTrace();
    18            throw e;
    19        }finally {
    20            MappedStatement statement= (MappedStatement) invocation.getArgs()[0];
    21            Object parameter = "";
    22            if (invocation.getArgs().length>1){
    23                parameter = invocation.getArgs()[1];
    24            }
    25            BoundSql boundSql = statement.getBoundSql(parameter);// BoundSql就是封装myBatis最终产生的sql类
    26            Configuration configuration = statement.getConfiguration();// 获取节点的配置
    27            String sql = getSql(configuration, boundSql);
    28            log.info(sql);
    29            stopWatch.stop();
    30            log.info(stopWatch.getLastTaskName()+":"+stopWatch.getLastTaskTimeMillis()+"ms");
    31        }
    32    }
    33
    34    /**
    35     * 获取带参SQL
    36     */
    37    public static String getSql(Configuration configuration, BoundSql boundSql) {
    38        Object parameterObject = boundSql.getParameterObject();//获取参数
    39        List<ParameterMapping> parameterMappings = boundSql
    40                .getParameterMappings();
    41        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");//sql语句中多个空格都用一个空格代替
    42        if (parameterMappings.size() > 0 && parameterObject != null) {
    43            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); //获取类型处理器注册器,类型处理器的功能是进行java类型和数据库类型的转换。如果根据parameterObject.getClass()可以找到对应的类型,则替换
    44            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
    45                sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));
    46            } else {
    47                MetaObject metaObject = configuration.newMetaObject(parameterObject);//MetaObject主要是封装了originalObject对象,提供了get和set的方法用于获取和设置originalObject的属性值,主要支持对JavaBean、Collection、Map三种类型对象的操作
    48                for (ParameterMapping parameterMapping : parameterMappings) {
    49                    String propertyName = parameterMapping.getProperty();
    50                    sql = formatSql(boundSql, sql, metaObject, propertyName);
    51                }
    52            }
    53        }
    54        return sql;
    55    }
    56
    57    /**
    58     * 格式化SQL(替换?)
    59     */
    60    private static String formatSql(BoundSql boundSql, String sql, MetaObject metaObject, String propertyName) {
    61        if (metaObject.hasGetter(propertyName)) {
    62            Object obj = metaObject.getValue(propertyName);
    63            sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
    64        } else if (boundSql.hasAdditionalParameter(propertyName)) {
    65            Object obj = boundSql.getAdditionalParameter(propertyName);//该分支是动态sql
    66            sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
    67        } else {
    68            sql = sql.replaceFirst("\\?", "缺失");//打印出缺失,提醒该参数缺失并防止错位
    69        }
    70        return sql;
    71    }
    72
    73    /**
    74     * 对SQL进行格式化
    75     */
    76    private static String getParameterValue(Object obj) {
    77        String value = null;
    78        if (obj instanceof String) {
    79            value = "'" + obj.toString() + "'";
    80        } else if (obj instanceof Date) {
    81            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
    82            value = "'" + formatter.format(obj) + "'";
    83        } else {
    84            if (obj != null) {
    85                value = obj.toString();
    86            } else {
    87                value = "";
    88            }
    89        }
    90        return value;
    91    }
    92
    93    //获取到拦截的对象,底层也是通过代理实现的,实际上是拿到一个目标代理对象
    94    @Override
    95    public Object plugin(Object target) {
    96        return Plugin.wrap(target, this);
    97    }
    98
    99}
  2. 注册该插件(拦截器)到MyBatis的拦截器链中

    java
    1@Bean
    2public MybatisInterceptor MyBatisInterceptor(SqlSessionFactory sqlSessionFactory) {
    3    MybatisInterceptor plugin2=new MybatisInterceptor();
    4    sqlSessionFactory.getConfiguration().addInterceptor(plugin2);
    5    return plugin2;
    6}

    xml
    1
    2<!--    插件配置,在<environments> 标签上面-->
    3<plugins>
    4    <plugin interceptor="com.lei.config.ExamplePlugin2" />
    5</plugins>

扩展

PageHelper

  1. PageHelper用来做数据分页的

  2. 分页插件的下载地址

  3. 在MyBatis主配置文件中配置插件

    xml
    1    <!--    插件配置,在<environments> 标签上面-->
    2    <plugins>
    3        <plugin interceptor="com.github.pagehelper.PageInterceptor" />
    4    </plugins>
  4. java中使用

    java
     1    public static void selectAll(){
     2        SqlSession sqlSession= Mybatis.getSqlSession();
     3        StudentDao dao=sqlSession.getMapper(StudentDao.class);
     4        //加入pagehelper的方法,分页;参数为 : 第几页开始,每页多少数据
     5        PageHelper.startPage(1,3);
     6        List<Student> stus=dao.selectAll();
     7        for (Student a:stus){
     8            System.out.println(a);
     9        }
    10    }

工具类

  1. MyBatis封装

    java
     1public class SqlSessionUtil {
     2    private static SqlSessionFactory sessionFactory;
     3    //静态代码块,只执行一次,类被加载时
     4    static {
     5        try {
     6            InputStream inputStream= Resources.getResourceAsStream("MyBatis-config.xml");
     7            sessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
     8        } catch (IOException e) {
     9            e.printStackTrace();
    10        }
    11    }
    12
    13    //私有化构造方法
    14    private SqlSessionUtil(){}
    15
    16    //线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的;保证同一个SqlSession对象
    17    private static ThreadLocal<SqlSession> t=new ThreadLocal<>();
    18
    19    //取得SqlSession对象
    20    public static SqlSession getSqlSession(){
    21        SqlSession sqlSession=t.get();
    22        if (sqlSession==null){
    23            sqlSession=sessionFactory.openSession();
    24            t.set(sqlSession);
    25        }
    26        return sqlSession;
    27    }
    28
    29    //关闭SqlSession对象
    30    public static void closeSqlSession(SqlSession sqlSession){
    31        if (sqlSession!=null){
    32            sqlSession.close();
    33        //用户发送请求,服务器分配一条线程,执行结束后,线程回到线程池中,并没有被销毁;所以需要移除,保证线程的干净
    34            t.remove();
    35        }
    36    }
    37}
  2. 动态代理封装

    java
     1public class TransactionInvocationHandler implements InvocationHandler {
     2    private Object target;
     3
     4    public TransactionInvocationHandler(Object target) {
     5        this.target = target;
     6    }
     7
     8    @Override
     9    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    10        SqlSession sqlSession=null;
    11        Object obj=null;
    12        try{
    13            sqlSession=SqlSessionUtil.getSqlSession();
    14            //处理业务逻辑
    15            obj=method.invoke(target,args);
    16            //业务逻辑完毕后,提交事务
    17            sqlSession.commit();
    18        }catch (Exception e){
    19            //出错回滚
    20            sqlSession.rollback();
    21            e.printStackTrace();
    22        }finally {
    23            SqlSessionUtil.closeSqlSession(sqlSession);
    24        }
    25        return obj;
    26    }
    27
    28    /**
    29     * 取得代理对象
    30     * @return 代理对象
    31     */
    32    public Object getProxy(){
    33        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
    34                target.getClass().getInterfaces(),
    35                this);
    36    }
    37}

manev配置

xml
 1<?xml version="1.0" encoding="UTF-8"?>
 2
 3<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5  <modelVersion>4.0.0</modelVersion>
 6
 7  <groupId>org.example</groupId>
 8  <artifactId>ch03</artifactId>
 9  <version>1.0-SNAPSHOT</version>
10  <packaging>war</packaging>
11
12  <name>ch03 Maven Webapp</name>
13  <!-- FIXME change it to the project's website -->
14  <url>http://www.example.com</url>
15
16  <properties>
17    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
18    <maven.compiler.source>1.7</maven.compiler.source>
19    <maven.compiler.target>1.7</maven.compiler.target>
20  </properties>
21
22  <dependencies>
23    <dependency>
24      <groupId>junit</groupId>
25      <artifactId>junit</artifactId>
26      <version>4.11</version>
27      <scope>test</scope>
28    </dependency>
29    <dependency>
30      <groupId>javax.servlet</groupId>
31      <artifactId>servlet-api</artifactId>
32      <version>2.5</version>
33    </dependency>
34    <dependency>
35      <groupId>javax.servlet.jsp</groupId>
36      <artifactId>jsp-api</artifactId>
37      <version>2.2</version>
38    </dependency>
39    <dependency>
40      <groupId>org.MyBatis</groupId>
41      <artifactId>MyBatis</artifactId>
42      <version>3.5.5</version>
43    </dependency>
44    <dependency>
45      <groupId>mysql</groupId>
46      <artifactId>mysql-connector-Java</artifactId>
47      <version>5.1.3</version>
48    </dependency>
49  </dependencies>
50
51  <build>
52    <finalName>ch03</finalName>
53    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
54      <plugins>
55        <plugin>
56          <artifactId>maven-clean-plugin</artifactId>
57          <version>3.1.0</version>
58        </plugin>
59        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
60        <plugin>
61          <artifactId>maven-resources-plugin</artifactId>
62          <version>3.0.2</version>
63        </plugin>
64        <plugin>
65          <artifactId>maven-compiler-plugin</artifactId>
66          <version>3.8.0</version>
67        </plugin>
68        <plugin>
69          <artifactId>maven-surefire-plugin</artifactId>
70          <version>2.22.1</version>
71        </plugin>
72        <plugin>
73          <artifactId>maven-war-plugin</artifactId>
74          <version>3.2.2</version>
75        </plugin>
76        <plugin>
77          <artifactId>maven-install-plugin</artifactId>
78          <version>2.5.2</version>
79        </plugin>
80        <plugin>
81          <artifactId>maven-deploy-plugin</artifactId>
82          <version>2.8.2</version>
83        </plugin>
84      </plugins>
85    </pluginManagement>
86    <resources>
87        <!--指定资源目录,编译xml与propertis文件-->
88      <resource>
89        <directory>src/main/java</directory>
90        <includes>
91          <include>**/*.xml</include>
92          <include>**/*.properties</include>
93        </includes>
94        <filtering>false</filtering>
95      </resource>
96    </resources>
97  </build>
98</project>
使用滚轮缩放
按住拖动