【MyBatis系列1】基础知识(下)

【MyBatis系列1】基础知识(下)

主要讲解 MyBatis 的基础知识,内容来源于《C 语言中文网》。

MyBatis示例

项目准备

DB使用的是Mysql,pom.xml需要添加的依赖包:

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>6.0.6</version>
</dependency>

DB结构:

CREATE TABLE `user_test` (
  `uid` tinyint(2) NOT NULL,
  `uname` varchar(20) DEFAULT NULL,
  `usex` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

DB初始数据:

uid uname usex
1 张三
2 陈恒
3 楼仔

具体示例

第一步:先创建持久化类:

@Data
public class MyUser {
    private Integer uid; // 主键
    private String uname;
    private String usex;
}

第二步:创建映射文件

<?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.dao.UserDao">
    <!-- 根据uid查询一个用户信息 -->
    <select id="selectUserById" parameterType="Integer" resultType="com.mybatis.entity.MyUser">
        select * from user_test where uid = #{uid}
    </select>
    <!-- 查询所有用户信息 -->
    <select id="selectAllUser" resultType="com.mybatis.entity.MyUser">
        select * from user_test
    </select>
    <!-- 添加一个用户,#{uname}为 com.mybatis.po.MyUser 的属性值 -->
    <insert id="addUser" parameterType="com.mybatis.entity.MyUser">
        insert into user_test (uid,uname,usex)
        values(#{uid},#{uname},#{usex})
    </insert>
    <!--修改一个用户 -->
    <update id="updateUser" parameterType="com.mybatis.entity.MyUser">
        update user_test set uname =#{uname},usex = #{usex} where uid = #{uid}
    </update>
    <!-- 删除一个用户 -->
    <delete id="deleteUser" parameterType="Integer">
        delete from user_test where uid= #{uid}
    </delete>
</mapper>

第三步:创建 MyBatis 的配置文件

<?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>
    <settings>
        <setting name="logImpl" value="LOG4J" />
        <setting name="cacheEnabled" value="false"/>
        <setting name="defaultExecutorType" value="REUSE"/>
        <setting name="useGeneratedKeys" value="true"/>
    </settings>

    <!-- 配置mybatis运行环境 -->
    <!-- 如果只启动mybatis,这里一定需要;如果通过spring使用mybatis,这里可以删掉,因为SQL的配置在src/main/resources/applicationContext.xml中 -->
    <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://xxx:xxx/xm_jointly?characterEncoding=utf8" />
                <property name="username" value="jointly_xxx" />
                <property name="password" value="G-uTlU-xxx" />
            </dataSource>
        </environment>
    </environments>
<!--     将mapper文件加入到配置文件中-->
    <mappers>
        <mapper resource="com/mybatis/mapper/UserMapper.xml" />
    </mappers>
</configuration>

第四步:创建测试类

public class MyBatisTest {
    public static void main(String[] args) {
        try {
            // 读取配置文件 mybatis-config.xml
            InputStream config = Resources.getResourceAsStream("com/mybatis/config/datasources/mybatis-config.xml");
            // 根据配置文件构建SqlSessionFactory
            SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);
            // 通过 SqlSessionFactory 创建 SqlSession
            SqlSession ss = ssf.openSession();

            // 查询一个用户
            MyUser mu = ss.selectOne("com.mybatis.dao.UserDao.selectUserById", 1);
            System.out.println(mu);

            // 修改一个用户
            MyUser updatemu = new MyUser();
            updatemu.setUid(1);
            updatemu.setUname("张三");
            updatemu.setUsex("女");
            ss.update("com.mybatis.dao.UserDao.updateUser", updatemu);

            // 查询所有用户
            List<MyUser> listMu = ss.selectList("com.mybatis.dao.UserDao.selectAllUser");
            for (MyUser myUser : listMu) {
                System.out.println(myUser);
            }
            // 提交事务
            ss.commit();
            // 关闭 SqlSession
            ss.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

输出:

MyUser(uid=1, uname=张三, usex=女)
MyUser(uid=1, uname=张三, usex=女)
MyUser(uid=2, uname=陈恒, usex=男)
MyUser(uid=3, uname=楼仔, usex=男)

项目整体结构:

【MyBatis系列1】基础知识(下)

配置文件详解

MyBatis 配置文件并不复杂,它所有的元素如下所示:

<?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 /><!-- 属性 -->
    <settings /><!-- 设置 -->
    <typeAliases /><!-- 类型命名 -->
    <typeHandlers /><!-- 类型处理器 -->
    <objectFactory /><!-- 对象工厂 -->
    <plugins /><!-- 插件 -->
    <environments><!-- 配置环境 -->
        <environment><!-- 环境变量 -->
            <transactionManager /><!-- 事务管理器 -->
            <dataSource /><!-- 数据源 -->
        </environment>
    </environments>
    <databaseIdProvider /><!-- 数据库厂商标识 -->
    <mappers /><!-- 映射器 -->
</configuration>

但是需要注意的是,MyBatis 配置项的顺序不能颠倒。如果颠倒了它们的顺序,那么在 MyBatis 启动阶段就会发生异常,导致程序无法运行。

properties

properties 属性可以给系统配置一些运行参数,可以放在 XML 文件或者 properties 文件中,而不是放在 Java 编码中,这样的好处在于方便参数修改,而不会引起代码的重新编译。一般而言,MyBatis 提供了 3 种方式让我们使用 properties,它们是property 子元素、properties 文件和程序代码传递,这里我们只讲“property 子元素”的方式。

以下面代码为基础,使用 property 子元素将数据库连接的相关配置进行改写,这里可以直接参考“MyBatis示例”中的XML配置即可。

这里使用了元素 “properties” 下的子元素 “property” 定义,用字符串 database.username 定义数据库用户名,然后就可以在数据库定义中引入这个已经定义好的属性参数,如 ${database.username},这样定义一次就可以到处引用了。但是如果属性参数有成百上千个,显然使用这样的方式不是一个很好的选择,这个时候可以使用 properties 文件。

properties 文件的方式,这个很简单,主要就是将Mysql的相关配置信息放入xx.properties的配置文件中,后面讲述MyBatis和Spring整合时再介绍。

settings

在 MyBatis 中 settings 是最复杂的配置,它能深刻影响 MyBatis 底层的运行,但是在大部分情况下使用默认值便可以运行,所以在大部分情况下不需要大量配置它,只需要修改一些常用的规则即可,比如自动映射、驼峰命名映射、级联规则、是否启动缓存、执行器(Executor)类型等。

【MyBatis系列1】基础知识(下)

【MyBatis系列1】基础知识(下)

settings 的配置项很多,但是真正用到的不会太多,我们把常用的配置项研究清楚就可以了,比如关于缓存的 cacheEnabled,关于级联的 lazyLoadingEnabled 和 aggressiveLazy Loading,关于自动映射的 autoMappingBehavior 和 mapUnderscoreToCamelCase,关于执行器类型的 defaultExecutorType 等。

东西太多,我才不会挨个看,就挑几个重点的看看就行。

typeAliases

由于类的全限定名称很长,需要大量使用的时候,总写那么长的名称不方便。在 MyBatis 中允许定义一个简写来代表这个类,这就是别名,别名分为系统定义别名和自定义别名。在 MyBatis 中别名由类 TypeAliasRegistry(org.apache.ibatis.type.TypeAliasRegistry)去定义。注意,在 MyBatis 中别名不区分大小写。

这块感觉用的不多,我就先不详细了解,等要用的时候,我再回来看看那。

typeHandlers

在 JDBC 中,需要在 PreparedStatement 对象中设置那些已经预编译过的 SQL 语句的参数。执行 SQL 后,会通过 ResultSet 对象获取得到数据库的数据,而这些 MyBatis 是根据数据的类型通过 typeHandler 来实现的。

在 typeHandler 中,分为 jdbcType 和 javaType,其中 jdbcType 用于定义数据库类型,而 javaType 用于定义 Java 类型,那么 typeHandler 的作用就是承担 jdbcType 和 javaType 之间的相互转换。在很多情况下我们并不需要去配置 typeHandler、jdbcType、javaType,因为 MyBatis 会探测应该使用什么类型的 typeHandler 进行处理,但是有些场景无法探测到。对于那些需要使用自定义枚举的场景,或者数据库使用特殊数据类型的场景,可以使用自定义的 typeHandler 去处理类型之间的转换问题。

系统提供的 typeHandler 能覆盖大部分场景的要求,但是有些情况下是不够的,比如我们有特殊的转换规则,枚举类就是这样。

同typeAliases,这块也用的不多,先不详细了解,留个记录,需要的话再回来看看。

objectFactory

当创建结果集时,MyBatis 会使用一个对象工厂来完成创建这个结果集实例。在默认的情况下,MyBatis 会使用其定义的对象工厂——DefaultObjectFactory(org.apache.ibatis.reflection.factory.DefaultObjectFactory)来完成对应的工作。

MyBatis 允许注册自定义的 ObjectFactory。如果自定义,则需要实现接口 org.apache.ibatis.reflection.factory.ObjectFactory,并给予配置。

在大部分的情况下,我们都不需要自定义返回规则,因为这些比较复杂而且容易出错,在更多的情况下,都会考虑继承系统已经实现好的 DefaultObjectFactory ,通过一定的改写来完成我们所需要的工作。

仅作了解即可,因为我们一般不会创建自己的“对象工厂”。

environments

在 MyBatis 中,运行环境主要的作用是配置数据库信息,它可以配置多个数据库,一般而言只需要配置其中的一个就可以了。它下面又分为两个可配置的元素:事务管理器(transactionManager)、数据源(dataSource),回归一下我们示例中的配置

<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://xxx:xxx/xm_jointly?characterEncoding=utf8" />
            <property name="username" value="jointly_xxx" />
            <property name="password" value="G-uTlU-xxx" />
        </dataSource>
    </environment>
</environments>

在实际的工作中,大部分情况下会采用 Spring 对数据源和数据库的事务进行管理,这个会在下一篇文章“Spring和MyBatis整合”进行讲解。

transactionManager

在 MyBatis 中,transactionManager 提供了两个实现类,它需要实现接口 Transaction(org.apache.ibatis.transaction.Transaction),它的定义代码如下所示。

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

从方法可知,它主要的工作就是提交(commit)、回滚(rollback)和关闭(close)数据库的事务。MyBatis 为 Transaction 提供了两个实现类:JdbcTransaction 和 ManagedTransaction,如图 1 所示。

【MyBatis系列1】基础知识(下)

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

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

这里做简要的说明:

  • JDBC 使用 JdbcTransactionFactory 生成的 JdbcTransaction 对象实现。它是以 JDBC 的方式对数据库的提交和回滚进行操作。

  • MANAGED 使用 ManagedTransactionFactory 生成的 ManagedTransaction 对象实现。它的提交和回滚方法不用任何操作,而是把事务交给容器处理。在默认情况下,它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。

当然,你也可以定义自己的transactionManager,这里就不展开了。

environment

environment 的主要作用是配置数据库,在 MyBatis 中,数据库通过 PooledDataSource Factory、UnpooledDataSourceFactory 和 JndiDataSourceFactory 三个工厂类来提供,前两者对应产生 PooledDataSource、UnpooledDataSource 类对象,而 JndiDataSourceFactory 则会根据 JNDI 的信息拿到外部容器实现的数据库连接对象。

无论如何这三个工厂类,最后生成的产品都会是一个实现了 DataSource 接口的数据库连接对象。由于存在三种数据源,所以可以按照下面的形式配置它们。

<dataSource type="UNPOOLED">
<dataSource type="POOLED">
<dataSource type="JNDI">

论述一下这三种数据源及其属性:

  • UNPOOLED:UNPOOLED 采用非数据库池的管理方式,每次请求都会打开一个新的数据库连接,所以创建会比较慢。在一些对性能没有很高要求的场合可以使用它。

  • POOLED:数据源 POOLED 利用“池”的概念将 JDBC 的 Connection 对象组织起来,它开始会有一些空置,并且已经连接好的数据库连接,所以请求时,无须再建立和验证,省去了创建新的连接实例时所必需的初始化和认证时间。它还控制最大连接数,避免过多的连接导致系统瓶颈。

  • JNDI:数据源 JNDI 的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。

欢迎大家多多点赞,更多文章,请关注微信公众号“楼仔进阶之路”,点关注,不迷路~~

【MyBatis系列1】基础知识(下)

上一篇:构造函数和原型对象


下一篇:资源配额