我的Mybatis学习之路

726 阅读11分钟

mybais概述

  • MyBatis 本是 Apache 的一个开源项目——iBatis,2010 年这个项目由 Apache Software Foundation 迁移到了 Google Code,并且改名为 MyBatis。
  • Mybatis是一个基于Java的持久层框架,Mybatis提供的持久层框架包括SQL Maps和Data Access Object(DAO),它消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索
  • Mybatis使用简单的XML或注解用于配置和原始映射,将接口和Java的POJOs(Plain Old Java Objects,普通的 Java 对象)应设为数据库中的记录
  • 目前,Java的持久层框架产品有许多,常见的有Hibernate和Mybatis

Mybatis和Hibernate的区别

  • sql优化方面: Hibernate不需要编写大量的SQL,就可以完全映射,提供了日志、缓存、级联(级联逼Mybatis强大)等特性,此外还提供了HQL对POJO进行操作,但是会消耗很多性能 Mysql手动编写SQL,支持动态SQL、处理列表、动态生成表名、支持存储过程,工作量较大
  • 开发方面 Mybatis是一个半自动映射的框架,因为Mybatis需要手动匹配POJO、SQL和映射关系 Hibernate是一个全表映射的框架,只需要提供POJO和映射关系即可
  • Hibernate优势 Hibernate的DAO层开发比Mybatis简单,Mybatis需要维护SQL和结果映射 Hibernate对对象的维护和缓存要比Mybatis好,对增删改查的对象的维护要方便 Hibernate有更好的数据库移植性,Mybatis的数据库移植性不好,不同的数据库要写不同的SQL Hibernate有更好的二级缓存机制,可以使用第三方缓存,Mybatis本身提供的缓存机制不佳
  • Mybatis优势 Mybatis可以进行更为细致的SQL优化,可以减少查询字段 Mybatis更容易掌握,而Hibernate的门槛较高

Mybatis的工作原理

  • 读取Mybatis配置文件,sqlmapconfig.xml为Mybatis的全局配置文件,配置了Mybatis的运行环境等信息,例如数据库连接信息。
  • 加载映射文件,映射文件即SQL文件,该文件中配置了操作数据库的SQL语句,需要在Mybatis配置文件中加载,sqlmapconfig.xml文件可以加载多个映射文件,每个文件对应数据库中的一张表。
  • 构造会话工厂,通过Mybatis的环境等配置信息构建会话工厂SqlSessionFactory。
  • 创建会话对象,由会话工厂创建SqlSession对象,该对象包含了执行SQL语句的所有方法。
  • Executor执行器:Mybatis底层定义了一个Executor接口来操作数据库,它根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。
  • MappedStatement对象:在Executor接口的执行方法中有一个MapperedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。
  • 输入参数映射,输入参数类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型,输入参数映射过程类似于JDBC对preparedStatement对象设置参数的过程。
  • 输出结果映射:输出结果类型可以是Map、List等类型,也可以是基本数据类型和POJO类型,输出结果映射过程类似于JDBC对结果集的解析过程。

Mybatis的核心组件

  • SqlSessionFactoryBuilder(构造器):会根据配置或者代码来生成SqlSessionFactory,采用的是分布构建的Builder模式。
  • SqlSessionFactory(工厂接口):依靠它生成SqlSession,使用的是工厂模式。
  • SqlSession(会话):一个既可以发送SQL执行返回结果,也可以获取Mapper的接口。
  • Sql Mapper(映射器):由一个Java接口和XML文件组成,需要给出对应的SQL和映射规则,负责发送SQL去执行并返回结果。

Mybatis SqlSessionFactory

使用Mybatis首先是使用配置或者代码生成SqlSessionFactory,而Mybatis提供了构造器SqlSessionFactoryBuilder。Mybatis提供了Configuration类,采用的Builder模式,具体的创建过程在Configuration类中完成。

SqlSessionFactory概述

SqlSessionFactory是一个接口,存在两个实现类:SqlSessionManager、DefaultSqlSessionFactory,一般而言,具体是有DefaultSqlSessionFactory实现的,而SqlSessionManager使用在多线程环境中,它的具体实现依靠DefaultSqlSessionFactory,关系如图。

SqlSessionFactory的创建

  • 读取XML配置文件创建 使用XML配置文件创建SqlSessionFactory首先需要配置,在配置完成后,Mybatis会读取配置文件,通过Configuration类对象构建整个Mybatis的上下文。
//创建sqlmapconfig。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>
    <typeAliases><!--别名-->
        <typeAliases alias="user" type="com.mybatis.po.User"/>
    </typeAliases>
    <!-- 数据库环境 -->
    <environments default="development">
        <environment id="development">
            <!-- 使用JDBC的事务管理 -->
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <!-- MySQL数据库驱动 -->
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <!-- 连接数据库的URL -->
                <property name="url"
                    value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8" />
                <property name="username" value="root" />
                <property name="password" value="1128" />
            </dataSource>
        </environment>
    </environments>
    <!-- 将mapper文件加入到配置文件中 -->
    <mappers>
        <mapper resource="com/mybatis/mapper/UserMapper.xml" />
    </mappers>
