61-Mybatis介绍

  • Post author:
  • Post category:其他



Mybatis介绍

框架简介:


三层架构:


软件开发常用的架构是三层架构,之所以流行是因为有着清晰的任务划分,一般包括以下三层:


持久层:主要完成与数据库相关的操作,即对数据库的增删改查。


因为数据库访问的对象一般称为Data Access Object(简称DAO),所以有人把持久层叫做DAO层


业务层:主要根据功能需求完成业务逻辑的定义和实现


因为它主要是为上层提供服务的,所以有人把业务层叫做Service层或Business层


表现层:主要完成与最终软件使用用户的交互,需要有交互界面(UI)


因此,有人把表现层称之为web层或View层


三层架构之间调用关系为:表现层调用业务层,业务层调用持久层


各层之间必然要进行数据交互,我们一般使用java实体对象来传递数据

在这里插入图片描述



框架:


什么是框架:


框架就是一套规范,既然是规范,你使用这个框架就要遵守这个框架所规定的约束


框架可以理解为半成品软件,框架做好以后,接下来在它基础上进行开发


为什么使用框架:


框架为我们封装好了一些冗余,且重用率低的代码,并且使用反射与动态代理机制,将代码实现了通用性


让开发人员把精力专注在核心的业务代码实现上


比如在使用servlet进行开发时,需要在servlet获取表单的参数,每次都要获取很麻烦


而框架底层就使用反射机制和拦截器机制帮助我们获取表单的值


使用jdbc每次做专一些简单的crud的时候都必须写sql


但使用框架就不需要这么麻烦了,直接调用方法就可以,当然,既然是使用框架,那么还是要遵循其一些规范进行配置


常见的框架:


Java世界中的框架非常的多,每一个框架都是为了解决某一部分或某些问题而存在的


下面列出在目前企业中流行的几种框架(一定要注意他们是用来解决哪一层问题的):


持久层框架:专注于解决数据持久化的框架,常用的有mybatis、hibernate、spring jdbc等等


表现层框架:专注于解决与用户交互的框架,常见的有struts2、spring mvc等等。


全栈框架:能在各层都给出解决方案的框架,比较著名的就是spring,


这么多框架,我们怎么选择呢:


我们以企业中最常用的组合为准来学习Spring + Spring MVC + mybatis(SSM)


Mybatis简介:


原始jdbc操作(查询数据):

在这里插入图片描述



对于多次同连接,实际上是对应会话,即与服务连接,并不是占端口,而是服务占对应端口,所以可以有多个相同连接


读取的数据大多数是字符串


原始jdbc操作的分析:


原始jdbc开发存在的问题如下:


数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能


sql 语句在代码中硬编码,造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变java代码


即又要打包部署,这样非常麻烦


查询操作时,需要手动将结果集中的数据手动封装到实体中


应对上述问题给出的解决方案:


使用数据库连接池初始化连接资源


将sql语句抽取到xml配置文件中


使用反射、内省等底层技术,自动将实体与表进行属性与字段的自动映射


MyBatis是一个优秀的基于ORM的半自动轻量级持久层框架,它对jdbc的操作数据库的过程进行封装


使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动


创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码


mybatis 历史 :


MyBatis 本是apache的一个开源项目iBatis, 2010年6月这个项目由apache software foundation 迁移到了google code


随着开发团队转投到Google Code旗下,iBatis正式改名为MyBatis ,代码于2013年11月迁移到Github


Github地址:https://github.com/mybatis/mybatis-3/

在这里插入图片描述

在这里插入图片描述



ORM思想:


ORM(Object Relational Mapping)对象关系映射:


O(对象模型):


实体对象,即我们在程序中根据数据库表结构建立的一个个实体javaBean


R(关系型数据库的数据结构):


关系数据库领域的Relational(建立的数据库表)


M(映射):


从R(数据库)到O(对象模型)的映射,可通过XML文件映射


实现:


让实体类和数据库表进行一一对应关系


先让实体类和数据库表对应


再让实体类属性和表里面字段对应


不需要直接操作数据库表,直接操作表对应的实体类对象

在这里插入图片描述



ORM作为是一种思想


帮助我们跟踪实体的变化,并将实体的变化翻译成sql脚本,执行到数据库中去,也就是将实体的变化映射到了表的变化


mybatis采用ORM思想解决了实体和数据库映射的问题,对jdbc 进行了封装,屏蔽了jdbc api 底层访问细节


使我们不用与jdbc api 打交道,就可以完成对数据库的持久化操作


Mybatis快速入门:


MyBatis开发步骤:


MyBatis官网地址:http://www.mybatis.org/mybatis-3/

在这里插入图片描述



案例需求:通过mybatis查询数据库user表的所有记录,封装到User对象中,打印到控制台上


步骤分析:
/*
1. 创建数据库及user表
2. 创建maven工程,导入依赖(MySQL驱动、mybatis、junit)
 
3. 编写User实体类
4. 编写UserMapper.xml映射配置文件(ORM思想)
5. 编写SqlMapConfig.xml核心配置文件
   数据库环境配置
   映射关系配置的引入(引入映射配置文件的路径)
6. 编写测试代码 
 // 1.加载核心配置文件
 // 2.获取sqlSessionFactory工厂对象
 // 3.获取sqlSession会话对象
 // 4.执行sql
 // 5.打印结果
 // 6.释放资源

*/


代码实现:


