MyBatis配置

本文最后更新于:3 年前

引言

在现代应用开发中,数据库操作和管理的效率至关重要。MyBatis作为一个半ORM(对象关系映射)框架,提供了灵活的配置选项,以支持不同的日志实现、连接池管理以及事务处理。本文将详细探讨MyBatis的关键配置项,包括如何配置日志来监控SQL执行,数据源的不同配置选项以及事务管理的策略,以帮助开发者最大化地利用MyBatis来构建高效、可维护的数据库应用。

日志

MyBatis提供了灵活的日志管理功能,允许开发者在应用中监控和记录SQL语句的执行情况,这对于调试和优化数据库操作是非常有帮助的。通过配置MyBatis使用特定的日志实现,开发者可以获得执行的SQL语句、参数以及与数据库交互的详细信息。

日志框架

常见的日志组件包括

  • **SLF4J (Simple Logging Facade for Java)**:作为一个日志门面,SLF4J允许开发者在后端使用不同的日志框架(如Logbacklog4j),而不需要修改源代码。
  • Log4j:这是一个非常流行的日志框架,提供了强大的日志管理和配置功能。
  • Log4j2Log4j的升级版,提供了更好的性能和更多的功能。
  • JUL (Java Util Logging):Java平台的内置日志工具,无需额外依赖。
  • JCL (Jakarta Commons Logging):Apache的日志门面,类似于SLF4J
  • Apache Commons Logging:提供日志功能的另一个门面。
  • STDOUTNO_LOGGING:分别代表标准输出和不记录任何日志。

作用

  1. 调试:在开发过程中,可以通过查看日志快速定位问题,了解SQL语句的执行情况和潜在的错误。
  2. 性能优化:通过分析日志中的SQL执行时间和频率,可以识别性能瓶颈,优化查询语句或数据库结构。
  3. 审计和监控:日志可以帮助跟踪系统的活动和数据访问,对于维护数据安全和合规性非常重要。
  4. 透明性:日志提供了数据库操作的透明记录,使得系统的数据库交互更加清晰可追踪。

开启

  1. 在全局配置文件mybatis-config.xmlsettings标签中加入logImpl的配置项

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <configuration>

    <settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <.../>
    <.../>
    </configuration>
  2. 设置为以后,运行测试程序,控制台输出日志如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
    PooledDataSource forcefully closed/removed all connections.
    PooledDataSource forcefully closed/removed all connections.
    PooledDataSource forcefully closed/removed all connections.
    PooledDataSource forcefully closed/removed all connections.
    Opening JDBC Connection
    Created connection 2020152163.
    Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@78691363]
    ==> Preparing: insert into tt_vehicle (product_name, vin, production_date, guide_price) values ("Audi A6L", "LVIN1", "2024-06-22", "400000.12")
    ==> Parameters:
    <== Updates: 1
    Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@78691363]

    在日志信息中可以看到开启JDBC链接、执行SQL、更新SQL条数、提交以及关闭JDBC连接等信息。

集成logback

logback作为经常使用的日志框架,也能集成到MyBatis中,步骤如下:

  1. 引入logback相关依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.13</version>
    </dependency>
  2. 修改全局配置文件mybatis-config.xml,将日志的实现类修改为SLF4J

    1
    2
    3
    <settings>
    <setting name="logImpl" value="SLF4J"/>
    </settings>
  3. 在类路径中创建logback的配置文件logback.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    <?xml version="1.0" encoding="UTF-8"?>

    <configuration debug="false">
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
    <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
    <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
    </encoder>
    </appender>
    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <!--日志文件输出的文件名,其中LOG_HOME需要配置为相对或绝对路径-->
    <FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern>
    <!--日志文件保留天数-->
    <MaxHistory>30</MaxHistory>
    </rollingPolicy>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
    <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
    <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
    </encoder>
    <!--日志文件最大的大小-->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
    <MaxFileSize>100MB</MaxFileSize>
    </triggeringPolicy>
    </appender>

    <!--mybatis日志配置-->
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>

    <!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR -->
    <root level="DEBUG">
    <appender-ref ref="STDOUT"/>
    <appender-ref ref="FILE"/>
    </root>

    </configuration>
  4. 再次运行程序,控制台输出日志信息如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    00:11:34.969 [main] DEBUG org.apache.ibatis.logging.LogFactory - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
    00:11:34.972 [main] DEBUG org.apache.ibatis.logging.LogFactory - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
    00:11:34.988 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
    00:11:34.988 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
    00:11:34.989 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
    00:11:34.989 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
    00:11:35.033 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
    00:11:36.175 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 501705927.
    00:11:36.176 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1de76cc7]
    00:11:36.178 [main] DEBUG xxx.insertTest - ==> Preparing: insert into tt_vehicle (product_name, vin, production_date, guide_price) values ("Audi A6L", "LVIN1", "2024-06-22", "400000.12")
    00:11:36.199 [main] DEBUG xxx.insertTest - ==> Parameters:
    00:11:36.215 [main] DEBUG xxx.insertTest - <== Updates: 1
    00:11:36.216 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1de76cc7]

    可以看出控制台打印的日志信息按照日志配置文件进行了格式化后才输出