</configuration>
//创建测试类
public class MybatisTest {
    SqlSessionFactory factory = null;
    @Before
    public void test1() throws IOException {
        InputStream is = Resources.getResourceAsStream("sqlmapconfig.xml");
        factory = new SqlSessionFactoryBuilder().build(is);
        System.out.println(factory);
    }
}

首先读取sqlmapconfig.xml,然后通过SqlSessionFactory的Builder方法去创建SqlSessionFactory,采用XMl创建的形式,信息在配置文件中,有利于我们日后的维护和修改,避免了重新编译代码。

使用代码创建SqlSessionFactory

PooledDataSource dataSource = new PooledDataSource();
dataSource.setDriver("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword ("1128");
dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis");
dataSource.setDefeultAutoCommit(false);
// 采用 MyBatis 的 JDBC 事务方式
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment ("development", transactionFactory, dataSource);
// 创建 Configuration 对象
Configuration configuration = new Configuration(environment);
// 注册一个 MyBatis 上下文别名
configuration.getTypeAliasRegistry().registerAlias("role", Role.class);
// 加入一个映射器
configuration.addMapper(RoleMapper.class);
//使用 SqlSessionFactoryBuilder 构建 SqlSessionFactory
SqlSessionFactory SqlSessionFactory =
new SqlSessionFactoryBuilder().build(configuration);
return SqlSessionFactory;

使用代码创建SqlSessionFactory和使用XML文件创建原理一致,但是代码冗余,如果发生系统修改,有可能需要重新编译代码才能继续,所以推荐使用读取XML文件的方式创建SqlSessionFactory

SqlSession

SqlSession概述

SqlSession是其核心接口,在Mybatis中有两个实现类,DefaultSqlSession和和SqlSessionManager

  • DefaultSqlSession是单线程使用的,而SqlSessionManager在多线程环境下使用,SqlSession的作用类似于一个JDBC中的Connection对象,代表一个连接资源的启动,具体来说有三个功能: 1):获取Mapper接口 2):发送SQL给数据库 3):控制数据库事务
  • SqlSession的创建 SqlSession sqlSession = SqlSessionfactory.openSession();
  • SqlSession控制数据库事务的方法
//定义 SqlSession
SqlSession sqlSession = null;
try {
    // 打开 SqlSession 会话
    sqlSession = SqlSessionFactory.openSession();
    // some code...
    sqlSession.commit();    // 提交事务
} catch (IOException e) {
    sqlSession.rollback();  // 回滚事务
}finally{
    // 在 finally 语句中确保资源被顺利关闭
    if(sqlSession != null){
        sqlSession.close();
    }
}

这里使用commit方法提交事务,或者使用rollback方法回滚事务。因为它代表一个数据库的链接资源,使用后要及时关闭,若不关闭,数据库的连接资源就会很快被耗光,整个系统会陷入瘫痪,所以用finally语句保证sqlSession顺利关闭。

Mybatis映射器

  • 映射器是Mybatis中最重要、最复杂的组件,由一个接口和对应的XML文件(或注解)组成,可以配置以下内容: 1):描述映射规则 2):提供SQL语句,并可以配置SQL参数类型、返回类型、缓存刷新等信息 3):配置缓存 4):提供动态SQL

使用XML文件实现映射器

