初识MyBatis——MyBatis基础

简介

MyBatis是一个比较优秀的、开源的数据持久层框架,它可以在实体类与SQL语句之间建立映射关系,替开发人员完成了JavaBean组件与数据库记录实体之间的转化,是一种半自动化的ORM实现。它内部封装了通过JDBC访问数据库的操作,支持普通SQL查询、存储过程和高级映射,几乎消除了所有的JDBC代码和参数的手工设置,以及结果集的检索。MyBatis作为持久层框架,其主要思想是将程序中大量的SQL语句分离出来,配置在配置文件中,通过读取配置文件执行相应的SQL语句。此框架性能可观,小巧易学,应用也比较广泛。

数据持久化和ORM

在学习MyBatis框架之前,要先弄清楚这两个概念,高手跳过。

1、数据持久化,是将内存中的数据模型转换为存储模型将存储模型转换为内存中的数据模型的统称。通俗点说,程序是运行在内存中的,程序中的数据也是存储在内存中的,如果不将数据写入磁盘,那么伴随着程序的结束这些数据也将消失。所以我们需要将数据写入到一个地方,存起来,以后需要这些数据的时候就去读取,数据库就是一种持久化的途径,将数据写入数据库表,要知道你的数据库、数据库表都是保存在磁盘上的。想一下,如果数据得不到持久化,那么我们每次访问一个程序,之前的信息都将无影无踪,没有购物车、没有好友列表、没有用户信息……。持久化不单单指往磁盘中“写”,从磁盘中“读”也属于持久化操作,比如文件的存储、数据的读取等。数据模型可以是任意数据结构或对象模型,存储模型可以是关系模型、XML、二进制流等。

2、ORM,全称是Object/Relational Mapping,即对象/关系映射,是一种数据持久化的技术。它在对象模型和关系型数据库之间建立对应关系,通过JavaBean对象去操作数据库表中的数据。按照一般的开发习惯,数据库每一张表都对应一个实体类对象,比如一张学生表,我们最好是创建一个实体类Student关联这张表,数据库表中有哪些字段,Student实体类中就有哪些属性,比如ID、Name、Address等等,这样我们进行数据传递的时候,将数据封装成JavaBean对象进行传递是很有必要的,毕竟只要是有思想的程序员就不会让一个方法体传递n多个参数。那么问题来了,一个实体类在内存中表现为对象,在数据库中表现为关系数据,而目前数据库还没有“智能”到直接解析表达内存中的对象,所以我们使用ORM以一个中间件的形式完成程序中的对象到数据库中的关系型数据的映射。关于ORM的详细内容,这里不再大篇幅的叙述,列位可以去查看资料去学习了解。

MyBatis就是一种ORM解决方案,基于ORM在对象和关系型数据库的表之间建立一座桥梁,通过建立SQL关系映射,便捷地实现数据的增删改查。

MyBatis的三个基本元素

一、核心接口和类

这里只列出使用MyBatis时显示调用的三个接口/类,MyBatis中还有一些很重要的接口/类组成,只不过那些一般都通过配置文件隐式地调用执行,想具体了解的朋友可以去看MyBatis的开发手册。

 每一个MyBatis应用程序都以一个SqlSessionFactory对象的实例为核心。首先根据XML配置文件或者Configuration类的实例获取SqlSessionFactoryBuilder对象,然后通过该对象的build()方法获取一个SqlSessionFactory对象,有了SqlSessionFactory对象后,进而获取SqlSession对象,SqlSession对象中完全包含以数据库为背景的所有执行SQL语句的方法,由SqlSession对象直接执行已映射的SQL语句。

1、SqlSessionFactoryBuilder的作用及其生命周期、最佳作用域

SqlSessionFactoryBuilder对象负责构建SqlSessionFactory的实例,提供了多个build()方法的重载(这里不一一列出,可自行查阅API),通过源码分析,可以发现都是在调用同一签名方法:build(InputStream inputStream, String environment, Properties properties)。由于参数environment和properties都可以置为null,那么去除重复的重载方法,其实就以下三种:

build(InputStream inputStream, String environment, Properties properties)

build(Reader reader, String environment, Properties properties)

build(Configuration config)