创建user数据表:
CREATE DATABASE `mybatis_db`;
USE `mybatis_db`;
CREATE TABLE `user` (
 `id` int(11) NOT NULL auto_increment,
 `username` varchar(32) NOT NULL COMMENT '用户名称',
 `birthday` datetime default NULL COMMENT '生日',
 `sex` char(1) default NULL COMMENT '性别',
 `address` varchar(256) default NULL COMMENT '地址',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- insert.... 
insert  into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (1,'子
慕','2020-11-11 00:00:00','男','北京海淀'),(2,'应颠','2020-12-12 00:00:00','男','北
京海淀');



导入MyBatis的坐标和其他相关坐标:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-
                             4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lagou</groupId>
    <artifactId>mybatis_quickstart</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!--指定编码和版本-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>1.11</java.version>
        <maven.compiler.source>1.11</maven.compiler.source>
        <maven.compiler.target>1.11</maven.compiler.target>
    </properties>


    <!--引入相关依赖-->
<dependencies>
<!--引入mybatis依赖-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.4</version>
    </dependency>

    <!--引入mysql驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.8</version>
    </dependency>
    <!--引入junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
<!--注意:有些问题是版本的问题,可能需要增加版本,当然,出错误时,百度是好的选择-->
</dependencies>
</project>


编写User实体 :
package com.lagou.domain;

import java.util.Date;

/**
 *
 */
public class User {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    public User() {
    }

    public User(Integer id, String username, Date birthday, String sex, String address) {
        this.id = id;
        this.username = username;
        this.birthday = birthday;
        this.sex = sex;
        this.address = address;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}



编写UserMapper映射文件(mapper/UserMapper.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="user">
    <!--namespace:命令空间,与id属性共同构成唯一标识 namespace.id:user.id-->
    <select id="findAll" resultType="com.lagou.domain.User">
<!--resultType指定查询结果给那个实体类(这里是这样)
当然若是单个的类型,如Integer(这里可以写上integer)
那么将结果最左边的进行赋值,若对应的值若是有字母,如1a,那么只取1,若是a1,那么报错,而String则全部取得
最后,像一般的这样的赋值(类的赋值),都是set方法执行的,且基本只识别后面set名称(根据表来的,以后说明的基本也是如此,即set一般是其他,而get一般是自己),大小写忽略,这里若没有对应的set方法,则会自己直接赋值,进行得值,即会先判断是否有对应变量
变量大小写忽略
即若是有对应变量,且没有对应set
则直接赋值(反射可以给private赋值,后面可能会说成创建set,这个意思就理解成直接赋值)
若没有对应变量,有对应set,则使用对应set,若都有,则调用set
若都没有,则不进行赋值,所以基本无论如何resultType对应的类是否有对应参数,都不会报错

若对应的是null,也就是对应字段没有值,那么直接按照默认值,即不赋值(不赋值的情况下自然就是默认值了,除非你初始化赋值了,那么自然使用的就是初始化的了)
其他情况就会赋值,所以当出现不同类型的值时,是可能会报错的
-->
        select * from user
             <!--上面自动操作的对方是*,即都是小写,无论手动设置大小还是,什么都是小写(这是数据库查询的原因,我们后端并不能改变),但是*和别名以及直接名称是一样的,所以在以后自动对应时,操作他们要注意(特别是别名)-->

    </select>

</mapper>
<!-- 由于名称是可以随便起的,且是自己定义的,那么在读取配置文件时需要手动读取,这里一般被导入
且不能有一样id的对应元素,否则读取时,会报错,因为会检查
即上面的id就不能是一样的,而之所以需要加namespace,是防止其他配置文件的id一样,所以必须要加这个参数,否则操作
时,会报错,当然这个也不能是一样的,否则读取时,也会报错,也是因为检查
检查可以理解为程序的判断,是一个判断对应操作的信息是否符合的操作
所有读取配置文件报错的,一般都是检查造成的,如读取时的检查
-->



编写MyBatis核心文件(配置文件在资源文件夹下面:sqlMapConfig.xml,resources(maven的)):
<?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 default="development">
        <!--default指定什么环境,即默认走下面那个配置,对应的environment可以是多个的,即看环境的
        development:开发环境
        staging:测试环境
        production:生产环境

        -->

        <environment id="development">
            <!--当前的事务管理器是JDBC,即当前的事务交由JDBC管理,即可以对应的事务方法-->
            <transactionManager type="JDBC"></transactionManager>
            <!--数据源信息,POOLED:使用mybatis的连接池,因为既然使用框架
那么就需要非常好的优化,连接池就可以
            UNPOOLED:不使用连接池,直接创建一个连接,最后关闭
            -->
            <dataSource type="POOLED">

                <property name="driver" value="com.mysql.jdbc.Driver"></property>
                <property name="url" value="jdbc:mysql:///mybatis_db"></property>
                <!--如果是本机,localhost:3306可以省略,但后面的/并没有省略,所以上面需要是/mybatis_db-->
                <property name="username" value="root"></property>
                <property name="password" value="123456"></property>
            </dataSource>
            <!--上面决定了驱动,连接信息,即这些信息,给对应连接池操作,给出对应连接-->
        </environment>
    </environments>

    <!--引入映射配置文件-->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"></mapper>
        <!--不能有空值,即不能换行,且默认从项目路径开始找,即没有相对路径
所以当前配置文件在与这个配置文件一个包时
        也要从项目路径开始找,因为是引入的需要被加载,所以需要指定路径(因为加载都是从项目路径开始)-->
    </mappers>
</configuration>


编写测试类:
package com.lagou.test;

import com.lagou.domain.User;
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 java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 *
 */
public class MybatisTest {

    /**
     *快速入门测试方法
     */
    @Test
    public void mybatisQuickStart() throws IOException {
        //加载核心配置文件,如果有引入,根据路径再次加载,就是获得该配置文件的输入流
        //因为文件一般都是与IO流关联的
        //xml底层也是IO流,都是读取文件,只是有对应的约束以及自己的顺序规则
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        //获取sqlSessionFactory工厂对象,通过输入流,得到对应的信息,来确定连接
        //相当于连接池,那么基本没有close()方法
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        //得到一个sqlSession对象,即获得其中一个连接,以及sql语句执行平台,简称会话对象
        SqlSession sqlSession = build.openSession();
        //通过会话对象,执行对应方法,该方法有对应的参数,该参数是上面配置文件中映射到的配置文件里的对应值
        List<User> objects = sqlSession.selectList("user.findAll");
        //上面配置文件加载的另外一个配置文件,就给这个对应方法使用的
        //使用这个方法,就回去得到加载的IO进行读取
        //然后根据参数确定位置,并判断元素类型是否与该方法所操作的类型一致
        //如果一致,就获得对应sql值,否则返回-1或者报错
        //得到sql值,进行对应操作,即通过会话中的sql语句平台,进行执行,并返回数据
        //实际上是先通过resultType的值进行反射,获得对应类,然后将数据通过对应的执行操作
        //如类似于BeanHandler,将数据封装到对应的类中
        for(User user : objects){
            System.out.println(user);
        }
        sqlSession.close();
        //上面的配置文件里有,驱动,连接信息,sql,方法自带的将数据封装到对应的类中
        //使得部署时,可以随时修改对应信息,当然了,虽然是反射的类,也基本不能改变对应实体类
        //但对比传统的来说,可扩展性更高了,可以在部署时进行操作了

    }
}



知识小结 :
/*
1. 创建mybatis_db数据库和user表
2. 创建项目,导入依赖
3. 创建User实体类
4. 编写映射文件UserMapper.xml
5. 编写核心文件SqlMapConfig.xml
6. 编写测试类
*/


最后注意:虽然说过了默认的给类赋值,会按照set方法来赋值,但最好还是按照默认的set方法对应名称来进行操作


不要随意修改,因为有些情况,会出现检查名称,使得赋值不了或者出现默认值,更有可能会报错


但基本上对应名称大小写是忽略的(建议不要修改)


Mybatis映射文件概述:

在这里插入图片描述



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="userMapper">
    <!--namespace:命令空间,与id属性共同构成唯一标识 namespace.id:user.id-->
  <select id="findAll" resultType="com.lagou.domain.User">
<!--resultType指定查询结果给那个实体类,最后,像一般的这样的赋值,都是set方法执行或者直接赋值,大小写忽略-->
        select * from user
<!--当然,若没有进行赋值,那么返回null(这是框架的判断,如判断到对应结果集是空的,则返回null),但是若返回的是不同类型的,那么就会报错
除非不同类型没有赋值,那么返回null也是可以的-->
    </select>

    <!-- 新增用户-->
    <!-- #{}:mybatis中的占位符,等同于JDBC中的?
    实际上就是在读取到数据时,会将#{}中进行?的等同操作,因为对应的sql代码平台是mybatis创建的,各有各的规范
    但都是进行检查替换,防止sql注入,因为对应值会变成sql可以转义的值
使得注入不了,如'变成\',那么在sql里就是'了
    -->
    <insert id="saveUser" parameterType="com.lagou.domain.User">
<!-- parameterType:指定接收到的参数类型 -->
        insert into user(username,birthday,sex,address) value (#{username},#{birthday},#{sex},#
        {address})
         <!--这里说明一下:对应替换,是使用get方法,若没有get方法则直接赋值
        且无论是get后面名称还是直接赋值,若#里面是小写的
那么首字母是对应变量(#里面的)的大小写,若是大写的,则只要赋值(不会操作get方法),且必须一致
对应的${}替换也是一样的-->
    </insert>

    <!--更新用户-->
    <update id="updateUser" parameterType="com.lagou.domain.User">
        update user set username = #{username},birthday = #{birthday},sex = #{sex},address = #
        {address} where id = #{id}
    </update>


    <!--删除用户 java.lang.Integer-->
    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id = #{abc}
<!--这里#{}里的值,可以随便写,因为指定的类型只获得一个操作变量(操作变量,只外出一个变量)
可以理解为对应基本类型变量类会直接替换,不检查对应变量
而前面指定的类型有多个变量,即需要对应变量名来确定位置
而这里就一个变量,所以,是不用对应变量名的,即不用确定对应位置
即基本可以随便写,但是不能为空,否则会报错,当然
本来就是不能为空的,因为需要替换,找不到对应值,就会报错
若不传参,则默认为null,自然对于多余的也是一样,因为多余的得不到替换
当然前提是你需要在数据库里面加上null数据
而不是数据库的null字符串(使用程序可以做到,但mysql直接添加基本不可以),虽然替换基本操作不了null,如is(在sql却可以操作is)

-->
    </delete>

</mapper>
<!-- 由于名称是可以随便起的,且是自己定义的,那么在读取配置文件时,需要手动读取,这里一般被导入
且不能有一样id的对应元素,否则读取时,会报错,因为会检查
即上面的id就不能是一样的,而之所以需要加namespace,是防止其他配置文件的id一样,所以必须要加这个参数,否则操作
时,会报错
所有读取配置文件报错的,一般都是检查造成的,如读取时的检查
-->

<?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 default="development">
        <!--default指定什么环境,即默认走下面那个配置
        development:开发环境
        staging:测试环境
        production:生产环境

        -->

        <environment id="development">
            <!--当前的事务管理器是JDBC,即当前的事务交由JDBC管理
即可以对应的事务方法,且会检查这个,没写的话,会报错-->
            <transactionManager type="JDBC"></transactionManager>
            <!--数据源信息,POOLED:使用mybatis的连接池
因为既然使用框架,那么就需要非常好的优化,连接池就可以
            UNPOOLED:不使用连接池,直接创建一个连接,最后关闭
            -->
            <dataSource type="POOLED">

                <property name="driver" value="com.mysql.jdbc.Driver"></property>
                <property name="url" value="jdbc:mysql:///mybatis_db?characterEncoding=utf-8">
                </property>
                <!--如果是本机,localhost:3306可以省略,但后面的/并没有省略,所以上面需要是/mybatis_db-->
                <property name="username" value="root"></property>
                <property name="password" value="123456"></property>
            </dataSource>
            <!--上面决定了驱动,连接信息,即这些信息,给对应连接池操作,给出对应连接-->
        </environment>
    </environments>

    <!--引入映射配置文件-->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"></mapper>
        <!--不能有空值,即不能换行,且默认从项目路径开始找,即没有相对路径
所以当前配置文件在与这个配置文件一个包时
        也要从项目路径开始找,因为是引入的需要被加载,所以需要指定路径(因为加载都是从项目路径开始)-->
    </mappers>
</configuration>
package com.lagou.test;

import com.lagou.domain.User;
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 java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;

/**
 *
 */
public class MybatisTest {

    /**
     *快速入门测试方法
     */
    @Test
    public void mybatisQuickStart() throws IOException {
        //加载核心配置文件,如果有引入,根据路径再次加载
        //就是获得该配置文件的输入流,因为文件一般都是与IO流关联的
        //xml底层也是IO流,都是读取文件,只是有对应的约束以及自己的顺序规则
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        //获取sqlSessionFactory工厂对象,通过输入流,得到对应的信息
        //来确定连接,相当于连接池,那么基本没有close()方法
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        //得到一个sqlSession对象,即获得其中一个连接,以及sql语句执行平台,简称会话对象
        SqlSession sqlSession = build.openSession();
        //通过会话对象,执行对应方法,该方法有对应的参数,该参数是上面配置文件中映射到的配置文件里的对应值
        List<User> objects = sqlSession.selectList("userMapper.findAll");
        //上面配置文件加载的另外一个配置文件,就给这个对应方法使用的
        //使用这个方法,就回去得到加载的IO进行读取
        //然后根据参数确定位置,并判断元素类型是否与该方法所操作的类型一致,如果一致
        //就获得对应sql值,否则返回-1或者报错
        //得到sql值,进行对应操作,即通过会话中的sql语句平台,进行执行,并返回数据
        //实际上是先通过resultType的值进行反射,获得对应类,然后将数据通过对应的执行操作
        //如类似于BeanHandler,将数据封装到对应的类中
        for(User user : objects){
            System.out.println(user);
        }
        sqlSession.close();

    }

    /*
       测试新增用户
    */
    @Test
    public void testSave() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new 
            SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);


        User user = new User();
        user.setUsername("自动提交事务");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddress("北京海淀");

        int insert = sqlSession.insert("userMapper.saveUser", user);
        //当然也是返回影响行数。因为也是使用类似的sql代码平台进行操作的,即对应的update和query等类似操作

        System.out.println(insert);
        // 手动提交事务
        sqlSession.commit();
        //由于事务的存在,使得不会自动提交,需要手动提交,openSession方法底层进行调用相关的事务操作(默认开启事务)
        //因为增删改都是一个事务,本来都是自动提交的,现在要手动的,且自增不参与回滚
        //注意:编码一般需要设置一下,在url里
        //我们在读取配置文件时,读取到的sql的占位,由传入的user参数,进行替换,#{}里面的会对应user的变量值
        //使得进行占位,当然占位时,也是会进行sql的检查的,防止sql注入
        //他们会先进行变量的检查,即当{}里面的与变量完全一致时(也可以是对应变量的小写)
        //才将对应变量值进行替换,如果有对应get方法,则使用对应get方法,否则直接创建一个get方法,进行得值
        //与set一样,都必须得值
        //否则报错
        //注意:这里可以不用设置值,因为类有默认值,所以始终是替换的
        sqlSession.close();

    }

    /*
  测试更新用户
*/
    @Test
    public void testUpdate() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new 
            SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();


        User user = new User();
        user.setId(4);
        user.setUsername("lucy");
        user.setBirthday(new Date());
        user.setSex("女");
        user.setAddress("北京朝阳");

        sqlSession.update("userMapper.updateUser",user);
        //参数必须与对应的类的类型一致,即反射的parameterType的类的类型一致

        // 手动提交事务
        sqlSession.commit();
        sqlSession.close();

    }

    /*
   测试删除用户
   */
    @Test
    public void testDelete() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new 
            SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        sqlSession.delete("userMapper.deleteUser",1);
        Integer integer = new Integer(1);
        // 手动提交事务
        sqlSession.commit();
        sqlSession.close();


    }
}



Mybatis核心文件概述 :


MyBatis核心配置文件层级关系 :


MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息


配置文档的顶层结构如下:

在这里插入图片描述



必须按照上面的顺序,因为约束存在,也可以说明是读取顺序


MyBatis常用配置解析 :


environments标签 :


数据库环境的配置,支持多环境配置

在这里插入图片描述

/*
1. 其中,事务管理器(transactionManager)类型有两种:
   - JDBC:
     这个配置就是直接使用了JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
     
   - MANAGED:
     这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。
     例如:mybatis与spring整合后,事务交给spring容器管理。
     
2. 其中,数据源(dataSource)常用类型有三种:
   - UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
     
   - POOLED:
     这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来。
     
   - JNDI :
     这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据
源,然后放置一个 JNDI 上下文的数据源引用

*/


properties标签:


实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mybatis_db
#如果是本机,localhost:3306可以省略,但后面的/并没有省略,所以上面需要是/mybatis_db
jdbc.username=root
jdbc.password=123

在这里插入图片描述



当加载后,会存放对应加载信息,那么可以在加载本配置文件时


查看${}里的值,进行配对,使得使用配对成功的对应值进行连接信息


注意:properties文件一般都是进行键值对配对的,即对应名称和对应变量一致,若不一致,就不会赋值


但是对应驱动的可以不一致,主要是对应驱动会自己注册(要一定的版本)


typeAliases标签:


类型别名是为 Java 类型设置一个短的名字


为了简化映射文件 Java 类型设置,mybatis框架为我们设置好的一些常用的类型的别名:

在这里插入图片描述



原来的类型名称配置如下:

在这里插入图片描述



配置typeAliases,为com.lagou.domain.User定义别名为user,基本只作用与parameterType和resultType,即他们两个会使用别名


实际上大多数的标签属性都会进行别名替换的,有些特殊的不会,如id等


你可以理解成:之所以需要配置别名,才可简写,是因为不同的包里面可以有相同的类,而java.lang.Integer可以简写成Integer和int(一般来说,他们不区分大小写)


因为这个类是唯一的(他们可以互相转换,一般都是变成int),所以Mybatis就帮我们封装了对应别名,因为默认也是代码进行的


不要当成理所应当


由于这个是写在映射配置的前面,所以会存放好这个别名,当对应映射的对应属性是这个别名时,就会替换的进行反射
  <!--设置别名-->
    <typeAliases>
         <!--方式一:给单个实体起别名,一般是对应的alias="user"的user不区分大小写,这时与类无关-->
        <!-- <typeAlias type="com.lagou.domain.User" alias="user"></typeAlias>-->
        <!--方式二:批量起别名 别名就是类名,且两种方式不区分大小写(一般也会包括上面的,虽然他指定user,但其实一般也不区分大小写)-->
        <package name="com.lagou.domain"/>

    </typeAliases>


上面就是对应设置,但之所以可以使用方式二,是因为前面说过


在没有配置前,不同的包里可以有相同的类,那么不可以简写对应类,实际上是Mybatis封装好的


当配置方式一时,我们是指定某个包里的某个类,但是类可以有更多,且我们又要简写,所以我们直接指定一个包,进行批量别名


使得只在一个包里面进行,因为一个包里面是不可以又相同类的,所以就可以使用方式二进行多个类的简写了


但是也要注意:别名是别名,是需要出现别名才会转化的,所以正常写也是可以的


mappers标签:


该标签的作用是加载映射的,加载方式有如下几种:
/*
1. 使用相对于类路径的资源引用,例如:
 <mapper resource="org/mybatis/builder/userMapper.xml"/>
2. 使用完全限定资源定位符(URL),例如:
 <mapper url="file:///var/mappers/userMapper.xml"/>
 
 
《下面两种mapper代理开发中使用:暂时了解》
3. 使用映射器接口实现类的完全限定类名,例如:
 <mapper class="org.mybatis.builder.userMapper"/>
4. 将包内的映射器接口实现全部注册为映射器,例如:
 <package name="org.mybatis.builder"/>
 */


知识小结:


核心配置文件常用配置:
//<properties resource="jdbc.properties"></properties> //可以操作url,与mapper的url类似,没指定则默认c盘 (file:///或者file:/开头),默认file:///var那么就是file:///C:var,file:///c:var,file:///C:/var,file:///c:/var,一般默认是file:///C:var,当然,这些自己进行测试,才是最好的说明


typeAliases标签:设置类型别名:
//<typeAlias type="com.lagou.domain.User" alias="user"></typeAlias>


mappers标签:加载映射配置:
//<mapper resource="com/lagou/mapper/UserMapping.xml"></mapper>


environments标签:数据源环境配置:
/*
<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </dataSource>
    </environment>
</environments>
*/


Mybatis的API概述:


API介绍:


SqlSession工厂构建器SqlSessionFactoryBuilder


常用API:SqlSessionFactory build(InputStream inputStream)


通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象
/*
String resource = "org/mybatis/builder/mybatis-config.xml"; 
InputStream inputStream = Resources.getResourceAsStream(resource); 
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); 
SqlSessionFactory factory = builder.build(inputStream);
*/


其中, Resources 工具类,这个类在 org.apache.ibatis.io 包中。Resources 类帮助你从类路径下


文件系统或一个 web URL 中加载资源文件


SqlSession工厂对象SqlSessionFactory :


SqlSessionFactory 有多个个方法创建SqlSession 实例。常用的有如下两个:

在这里插入图片描述



设置true,实际上就是不开启事务,那么增删改会启动自带的自动提交,所以对应查询操作是没有任何影响的


因为无论是否开启事务,第一次查询都基本上是表的数据,但是第二次以及以后的查询,若在事务里面,可能不会与表一致了


因为还没有提交,使得表数据不变,但当前事务表已经改变,所以查询的是这个事务表,而不是原表,第一次原表与事务表一致


SqlSession会话对象


SqlSession 实例在 MyBatis 中是非常强大的一个类


在这里你会看到所有执行语句、提交或回滚事务和获取映射器实例的方法


执行语句的方法主要有:
/*
<T> T selectOne(String statement, Object parameter) 
<E> List<E> selectList(String statement, Object parameter) 
int insert(String statement, Object parameter) 
int update(String statement, Object parameter) 
int delete(String statement, Object parameter)
*/


操作事务的方法主要有:
/*
void commit()  
void rollback()  //回滚
*/


Mybatis基本原理介绍:

在这里插入图片描述

在这里插入图片描述



Mybatis的dao层开发使用 :


注意:pom.xml只是将对应依赖下载好,放到一个位置,然后引入在项目的包目录下,并自动进行配置


若这时你删掉pom.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="userMapper">
    <!--查询所有用户-->
    <select id="findAll" resultType="user">
        select * from user
    </select>
</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>

    <!--加载properties文件-->
    <properties resource="jdbc.properties"></properties>

    <!--设置别名-->
    <typeAliases>
        <!--方式一:给单个实体起别名-->
        <!-- <typeAlias type="com.lagou.domain.User" alias="user"></typeAlias>-->
        <!--方式二:批量起别名 别名就是类名,且不区分大小写-->
        <package name="com.lagou.domain"/>

    </typeAliases>


    <!--environments:运行环境,如开放环境,生产环境,测试环境
    可以理解寻常操作使用开放环境的配置
    测试操作使用测试环境的配置
    打包部署:使用生产环境的配置-->
    <environments default="development">
        <!--default指定什么环境,即默认走下面那个配置
        development:开发环境
        staging:测试环境
        production:生产环境

        -->

        <environment id="development">
            <!--当前的事务管理器是JDBC,即当前的事务交由JDBC管理
即可以对应的事务方法,且会检查这个,没写的话,会报错-->
            <transactionManager type="JDBC"></transactionManager>
            <!--数据源信息,POOLED:使用mybatis的连接池,因为既然使用框架
那么就需要非常好的优化,连接池就可以
            UNPOOLED:不使用连接池,直接创建一个连接,最后关闭
            -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>

            </dataSource>
            <!--上面决定了驱动,连接信息,即这些信息,给对应连接池操作,给出对应连接-->
        </environment>
    </environments>

    <!--引入映射配置文件-->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"></mapper>
        <!--不能有空值,即不能换行,且默认从项目路径开始找,即没有相对路径
所以当前配置文件在与这个配置文件一个包时
        也要从项目路径开始找,因为是引入的需要被加载,所以需要指定路径(因为加载都是从项目路径开始)-->
    </mappers>
</configuration>
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mybatis_db?characterEncoding=utf8
#如果是本机,localhost:3306可以省略,但后面的/并没有省略,所以上面需要是/mybatis_db
jdbc.username=root
jdbc.password=123456
package com.lagou.dao;

import com.lagou.domain.User;

import java.io.IOException;
import java.util.List;

/**
 *
 */
public interface IUserDao {

    /*
    查询所有
     */
    public List<User> findAll() throws IOException;
}

package com.lagou.dao.impl;

import com.lagou.dao.IUserDao;
import com.lagou.domain.User;
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;
import java.util.List;

/**
 *
 */
public class UserDaoImpl  implements IUserDao {
    @Override
    public List<User> findAll() throws IOException {
        
        //模板重复
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);

        SqlSession sqlSession = build.openSession();
		
        //写死的对应参数
        List<User> objects = sqlSession.selectList("userMapper.findAll");


        return objects;
    }
/*
    @Override
    public void saveUser(User user) throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);

        SqlSession sqlSession = build.openSession();

        sqlSession.insert("userMapper.saveUser",user);
    }
    */
}