//首先创建role表
CREATE TABLE `role` (
    `id` bigint(20) NOT NULL,
    `role_name` varchar(20) DEFAULT NULL,
    `note` varchar(20) DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
//定义一个POJO文件
public class Role {
    private Long id;
    private String roleName;
    private String note;
/**setter and getter**/
}
//定义映射器接口
public interface RoleMapper {
    public Role getRole(Long id);
}
//在sqlmapconfig.xml配置文件中添加,它的作用就是引入一个XML文件
<mapper resource="com.mybatis.mapper.RoleMapper.xml" />
//创建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.mybatis.mapper.RoleMapper">
    <select id="getRole" parameterType="long" resultType="role">
        SELECT id,role_name as roleName,note FROM role WHERE id =#{id}
    </select>
</mapper>

通过以上代码,映射器就配置完成了

Mybatis执行SQL语句

使用SqlSession执行
//创建测试方法
    @Test
    public void test() {
        SqlSession sqlSession = factory.openSession();
        Role role = sqlSession.selectOne("com.mybatis.mapper.RoleMapper.getRole", 1L);
        Role role1 = sqlSession.selectOne("getRole", 1L);
        System.out.println(role);
        sqlSession.close();
    }
使用Mapper接口执行SQL
//创建测试方法
    @Test
    public void test() {
        SqlSession sqlSession = factory.openSession();
        RoleMapper mapper = sqlSession.getMapper(RoleMapper.class);
        System.out.println(mapper.getRole(1L));
        sqlSession.close();
    }
sqlSession执行SQL和Mapper执行SQL的区别
  • 使用Mapper接口编程可以消除SqlSession带来的功能性代码,提高可读性,而SqlSession发送SQL需要一个SQL id,比较晦涩难懂。

Mybatis核心组件的作用与和生命周期

SqlSessionFactoryBuilder

SqlSessionFactoryBuilder的作用在于创建SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder就失去了作用,所以它只能存在于创建SqlSessionFactory的方法中,而不要让其长期存在,所以SqlSessionFactoryBuilder实例的最佳作用域是方法作用域(局部方法变量)。

SqlSessionFactory

SqlSessionFactory可以被认为是一个数据库连接池,作用是创建SqlSession接口对象,因为Mybatis的本质就是对数据库的操作,所以SqlSessionFactory的生命周期存在于整个Mybatis的应用中,所以一旦创建了SqlSessionFactory,就要长期保存它,直至不再使用Mybatis应用,所以可以认为SqlSessionFactory的生命周期等同于Mybatis的生命周期。

SqlSession

  • 如果说SqlSessionFactory相当于数据库连接池,那么SqlSession就相当于一个数据库的连接(Connection对象),你可以在一个事务中执行多条SQL语句,然后通过commit、rollback等方法,提交或者回滚事务。
  • 所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try...catch...finally... 语句来保证其正确关闭
  • 所以 SqlSession 的最佳的作用域是请求或方法作用域

Mapper

Mapper是一个接口,它由SqlSession所创建,所以它的最大生命周期至多和SqlSession保持一致,尽管它很好用,但是由于SqlSession的关闭,它的数据库连接资源也会消失,所以它的生命周期应该小于等于SqlSession的生命周期。Mapper代表的是一个请求中的业务处理,所以它应该在一个请求中,一旦处理完了相关的业务,就应该废弃它。

Mybatis ObjectFactory(对象工厂)

  • 当创建结果集时,Mybatis会使用一个对象工厂来完成创建这个结果集实例,默认情况下,Mybatis会使用其定义的对象工厂(DefaultObjectFactory)来完成对应的工作。
  • Mybatis允许注册自定义的ObjectFactory,如果自定义,则需要实现ObjectFactory接口,并实现其中的方法。

Mybatis事务管理器——transactionManager

在Mybatis中,transactionManager提供了两个实现类,它们需要实现Transaction接口

public interface Transaction {
    Connection getConnection() throws SQLException;
    void commit() throws SQLException;
    void rollback() throws SQLException;
    void close() throws SQLException;
    Integer getTimeout() throws SQLException;
}

Mybatis为Transaction提供了两个实现类,分别为JdbcTransaction和ManagedTransaction,如图:

这两个实现类对应了两种工厂:JdbcTransactionFactory和ManagedTransactionFactory,这两个工厂需要实现TransactionFactory接口,通过它们会生成对应的Transaction对象,于是可以把事务管理通过配置文件的方式管理:

<transactionManager type="JDBC"/>
<transactionManager type="MANAGED"/>

JDBC使用JdbcTransactionFactory生成的JdbcTransaction对象实现,是以JDBC的方式对数据库的提交和回滚进行操作,MANAGED使用ManagedTransactionFactory生成的ManagedTransaction对象实现,它的提交和回滚方式不用任何操作,而是把事务交给容器处理。

environment数据源环境

environment的主要作用是配置数据库,在Mybatis中数据库通过PooledDataSourceFactory、UnPolledDataSourceFactory和JndiDataSourceFactory三个工厂类来提供,前两者对应产生PooledDataSource、UnpolledDataSource类对象,而JndiDataSourceFactory则会根据JNDI的信息拿到外部容器实现的数据库连接对象,无论选择那种工厂,最后都会生成一个实现了DataSource接口的数据库连接对象。 由于存在三种数据源,所以可以按照下面的形式配置

<dataSource type="UNPOOLED">
<dataSource type="POOLED">
<dataSource type="JNDI">
  • UNPOLLED:UNPOLLED采用非数据库池的管理方式,每次请求都会打开一个新的数据库连接,所以创建比较慢
  • POLLED:数据源POLLED利用"池"的概念将JDBC的Connection对象组织起来,它开始会有一些空置并且已经连接好的数据库连接,所以在请求时,无需再建立和验证,省去了创建新的连接实例时所必须的初始化和认证时间,还控制了最大连接数,避免了过多的连接导致系统瓶颈。
  • JNDI:数据源JNDI的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JDNI上下文的引用。 使用JDNI需要配置两个属性 initial_context:用来在InitialContext中寻找上下文,initial_context是可选属性,如果忽略,data_source属性将会直接从InitialContext中寻找。 data_source:是引用数据源实例位置上下文的路径,当提供initial_context配置时,data_source会在其返回的上下文中查找,当没有提供initial_context时,data_source直接在InitialContext中查找。