不难发现,我们可以用三种形式提供给build()方法创建SqlSessionFactory对象,分别是InputStream 字节流、Reader 字符流、Configuration 类,由于字符流、字节流都读取了配置文件,所以总的来说构建一个SqlSessionFactory对象有两种方式:读取XML配置文件和传入Configuration 对象。

SqlSessionFactoryBuilder的最大特点是“用过即丢”,一旦构建完SqlSessionFactory对象,它就可以销毁了,所以它应该以局部变量存在于方法体内。

2、SqlSessionFactory的作用及其生命周期、最佳作用域

SqlSessionFactory简单的理解就是创建SqlSession实例的工厂,所有的MyBatis应用都是以它为中心的。有了它之后,通过openSession()方法获取SqlSession实例。openSession()方法也有好几个重载,最常用的还是传入boolean类型的参数值,若传入true表示关闭事务控制,自动提交;传入false表示开启事务控制;默认为true,自动提交。

SqlSessionFactory对象一旦创建,它应该在整个应用运行过程中始终存在,不建议多次创建SqlSessionFactory对象,因为每次创建它都需要读一次XML配置文件,哪怕你是通过传入Configuration对象的方式构建,都是昂贵的开销。因此SqlSessionFactory对象的作用域应该是全局共享,即随着应用的生命周期一同存在,且只存在一个,与Servlet实例类似,都是单例模式。一般的方案是写一个专门管理SqlSession的类,在此类中将SqlSessionFactory对象设置为公用的、静态的,在静态块中构建它。当然最佳的方案是使用依赖注入容器——Spring框架来管理SqlSessionFactory的单例生命周期,这是后话了。

3、SqlSession的作用及其生命周期、最佳作用域

SqlSession是用于执行持久化操作的对象,可以看作JDBC中的Connection来理解,它提供了面向数据库执行SQL命令所需的所有方法,通过它的实例可以运行已映射的SQL语句。

 这里要说一下使用SqlSession的两种使用方式:

第一种,直接执行已映射的SQL语句;

第二种,基于mapper接口方式操作数据,要求映射文件中的mapper标签的namespace属性值要与业务接口的包路径一致,SQL块的id要与接口中定义的方法名一致。感觉模糊?稍后演示一遍就明白了!

一个SqlSession对应一次数据库会话,数据库会话并不是永久的,因此SqlSession也不应该是永久的。相反,应该每一次访问数据库就创建一个SqlSession实例(并不是说每执行一次SQL语句就创建一个,它可以执行多次SQL语句,只不过将它关闭之后就要重新创建),在一系列操作之后(任意次数的增删改查,根据业务需求而定)再将它关闭。每个线程都应该有自己的SqlSession实例,不应该被共享,因此它的最佳作用域应该是方法体内。

二、核心配置文件

该文件配置了MyBatis的一些全局信息,包含数据库连接信息和MyBatis运行时所需的各种特性,以及设置和影响MyBatis行为的一些属性,我们一般命名为mybatis-config.xml。

mybatis-config.xml文件需配置一些基本元素(必须依靠先后顺序),层次结构如下:

 可看出config元素是整个配置文件的根节点,所有的配置信息都放在它里面。接下来介绍一下以上元素中比较重要的。

1、properties元素

此元素一般用来描述数据库连接配置信息,有两种描述方式:

     (1)指定resource属性指向.properties文件

     (2)直接配置在此元素节点之间

 注意:如果在properties标签中同时使用了两种方式,resource属性值的优先级高于子节点配置的属性值。因为properties的子节点是先被读取的,而后才指向.properties文件,如果出现了相同的key(即name),后者会覆盖前者的属性值。

2、settings元素

settings元素的作用是设置一些非常重要的设置选项,用于设置和改变MyBatis运行中的行为,比如log日志的记录。这一部分不过多的叙述,东西比较碎而且都是那种看一眼就知道怎么用的东西,具体的可参考MyBatis的开发手册进行学习。

3、typeAliases元素

此元素的作用是配置类型别名,通过与MyBatis的SQL映射文件相关联,减少输入冗长的完整类名,简化操作。

 其中type是要简化的类的包路径,alias是别名。