package com.lagou.domain;

import java.util.Date;

/**
 *
 */
public class User {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    public User() {
    }

    public User(Integer id, String username, Date birthday, String sex, String address) {
        this.id = id;
        this.username = username;
        this.birthday = birthday;
        this.sex = sex;
        this.address = address;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

package com.lagou.test;

import com.lagou.dao.impl.UserDaoImpl;
import com.lagou.domain.User;
import org.junit.Test;

import java.io.IOException;
import java.util.List;

/**
 *
 */
public class MybatisText {

    /*
    Mybatis的dao层传统方式测试
     */

  @Test
  public void test1() throws IOException {
      //调用持久层对象
      UserDaoImpl userDao = new UserDaoImpl();
      List<User> all = userDao.findAll();
      for (User user : all) {
          System.out.println(user);
      }

  }
}



传统方式问题思考:


实现类中,存在mybatis模板代码重复


实现类调用方法时,xml中的sql statement 硬编码到java代码中


即userMapper.findAll对应名称不能改变,改变了,就需要java代码也要改变


思考:能否只写接口,不写实现类(里面使用了Mapper.xml),只编写接口和Mapper.xml即可,答:可以


因为在dao(mapper)的实现类中对sqlsession的使用方式很类似,因此mybatis提供了接口的动态代理


代理开发方式:


采用 Mybatis 的基于接口代理方式实现 持久层 的开发


注意:只能是接口,而不能是类,否则会报错,这是他的底层操作的规定的,直接去除了类的操作,即一般是用来当作实现,所以基本只能是接口


而之所以使用接口,而不使用类,是因为接口实现非常方便,易于扩展,所以就不操作类的实现了


那么既然不操作类的实现,自然就不能是类,若是类,则会报错


这种方式是我们后面进入企业的主流


基于接口代理方式的开发只需要程序员编写 Mapper 接口,Mybatis 框架会为我们动态生成实现类的对象


这种开发方式要求我们遵循一定的规范:

在这里插入图片描述



注意:在资源文件夹里,无论怎么设置创建如com.lagou.mapper,都是没有层级的


因为在资源文件里面,创建的是文件夹,而不是包,所以上面会看成一个整体文件夹名称,而包会识别” . “,从而分层


要设置文件夹层级,可以com/lagou/mapper来进行创建设置,包不识别” / “,但识别的还是会创建


最后创建的,若是与主文件目录一致,那么就会到里面去,如都是com开头,那么只有一个com(out文件夹里面显示可以看到)


即相当于是同一个目录了


Mapper.xml映射文件中的namespace与mapper接口的全限定名相同


Mapper接口方法名和Mapper.xml映射文件中定义的每个statement的id相同


Mapper接口方法的输入参数类型和mapper.xml映射文件中定义的每个sql的parameterType的类型相同


Mapper接口方法的输出参数类型和mapper.xml映射文件中定义的每个sql的resultType的类型相同


Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis 框架根据接口


定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法
<?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.lagou.mapper.UserMapper">

    <!--根据id查询用户-->
    <select id="findUserById" parameterType="int" resultType="user">
        select * from user where id = #{id}
    </select>
     <!--要实现不用实现类,即不用自己创建对应工厂对象以及自己写的对应sql名称
    那么就需要一些规范,因为这是为了实现上面操作进行的规范
    一般的,我们在指定sql名称时,通常都需要对应参数,即parameterType一致
    而实现类的参数通常都是这个参数
    那么我们规定,接口的参数就与上面的parameterType的值是一样的一致类型
    那么如何确定sql名称呢,很明显,我们可以通过接口路径和对应方法名来确定
    返回的resultType有接口方法返回值来确定
    这里注意一下:只有select元素可以有parameterType和resultType这两个属性
    其他的insert,update,delete都只有parameterType属性,因为他们返回的都是int类型
    而之所以select需要resultType,是因为sql返回的数据中,要确定Object的对应类型是什么
    否则确定不了,不写的话,就会报错
    那么我们sql名称和对应输入输出的,都可以确定
    即当我们直接调用接口方法时,会自动的创建对应的工厂对象,然后根据接口的路径,返回值,参数,方法名
    来确定对应的模板参数
    自身接口路径加方法名=sql名称
    自身参数=sql名称需要传递的参数
    自身返回值=调用对应sql返回的值,这里之所以要一致,是因为会返回获得的类型,所以一般与对于resultType一致
    那么就形成了不用写实现类,就帮我们创建了对应类,且对应sql名称不是写死的了
    但是要注意:必须要在同一路径,否则根据自身路径判断会报错的
    而像void返回值,和无参,一般的,表示对应resultType和parameterType的参数是没有的
    但通常他们都会至少存在一个的

    最后:不要将.当成路径,路径一般都是/来表示的,而包的创建之所以可以用.,是因为识别


    -->
</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>

    <!--加载properties文件-->
    <properties resource="jdbc.properties"></properties>

    <!--设置别名-->
    <typeAliases>
        <!--方式一:给单个实体起别名-->
        <!-- <typeAlias type="com.lagou.domain.User" alias="user"></typeAlias>-->
        <!--方式二:批量起别名 别名就是类名,且不区分大小写-->
        <package name="com.lagou.domain"/>

    </typeAliases>


    <!--environments:运行环境,如开放环境,生产环境,测试环境
    可以理解寻常操作使用开放环境的配置
    测试操作使用测试环境的配置
    打包部署:使用生产环境的配置-->
    <environments default="development">
        <!--default指定什么环境,即默认走下面那个配置
        development:开发环境
        staging:测试环境
        production:生产环境

        -->

        <environment id="development">
            <!--当前的事务管理器是JDBC,即当前的事务交由JDBC管理,即可以对应的事务方法
且会检查这个,没写的话,会报错-->
            <transactionManager type="JDBC"></transactionManager>
            <!--数据源信息,POOLED:使用mybatis的连接池,因为既然使用框架
那么就需要非常好的优化,连接池就可以
            UNPOOLED:不使用连接池,直接创建一个连接,最后关闭
            -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>

            </dataSource>
            <!--上面决定了驱动,连接信息,即这些信息,给对应连接池操作,给出对应连接-->
        </environment>
    </environments>

    <!--引入映射配置文件-->
 <mappers>
<!--     <mapper resource="com/lagou/mapper/UserMapper.xml"></mapper>-->
        <!--不能有空值,即不能换行,且默认从项目路径开始找,即没有相对路径
所以当前配置文件在与这个配置文件一个包时
        也要从项目路径开始找,因为是引入的需要被加载,所以需要指定路径(因为加载都是从项目路径开始)-->
<!--下面两个是通过扫描类,来决定扫描配置文件-->
<!--     <mapper class="com.lagou.mapper.UserMapper"></mapper>-->

     <!--我们知道:使用resource时,对应配置文件的名称可以随便起,我们可以给他一个约束
     使得他的名称与对应的接口名称一致,会先去查找对应类所在包的同名xml文件
     那么我们就会通过加载同包路径下的对应同名xml文件,实现名称对应加载
     在框架里,对应不符合的基本都是报错,而不会有什么折中操作,特别是配置文件的位置和属性等
     程序里可能会有折中,如-1,0,null等等


     -->

     <!-- 但这里就与上面的别名差不多,当多个配置文件加载时,需要写多次
     于是也是类似与别名的加载
     也要注意:这里加载配置文件时,会有先后顺序的,就如过滤器一样,但这里只会加载一次
     基本是配置文件也就是加载一次的,而有些配置不会影响程序,这样的基本是不会有什么错误的
如别名,也只是提供别名而已
     因为配置文件也是如html一样从上到下,读取成功,基本上就不会去读取下一个,或者只会读取一次
     当这些都是与对应代码底层相关的,就比如加载,若有路径错的,加载必然会使得报错
     而正确的,会继续加载,但可能不同的加载方式会冲突的,即使得报错,如这里的三种,不能有不一样的
-->
     
     <!--即批量加载配置
要注意:这个批量,是对应上面的批量class的方式,所以对应配置名称要与对应接口名称一致
-->
     <package name="com.lagou.mapper"></package>
 </mappers>
</configuration>
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mybatis_db?characterEncoding=utf8
#如果是本机,localhost:3306可以省略,但后面的/并没有省略,所以上面需要是/mybatis_db
jdbc.username=root
jdbc.password=123456
package com.lagou.mapper;

import com.lagou.domain.User;

/**
 *
 */
public interface UserMapper {

    /*
    根据id查询用户
     */
    public User findUserById(int id);
}

package com.lagou.test;

import com.lagou.domain.User;
import com.lagou.mapper.UserMapper;
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 java.io.IOException;
import java.io.InputStream;

/**
 *
 */
public class MybatisText {

    /*
    Mybatis的dao层mapper代理方式测试
     */

  @Test
  public void test1() throws IOException {
      InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
      SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
      SqlSession sqlSession = build.openSession();
      //这里就不是使用方法来调用了,而是直接使用mapper代理对象,即Usermapper指向一个实现类
      //代理对象,可以看成,通过接口方法,即会传入对应的Class类,来进行解析
      //一般的我们是直接调用对应读取方法,传入自己写的参数,即进行sql的获得信息
      //这里我们通过解析对应接口方法,来获得对应的sql信息
      //方法类路径+方法名称=sql路径传参
      //parameterType=接口方法参数
      //而方法返回值,是当作这个方法的返回值的,返回的是resultType的类型,所以也要一致才行
       //实际上只需要方法类路径+方法名称一致就可以了,而之所以对应的resultType要一致
      //是因为最后我们还是需要调用接口方法,所以是需要一致的
      //而当返回null时,是不会进行转换的(Object转换),所以就可以出现了不一致(没有对应参数对应),你可以试验一下
      //当前返回的 其实是基于UserMapper所产生的代理对象:底层:JDK动态代理 实际类型:proxy
      UserMapper mapper = sqlSession.getMapper(UserMapper.class);
      //上面说明了对应参数,那么调用这个方法,会在对应实现类中,通过反射,进行参数传递
      //使得类似于实现类的操作(实际上就是帮我们创建一个类似实现类的对象)
      //这样就形成了我们只用接口来进行实现类的操作了,虽然解决了硬编码的问题,但重复问题还没有进行好的解决
      //实际上也是可以通过全局来操作的,后面知识中有更好的解决方式
      User userById = mapper.findUserById(6);
      System.out.println(userById);
  }

}



由此可知,反射的用处重大,在前面也说过,反射就是可以动态的创建对象,所以对于配置文件来说,反射有很大用处


在大多数的框架,基本会用到反射


Mybatis基于接口代理方式的内部执行原理:


我们的持久层现在只有一个接口,而接口是不实际干活的,那么是谁在做查询的实际工作呢?


下面通过追踪源码看一下:


通过追踪源码我们会发现,我们使用的mapper实际上是一个代理对象,是由MapperProxy代理产生的

在这里插入图片描述



追踪MapperProxy的invoke方法会发现,其最终调用了mapperMethod.execute(sqlSession, args)

在这里插入图片描述



进入execute方法会发现,最终工作的还是sqlSession

在这里插入图片描述



所以说,他封装了对应的操作,参数由反射得出的,对应的动态代理,所执行的方法,就算反射出来的默认执行方法


如下面的invoke方法,只要是通过反射出来的,调用对应方法都是这个invoke方法,这个方法传入对应方法名称,进行帮你调用

在这里插入图片描述



所以,调用的getMapper()方法,实际上最后还是调用了对应工厂对应自带的对应增删改查方法



版权声明:本文为qq_59609098原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。