拓展SLF4JSimple Logging Facade for Java)本质上不是一个日志框架,而是一个日志抽象层(facade),提供了一个简单的日志API,它允许最终用户在部署时插入所需的日志框架。这使得SLF4J成为一种标准的日志API,开发者可以通过这个API编写日志语句,而不需要直接依赖于具体的日志实现库,如Log4jLogbackjava.util.logging等。

数据源

dataSource标签是用于配置数据库连接的来源,它是配置数据库连接细节的关键部分。这个标签定义了数据库的连接信息,包括驱动程序、连接字符串、用户名、密码等。

配置示例

1
2
3
4
5
6
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>

dataSource标签含有属性type,可以指定数据源的类型,常见的类型有:

  • UNPOOLED:每次请求都会打开和关闭连接。
  • POOLED:使用连接池,复用连接,提高效率。
  • JNDI:用于在如Java EE或应用服务器这样的容器中查找JNDI数据源。

UNPOOLED

这个数据源的实现会每次请求时打开和关闭连接。性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。UNPOOLED类型的数据源仅仅需要配置以下 5 种属性:

  • driver:JDBC驱动的Java类全限定名(如com.mysql.cj.jdbc.Driver)。
  • url:数据库的JDBC URL地址。
  • username:登录数据库的用户名。
  • password:登录数据库的密码。
  • defaultTransactionIsolationLevel:默认的连接事务隔离级别,仅适用于mybatis管理的SqlSession
  • defaultNetworkTimeout:等待数据库操作完成的默认网络超时时间(单位:毫秒)。

POOLED

这种数据源的实现利用“池”的概念将JDBC连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。

除了UNPOOLED中提到的属性,POOLED还有如下属性:

  • poolMaximumActiveConnections:在任意时间可存在的活动(正在使用)连接数量,默认值:10
  • poolMaximumIdleConnections:任意时间可存在的空闲连接数。
  • poolMaximumCheckoutTime:连接从池中被借出的最大时间,默认值:20000毫秒。
  • poolTimeToWait:超过时间未获取到连接,则连接池会打印状态日志并重新尝试获取一个连接,默认值:20000毫秒。
  • poolMaximumLocalBadConnectionTolerance:这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过poolMaximumIdleConnectionspoolMaximumLocalBadConnectionTolerance之和。 默认值:3(新增于 3.4.5)
  • poolPingQuery:发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。
  • poolPingEnabled:是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的SQL语句(最好是一个速度非常快的SQL语句),默认值:false
  • poolPingConnectionsNotUsedFor:配置poolPingQuery的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当poolPingEnabledtrue时适用)。