这时候问题来了,如果有好多个实体类,岂不是要写好些个<typeAlias>标签?所以,我们还可以通过package标签的name属性直接指定一个包名,MyBatis会自动扫描该包下的JavaBean实体类,这样一来,将所有的实体类放到一个包下,岂不是用的时候直接写类名、可以省略冗长的包路径了?它到底用在哪,待会来一个示例就明白了。

 4、environments元素

MyBatis可以配置多套运行环境,如开发环境、测试环境、生产环境等,或者连接不同的数据库。environments元素就是灵活地配置运行环境的,它可以有多个子节点environment,每一个子节点都有一个唯一的id,作为本配置文件中某运行环境的唯一标识。你可以在第一个environment节点配置一个连接MySQL数据库的运行环境,在第二个配置连接其他服务器的MySQL数据库,或者连接SQL Server,等等。但是environments元素必须指定一个默认的运行环境,通过指定default属性值实现,因为每个数据库都对应一个SqlSessionFactory实例,需要指明默认创建哪个运行环境,然后把其中的参数传给SqlSessionFactoryBuilder。

每一个environment节点又有一个transacyionManager自闭和标签、一个dataSource标签。

transacyionManager是事务管理器,设置为JDBC(MyBatis有两种事务管理类型,JDBC和MANAGED),表示直接使用JDBC的提交和回滚功能。

dataSource元素使用标准的JDBC数据源接口来配置JDBC连接对象的资源,其通过读取properties元素中配置的信息,获取要连接的对象的信息。MyBatis提供了三种数据源类型:UNPOOLED、POOLED、JNDI,一般使用POOLED,该类型的实现利用池的概念将JDBC连接对象组织起来,避免了创建新连接时所必须的初始化和认证时间,是MyBatis实现的简单的数据库连接池类型,使连接可复用,不必在每次请求时都去创建一个连接。

5 、mappers元素

mappers映射器,用来定义SQL的映射语句,我们只需要告诉MyBatis去哪里找到这些SQL语句就好,即去哪里找相应的SQL映射文件。可以使用类资源路径和URL两种方式:

 三、SQL映射文件

MyBatis真正的强大之处,莫过于SQL映射语句。相比于其他,SQL映射文件倒是显得很简单

1、mapper

映射元素的根节点,只有一个namespace(命名空间)属性,用于区分不同的mapper、绑定DAO接口,具体作用在示例中展示。

2、cache

配置给定命名空间的缓存

3、cache-ref

从其他命名空间引用缓存设置

4、resultMap

用来描述数据库结果集和对象的对应关系

5、sql

可以重用的SQL块,也可以被其他语句引用

6、insert、delete、update、select

分别映射增删改查语句。此类标签都有一个id属性,区分不同的映射语句,不同的mapper可以重名,同一个mapper不可以。

下面贴一张最简单的配置

=========================================MyBatis示例=============================================

 

 新建Web项目,结构如下

 data.properties内容:

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:端口号/数据库名?useSSL=true&serverTimezone=UTC
username=用户名
password=密码

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>
    <!--加载资源文件-->
    <properties resource="data.properties"></properties>

    <!--settings配置LOG4J输出日志 -->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    <!--typeAliases配置类、包的别名-->
    <typeAliases>
        <!--<typeAlias type="Entity.User" alias="user"></typeAlias>-->
        <package name="Entity" />
    </typeAliases>

    <!--environments配置了数据库连接,配置了driver、url、username、password属性-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC">
                <property name="" value="" />
            </transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}" />
                <property name="url" value="${url}" />
                <property name="username" value="${username}" />
                <property name="password" value="${password}" />
            </dataSource>
        </environment>
    </environments>
    <!--配置一个SQL语句和映射的配置文件-->
    <mappers>
        <mapper resource="DaoImpl/ProviderMapper.xml" />
    </mappers>
</configuration>

接口ProviderMapper中的内容:

package Dao;

import Entity.Provider;

import java.util.List;

public interface ProviderMapper {
    public List<Provider> getAllProvider();
}

映射文件ProviderMapper.xml中的内容:

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="Dao.ProviderMapper"> <!--注意namespace的值要跟包路径(类资源路径)一致!-->
    <select id="getAllProvider" resultType="Provider">  <!--查询的返回值类型,因为我们在核心配置文件指向了包名,所以这里直接写实体类的名字就行了-->
        SELECT * FROM ProviderInfo
    </select>
