# 配置文件

之前我们最开始的 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>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${驱动类(含包名)}"/>
        <property name="url" value="${数据库连接URL}"/>
        <property name="username" value="${用户名}"/>
        <property name="password" value="${密码}"/>
      </dataSource>
    </environment>
  </environments>
</configuration>

# 环境

可以看到有这样的标签:

<environments default="development">
    <environment id="development">
        <!-- .... -->
    </environment>
</environments>

其实可以写很多个 <environment> 标签,表示连接不同的数据库,因为实际开发中不可能只使用一个数据库。例如:同时支持 Oracle 和 MySQL 数据库。也有可能是为了分别定义开发环境、测试环境和生产环境。

你可以配置多种环境(environment),但是每个 SqlSessionFactory 实例只能选择一个环境

我们看一下之前为我们为了得到 SqlSession 做了些什么:

SqlSessionFactory factory = new 
    SqlSessionFactoryBuilder().
    build(Resources.getResourceAsReader("mybatis-config.xml"));

其实 SqlSessionFactoryBuilderbuild 可以接收配置环境的属性:

SqlSessionFactory factory = new 
    SqlSessionFactoryBuilder().
    build(Resources.getResourceAsReader("mybatis-config.xml"),"环境ID");

如果没有选择环境 ID,就使用默认的环境 ID,看最上面的 default = development

# typeAliases 标签

我们之前为了定义一个 mapper,需要关联到项目中的实体类,然而实体类需要使用全类限定名:

<mapper namespace="TestMapper">
    <select id="selectStudent" resultType="com.cyan.entity.Student">
        select * from student
    </select>
</mapper>

resultType 指定了 JavaBean 完全限定名,这个名称很长,每次都要完整的输入,很是麻烦。因此就出现了 <typeAliases> 标签,该标签用来将给定的实体定义别名。

<typeAliases>
	<typeAlias alias="Student" type="com.cyan.entity.Student" />
    <!-- 还可以定义很多 -->
    
    <!-- 如果觉得还是麻烦,直接让 Mybatis 去扫描一个包,并将包下的所有类自动起别名 -->
    <package name="com.cyan.entity"/>
</typeAliases>

你应该尽量避免不同包出现相同类名的类。

也可以指定实体类添加一个注解,指定别名:

@Alias("lbwnb")
public class Student {
    private int sid;
    private String name;
    private String sex;
}

# transactionManager 标签

翻译过来是事务管理器,我们先看一下之前是怎么写的:

<transactionManager type="JDBC"/>

在 MyBatis 中有两种事务管理器类型:

  • JDBC – 这个配置直接简单使用了 JDBC 的提交和回滚设置。它依赖于从数据源得到的连接来管理事务范围。
  • MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接。而它会让容器来管理事务的整个生命周期(比如 Spring 或 JEE 应用服务器的上下文)。默认情况下它会关闭连接。然而一些容器并不希望这样,因此如果你需要从连接中停止它,将 closeConnection 属性设置为 false。

还可以自定义事务管理器,JDBC 实现了 TransacFacotry,MANAGED 实现了 Transaction,所以我们自定义事务管理器需要实现其中一个接口。

TransacFacotry 源码:

public interface TransactionFactory {
 
	// 设置属性
	void setProperties(Properties props);
  	// 创建事务
	Transaction newTransaction(Connection conn);
   
  	Transaction newTransaction(DataSource dataSource, 
                             TransactionIsolationLevel level, boolean autoCommit);
}

Transaction 源码:

public interface Transaction {
	
    // 获取数据库连接
	Connection getConnection() throws SQLException;
    // 提交事务
    void commit() throws SQLException;
    // 回滚事务
    void rollback() throws SQLException;
	// 关闭数据库连接
    void close() throws SQLException;
}

假设为我们要实现的自定义事务管理器叫做 MyTransaction ,全类限定名是 com.utils.MyTransaction

那么需要在别名中声明:

<typeAliases>
	<typeAlia alias="my_transaction" type="com.utils.MyTransaction"/>
</typeAliases>

Mybatis 会通过 resolveAlias 方法去解析别名:

protected Class<?> resolveClass(String alias) {
    if(alias == null) return null;
    try {
        // 拿到类
        return resolveAlias(alias);
    } catch (Exception e) {
        throw new BuilderException("Error resolving class. Cause: " + e, e);
    }
}

现在看一下整个配置文件:

<?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>
        <typeAlia alias="my_transaction" type="com.utils.MyTransaction"/>
    </typeAliases>
    
    <environments default="development" >
        <environment id="development" >
            <transactionManager type="my_transaction" />
            <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-->
    </mappers>
</configuration>

自定义事务工厂类:

public class MyTransactionFactory implements TransactionFactory {
    private static final String NAME = MyTransactionFactory.class.getSimpleName();
 
    @Override
    public void setProperties(Properties props) {
        System.out.println(NAME + " setProperties()");
    }
 
    @Override
    public Transaction newTransaction(Connection conn) {
        System.out.println(NAME + " newTransaction()");
        return new MyTransaction(conn);
    }
 
    @Override
    public Transaction newTransaction(DataSource dataSource, T
                                      ransactionIsolationLevel level,
                                      boolean autoCommit) {
        System.out.println(NAME + " newTransaction()");
        try {
            return new MyTransaction(dataSource.getConnection());
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
}

自定义事务类:

public class MyTransaction implements Transaction {
    private static final String NAME = MyTransaction.class.getSimpleName();
    private Connection connection;
 
    public MyTransaction(Connection connection) {
        this.connection = connection;
    }
 
    @Override
    public Connection getConnection() throws SQLException {
        System.out.println(NAME + " getConnection()");
        return this.connection;
    }
 
    @Override
    public void commit() throws SQLException {
        System.out.println(NAME + " commit()");
        if(null != this.connection && !this.connection.getAutoCommit()) {
            this.connection.commit();
        }
    }
 
    @Override
    public void rollback() throws SQLException {
        System.out.println(NAME + " rollback()");
        if(null != this.connection && !this.connection.getAutoCommit()) {
            this.connection.rollback();
        }
    }
 
    @Override
    public void close() throws SQLException {
        System.out.println(NAME + " close()");
        if(null != this.connection) {
            this.connection.close();
        }
    }
}

就可以使用了。

# dataSource 标签

先看一下之前 dataSource 是什么:

<dataSource type="POOLED">
    <property name="driver" value="${驱动类(含包名)}"/>
    <property name="url" value="${数据库连接URL}"/>
    <property name="username" value="${用户名}"/>
    <property name="password" value="${密码}"/>
</dataSource>

<dataSource> 标签使用基本的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

type 可取的内置数据源有: UNPOOLEDPOOLEDJNDI

第二个是 web 常用的,不同的数据库对这个的表现也是不一样的,所以对某些数据库来说配置数据源并不重要,这个配置也是闲置的。

# 参考

https://www.hxstrive.com/subject/mybatis/245.htm

https://www.hxstrive.com/subject/mybatis/241.htm

https://www.hxstrive.com/subject/mybatis/246.htm

https://www.hxstrive.com/subject/mybatis/247.htm

https://www.yuque.com/qingkongxiaguang/javaweb/gn0syt#a28726e1