JNDI

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

  • initial_context:这个属性用来在InitialContext中寻找上下文(即initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext中寻找data_source属性。
  • data_source:这是引用数据源实例位置的上下文路径。提供了initial_context配置时会在其返回的上下文中进行查找,没有提供时则直接在InitialContext中查找。

拓展:通常,在应用启动时通过数据源设置连接的autoCommit模式,而当连接关闭并返回到连接池时,MyBatis默认会尝试将autoCommit设置回借用前的状态。这样做的目的是保证连接的状态清晰一致,避免对后续使用该连接的操作产生意外影响。出于对性能优化或特定数据源兼容性的需要,可以设置属性值skipSetAutoCommitOnClose,这个属性允许控制在连接关闭时是否跳过这种自动恢复autoCommit状态的行为。

测试

  1. 将配置文件中dataSourcetype属性设置为UNPOOLED

    1
    <dataSource type="UNPOOLED">

    运行测试程序

    1
    2
    3
    4
    5
    6
    SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
    SqlSession dbSession = sqlSessionFactory.openSession();
    Object o1 = dbSession.selectOne("ownerMapper.selectMapTest", 1);
    Object o2 = dbSession.selectOne("ownerMapper.selectMapTest", 2);
    dbSession.close();

    控制台输出日志如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    01:37:36.586 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
    01:37:37.344 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@531c311e]
    01:37:37.349 [main] DEBUG ownerMapper.selectMapTest - ==> Preparing: select id as id, name as name from tt_owner where id = ?
    01:37:37.369 [main] DEBUG ownerMapper.selectMapTest - ==> Parameters: 1(Integer)
    01:37:37.383 [main] DEBUG ownerMapper.selectMapTest - <== Total: 0
    01:37:37.385 [main] DEBUG ownerMapper.selectMapTest - ==> Preparing: select id as id, name as name from tt_owner where id = ?
    01:37:37.385 [main] DEBUG ownerMapper.selectMapTest - ==> Parameters: 2(Integer)
    01:37:37.386 [main] DEBUG ownerMapper.selectMapTest - <== Total: 0
    01:37:37.386 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@531c311e]
    01:37:37.386 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@531c311e]

    根据日志内容可看到,程序运行首先是打开了JDBC连接,在执行完SQL语句以后,关闭了JDBC连接。

  2. dataSourcetype设置为POOLED

    1
    <dataSource type="POOLED">

    再次运行测试类,控制台输出日志如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    01:39:05.036 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
    01:39:05.036 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
    01:39:05.036 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
    01:39:05.036 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
    01:39:05.036 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
    01:39:05.036 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
    01:39:05.036 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
    01:39:05.036 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
    01:39:05.036 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
    01:39:05.037 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
    01:39:05.037 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
    01:39:05.774 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
    01:39:06.528 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 246003654.
    01:39:06.529 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@ea9b7c6]
    01:39:06.531 [main] DEBUG ownerMapper.selectMapTest - ==> Preparing: select id as id, name as name from tt_owner where id = ?
    01:39:06.552 [main] DEBUG ownerMapper.selectMapTest - ==> Parameters: 1(Integer)
    01:39:06.564 [main] DEBUG ownerMapper.selectMapTest - <== Total: 0
    01:39:06.565 [main] DEBUG ownerMapper.selectMapTest - ==> Preparing: select id as id, name as name from tt_owner where id = ?
    01:39:06.566 [main] DEBUG ownerMapper.selectMapTest - ==> Parameters: 2(Integer)
    01:39:06.567 [main] DEBUG ownerMapper.selectMapTest - <== Total: 0
    01:39:06.567 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@ea9b7c6]
    01:39:06.567 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@ea9b7c6]
    01:39:06.567 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 246003654 to pool.

    与上一次不同的是,在连接池初始化时PooledDataSource创建了连接,但在操作了执行完SQL语句以后将连接对象返还到连接池中。

  3. 配置数据库连接池如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!-- 最大连接数 -->
    <property name="poolMaximumActiveConnections" value="2"/>
    <!-- 最大空闲数 -->
    <property name="poolMaximumIdleConnections" value="1"/>
    <!-- 连接被借出的最长时间 -->
    <property name="poolMaximumCheckoutTime" value="10000"/>
    <!-- 超时打印时间 -->
    <property name="poolTimeToWait" value="2000"/>
    <!-- 开启数据库侦测 -->
    <property name="poolPingEnabled" value="true"/>
    <!-- 数据库侦测sql -->
    <property name="poolPingQuery" value="SELECT 1"/>
    <!-- 未使用数据库连接的时间阈值 -->
    <property name="poolPingConnectionsNotUsedFor" value="3600000"/>

    测试代码:在一个循环中,每次循环都开启一个新的SqlSession并执行查询,但不关闭这些SqlSession,模拟遇到了资源耗尽的情况

    1
    2
    3
    4
    for (int i = 0; i < 3; i++) {
    SqlSession sqlSession = SqlSessionUtil.openSession();
    sqlSession.selectOne("ownerMapper.selectMapTest", i);
    }

    运行后,控制台输出日志如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    01:44:54.468 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
    01:44:55.227 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 246003654.
    01:44:55.228 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@ea9b7c6]
    01:44:55.232 [main] DEBUG ownerMapper.selectMapTest - ==> Preparing: select id as id, name as name from tt_owner where id = ?
    01:44:55.253 [main] DEBUG ownerMapper.selectMapTest - ==> Parameters: 0(Integer)
    01:44:55.266 [main] DEBUG ownerMapper.selectMapTest - <== Total: 0
    01:44:55.268 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
    01:44:55.281 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 737104370.
    01:44:55.282 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2bef51f2]
    01:44:55.282 [main] DEBUG ownerMapper.selectMapTest - ==> Preparing: select id as id, name as name from tt_owner where id = ?
    01:44:55.282 [main] DEBUG ownerMapper.selectMapTest - ==> Parameters: 1(Integer)
    01:44:55.283 [main] DEBUG ownerMapper.selectMapTest - <== Total: 0
    01:44:55.283 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
    01:44:55.283 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - Waiting as long as 2000 milliseconds for connection.
    01:44:57.287 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - Waiting as long as 2000 milliseconds for connection.
    01:44:59.300 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - Waiting as long as 2000 milliseconds for connection.
    01:45:01.311 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - Waiting as long as 2000 milliseconds for connection.
    01:45:03.312 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - Waiting as long as 2000 milliseconds for connection.
    01:45:05.321 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - Claimed overdue connection 246003654.
    01:45:05.322 [main] DEBUG ownerMapper.selectMapTest - ==> Preparing: select id as id, name as name from tt_owner where id = ?
    01:45:05.322 [main] DEBUG ownerMapper.selectMapTest - ==> Parameters: 2(Integer)
    01:45:05.323 [main] DEBUG ownerMapper.selectMapTest - <== Total: 0

    分析:在前两次循环中,都拿到了连接,此时达到了连接池的最大连接数,因此第三个连接请求会等待,且每经过2000ms都会打印等待信息,等待时间超过10000ms(连接被借出的最长时间)以后,第一个连接被声明为超时并回收,之后重新分配给了第三次连接请求。