</mapper>

MyBatisUtil.java中的内容:

package Data;

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

/**
 *  SqlSession管理类
 * */
public class MyBatisUtil {
    private static SqlSessionFactory factory;
    static {
        try {
            InputStream stream= Resources.getResourceAsStream("mybatis-config.xml");
            factory=new SqlSessionFactoryBuilder().build(stream);  //创建SqlSessionFactory对象
        }catch (Exception e){

        }
    }

    /**
     *  获取SqlSession连接
     * */
    public static SqlSession getSqlSession(){
        return factory.openSession(false);  //开启事务控制
    }

    /**
     *  关闭SqlSession连接
     * */
    public static void closeSqlSession(SqlSession sqlSession){
        if (sqlSession!=null){
            sqlSession.close();
        }
    }
}

实体类中的内容:

package Entity;

public class Provider {
    public int getId() {
        return id;
    }

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

    public String getProCode() {
        return proCode;
    }

    public void setProCode(String proCode) {
        this.proCode = proCode;
    }

    public String getProName() {
        return proName;
    }

    public void setProName(String proName) {
        this.proName = proName;
    }

    public String getProDesc() {
        return proDesc;
    }

    public void setProDesc(String proDesc) {
        this.proDesc = proDesc;
    }

    public String getProContact() {
        return proContact;
    }

    public void setProContact(String proContact) {
        this.proContact = proContact;
    }

    public String getProPhone() {
        return proPhone;
    }

    public void setProPhone(String proPhone) {
        this.proPhone = proPhone;
    }

    public String getProAddress() {
        return proAddress;
    }

    public void setProAddress(String proAddress) {
        this.proAddress = proAddress;
    }

    public String getProFax() {
        return proFax;
    }

    public void setProFax(String proFax) {
        this.proFax = proFax;
    }

    public int getCreateBy() {
        return createBy;
    }

    public void setCreateBy(int createBy) {
        this.createBy = createBy;
    }

    public String getCreateTime() {
        return createTime;
    }

    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }

    public int getModifyBy() {
        return modifyBy;
    }

    public void setModifyBy(int modifyBy) {
        this.modifyBy = modifyBy;
    }

    public String getModifyTime() {
        return modifyTime;
    }

    public void setModifyTime(String modifyTime) {
        this.modifyTime = modifyTime;
    }

    private int id;  //供应商ID
    private String proCode;  //供应商编码
    private String proName;  //供应商名称
    private String proDesc;  //供应商描述
    private String proContact;  //供应商联系人
    private String proPhone;  //供应商电话
    private String proAddress;  //供应商地址
    private String proFax;  //传真
    private int createBy;  //创建者
    private String createTime;  //创建时间
    private int modifyBy;  //修改者
    private String modifyTime;  //修改时间
}

测试类ProviderService中的内容:

package Servlet;

import Dao.ProviderMapper;
import Data.MyBatisUtil;
import Entity.Provider;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class ProviderService {
    public static void main(String[] args) {
        SqlSession sqlSession= MyBatisUtil.getSqlSession();  //获取连接
        //使用调用mapper接口的方式
        List<Provider> providerList=sqlSession.getMapper(ProviderMapper.class).getAllProvider();  //调用MyBatis,执行查询,指定接口名.class
        for (int i = 0; i <providerList.size() ; i++) {
            System.out.println("供应商姓名:"+providerList.get(i).getProName());
            System.out.println("编码:"+providerList.get(i).getProCode());
            System.out.println("描述:"+providerList.get(i).getProDesc());
            System.out.println("联系人:"+providerList.get(i).getProContact());
            System.out.println("电话:"+providerList.get(i).getProPhone());
            System.out.println("地址:"+providerList.get(i).getProAddress());
            System.out.println("传真:"+providerList.get(i).getProFax());
            System.out.println("创建者ID:"+providerList.get(i).getCreateBy());
            System.out.println("创建时间:"+providerList.get(i).getCreateTime());
            System.out.println("更新者ID:"+providerList.get(i).getModifyBy());
            System.out.println("更新时间:"+providerList.get(i).getModifyTime());
            System.out.println();
            System.out.println();
        }
    }
}

运行结果:

猜你喜欢

转载自blog.csdn.net/wzy18210825916/article/details/81805342