多环境

MyBatis支持配置多个数据库环境,在mybatis-config.xml中的environments标签下,通过environment配置多个数据库环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<environments default="dev_mybatis_db">
<!-- 一个数据库环境 -->
<environment id="dev_mybatis_db">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
<!-- 另一个数据库环境 -->
<environment id="dev_mybatis_db2">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_db2"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>

其中一个environment对应一个数据库环境,也对应一个SqlSessionFactory对象,这些environment也有一个专属的id,在创建SqlSessionFactory对象时,这个id可作为构造参数,用于指定对应的数据库环境。

创建用于测试的另一个数据库表mybatis_db2.tt_test

1
2
3
4
5
6
7
8
9
10
use mybatis_db2;
CREATE TABLE `tt_test`
(
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8mb4 COMMENT ='测试表';

insert into tt_test(id) value (1);

对应的配置Mapper文件

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="testMapper">
<select id="selectMapTest" resultType="Map">
select id as id
from tt_test
where id = #{id}
</select>
</mapper>

运行测试程序:查询不同的数据库表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory dbFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis_db");
SqlSessionFactory db2Factory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"), "mybatis_db2");
SqlSession dbSession = dbFactory.openSession();
SqlSession db2Session = db2Factory.openSession();

Object o1 = dbSession.selectOne("ownerMapper.selectMapTest", 1);
Object o2 = db2Session.selectOne("testMapper.selectMapTest", 1);

log.info("o1:{}", o1);
log.info("o2:{}", o2);

dbSession.close();
db2Session.close();

控制台输出日志如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
01:29:42.671 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
01:29:43.470 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 1337829755.
01:29:43.470 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4fbda97b]
01:29:43.472 [main] DEBUG ownerMapper.selectMapTest - ==> Preparing: select id as id, name as name from tt_owner where id = ?
01:29:43.492 [main] DEBUG ownerMapper.selectMapTest - ==> Parameters: 1(Integer)
01:29:43.509 [main] DEBUG ownerMapper.selectMapTest - <== Total: 0
01:29:43.511 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
01:29:43.527 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 1398260359.
01:29:43.527 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5357c287]
01:29:43.527 [main] DEBUG testMapper.selectMapTest - ==> Preparing: select id as id from tt_test where id = ?
01:29:43.527 [main] DEBUG testMapper.selectMapTest - ==> Parameters: 1(Integer)
01:29:43.529 [main] TRACE testMapper.selectMapTest - <== Columns: id
01:29:43.529 [main] TRACE testMapper.selectMapTest - <== Row: 1
01:29:43.531 [main] DEBUG testMapper.selectMapTest - <== Total: 1
01:29:43.531 [main] INFO space.yangtao.client.TestClient001 - o1:null
01:29:43.531 [main] INFO space.yangtao.client.TestClient001 - o2:{id=1}
01:29:43.531 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4fbda97b]
01:29:43.531 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4fbda97b]
01:29:43.531 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 1337829755 to pool.
01:29:43.531 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5357c287]
01:29:43.532 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5357c287]
01:29:43.532 [main] DEBUG o.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 1398260359 to pool.

由日志可知已正确查询出了数据,即通过不同的environment创建了不同的SqlSessionFactory,获取不同环境的SqlSession,从而实现了不同环境的库表查询操作。

事务管理

MyBatis提供了一个事务管理器接口org.apache.ibatis.transaction.Transaction,通过配置mybatis-config.xmltransactionManager的标签属性type,可指定接口Transaction的实现类,进而完成MyBatis管理事务的配置。

在MyBatis中有两种类型的事务管理器,分别是JDBCMANAGED,设置方式为

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

JDBC

  • Transaction接口的实现类为JdbcTransaction
  • 直接使用 JDBC 的事务控制机制,需要手动控制事务的提交(commit)和回滚(rollback)。
  • 适合多数场景,特别是在没有复杂的 JEE 容器管理事务的环境中。

MANAGED

  • Transaction接口的实现类为ManagedTransaction
  • MyBatis不再负责事务的管理,而是将事务管理交给其他的JavaEE容器管理。
  • 通常用在Spring或JEE容器中,这些容器支持声明式事务管理。

选择

  • 在选择事务管理器时,考虑应用运行的环境和需要的事务控制级别。
  • 在使用JDBC事务管理时,需要确保正确处理事务的提交和回滚。
  • 在使用MANAGED事务管理时,确保应用服务器或Spring容器正确配置了事务管理。

Mapper配置

mybatis-config.xmlmappers标签用于配置Mapper映射文件,允许MyBatis知道如何找到并加载这些Mapper,从而可以将SQL语句映射到接口方法。

通过XML文件

  1. 指定mapper标签的resource属性为XML文件的相对路径(注意resources目录为项目的根路径)

    1
    2
    3
    <mappers>
    <mapper resource="mapper/VehicleMapper.xml"/>
    </mappers>
  2. 指定mapper标签的url属性(不推荐,不同的操作系统中可能不通用)

    1
    2
    3
    <mappers>
    <mapper url="file:///C:/CodeSpaces/IDEASpace/mybatis-learn/mybatis-demo/target/classes/mapper/VehicleMapper.xml"/>
    </mappers>

通过类名

直接指定Mapper对应接口的类名,注意要保证Mapper XML文件与接口class文件在同一个命名空间下

如在space.yangtao.mapper目录下定义了接口VehicleMapper.java,且Mapper XML文件所在目录为space/yangtao/mapper,则可以通过mapper标签的class属性配置

1
2
3
<mappers>
<mapper class="space.yangtao.mapper.VehicleMapper"/>
</mappers>

包扫描

如果有多个Mapper,并且希望MyBatis自动扫描一个报下的所有Mapper,可以通过package标签实现。注意跟通过类名配置一样,要保证Mapper XML文件与接口class文件在同一个命名空间下

1
<package name="space.yangtao.mapper"/>

总结

通过本文的介绍,开发者应能够掌握MyBatis的高级配置技巧,这包括如何通过日志配置来优化SQL调试过程,选择适合项目需求的数据源类型,以及正确地管理事务以确保应用的稳定性和一致性。此外,文章还讨论了如何通过Mapper配置来简化SQL管理和提高开发效率。掌握这些配置策略将有助于开发者更好地利用MyBatis框架,以应对复杂的数据库操作和事务管理需求。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!