MyBatis 配置中那些值得注意的坑

当你在使用Mybatis 时进行配置的时候有这样几个坑一定要注意下。

第一坑:Mybatis 的缓存机制

有时候你对数据库中的数据进行了增删改查操作,但是页面请求获取到的总不是最新的数据,这时候你就要检查是不是自己入坑了。

<setting name="cacheEnabled" value="true" />
<setting name="localCacheScope" value="SESSION" />

MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。
Tips:

关于缓存配置,第一个设置一般都会设置成false,这个坑很少会进,但是第二个默认的值为session,这个就是一个大坑,一不留神你就可能进坑,如果你设置成了session 那么就会有缓存,当你对数据进行了修改,但是你每次的请求获取数据都不是最新的,你发现页面还是原来数据,但是数据库已经修改了。这在项目开发和使用中可是相当蛋疼的问题。
解决方案:

<setting name="cacheEnabled" value="false" />
<setting name="localCacheScope" value="STATEMENT" />

第二坑:Mybatis数据库语句打印不出来

你有没有发现当你和struts2 或者springmvc 等其他框架整合后发现,想打印Mybatis 数据库语句却怎么样也打印不出来?

不少应用服务器的classpath中已经包含Commons Logging,如Tomcat和WebShpere,
所以MyBatis会把它作为具体的日志实现。记住这点非常重要。这将意味着,在诸如
WebSphere的环境中——WebSphere提供了Commons Logging的私有实现,你的Log4J配置将被忽略。这种做法不免让人悲催,MyBatis怎么能忽略你的配置呢?事实上,因Commons Logging已经存 在了,按照优先级顺序,Log4J自然就被忽略了!不过,如果你的应用部署在一个包含Commons Logging的环境, 而你又想用其他的日志框架,你可以通过在MyBatis的配置文件mybatis-config.xml里面添加一项setting(配置)来选择一个不同的日志实现。

官方提供的解决方案:
http://www.mybatis.org/mybatis-3/zh/logging.html

<configuration>
  <settings>
    ...
    <setting name="logImpl" value="LOG4J"/>
    ...
  </settings>
</configuration>

但是当你以为找到答案而欣喜时,按照官网这样配置后发现还是打印不出来SQL 语句?why?

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>
        <Console name="STDOUT" target="SYSTEM_OUT">
            <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n" />
        </Console>
        <File name="MyFile" fileName="${log4j:configParentLocation}/logs/${date:yyyy-MM-dd}-application.log">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
        </File>
    </Appenders>
    <Loggers>
        <Logger name="com.opensymphony.xwork2" level="info" />
        <Logger name="org.apache.struts2" level="info" />
        <Logger name="yourcompany.com" level="error" />
        <Root level="error">
            <AppenderRef ref="STDOUT" />
            <AppenderRef ref="MyFile" />
        </Root>
    </Loggers>
</Configuration>

好吧,突然我不卖关子了,当你等级设置成info 或者error的时候,你是看不到SQL的,想看到打印的SQL语句只需要将等级设置为debug 即可。

正确配置如图所示:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>
        <Console name="STDOUT" target="SYSTEM_OUT">
            <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n" />
        </Console>
        <File name="MyFile" fileName="${log4j:configParentLocation}/logs/${date:yyyy-MM-dd}-application.log">
            <PatternLayout>
                <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
            </PatternLayout>
        </File>
    </Appenders>
    <Loggers>
        <Logger name="com.opensymphony.xwork2" level="info" />
        <Logger name="org.apache.struts2" level="info" />
        <Logger name="yourcompany.com" level="debug" />
        <Root level="debug">
            <AppenderRef ref="STDOUT" />
            <AppenderRef ref="MyFile" />
        </Root>
    </Loggers>
</Configuration>

第三坑 数据源

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

许多 MyBatis 的应用程序将会按示例中的例子来配置数据源。然而它并不是必须的。要知道为了方便使用延迟加载,数据源才是必须的。
有三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”)

然而我们一般大多数都会选择POOLED 是不是?

<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>

只是这样来看似乎并没有什么大的问题,但是你有没有发现当你的tomcat 运行了一段时间后会挂掉?

iBatis自己带了一个simple的数据库连接池,基本的功能都有。但是在处理部分数据库(比如mysql)的连接空闲时间太长(mysql是8小时)自动超时的时候,就比不上象c3p0这样的连接池软件了(c3p0能自动处理数据库连接被关闭的情况)。

感谢博主:http://www.blogjava.net/usherlight/archive/2008/05/13/200164.html

解决方案:

<dataSource type="POOLED">
<!-- 数据库连接池配置 -->
<!-- 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10 -->
<property name="poolMaximumActiveConnections" value="300" />
<!-- 任意时间可能存在的空闲连接数 默认是5,最好设置为0,否则可能会崩溃掉 -->
<property name="poolMaximumIdleConnections" value="0" />
<!-- 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒) -->
<property name="poolMaximumCheckoutTime" value="20000" />
<!-- 这是一个底层设置,如果获取连接花费的相当长的时间,它会给连接池打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),默认值:20000 
                    毫秒(即 20 秒)。 -->
<property name="poolTimeToWait" value="20000" />

<!-- 是否启用侦测查询。若开启,也必须使用一个可执行的 SQL 语句设置 poolPingQuery 属性(最好是一个非常快的 SQL),默认值:false。 -->
<property name="poolPingEnabled" value="true" />
<!-- 配置 poolPingQuery 的使用频度。这可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 
                    — 当然仅当 poolPingEnabled 为 true 时适用) -->
<property name="poolPingConnectionsNotUsedFor" value="3600000" />
                <!-- 发送到数据库的侦测查询,用来检验连接是否处在正常工作秩序中并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一个恰当的错误消息。 -->
                <property name="poolPingQuery" value="select 1" />
            </dataSource>

上面注意一个属性值,在任意时间可以存在的活动(也就是正在使用)连接数量
这个值我设置成了300是因为我查看了我的阿里云服务器最大连接数是600,但是本地安装的MySQL我看了下my.ini 文件,默认最大连接数是341。你可以根据自己实际情况修改。

property name="poolMaximumActiveConnections" value="300" />

这里还要强调一点,这个值最好设置为0,否则会入坑。

<property name="poolMaximumIdleConnections" value="0" />

具体原因详情请移步:
使用Mybatis时请注意这两个参数,否则会让你的数据库连接爆掉
http://biancheng.dnbcw.net/mysql/420838.html

好了,坑总结完了。
现在献上自己的mybatis3 配置模板文件:

<?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 必须放到最上面 -->
    <settings>
        <!-- 该配置影响的所有映射器中配置的缓存的全局开关。默认true -->
        <setting name="cacheEnabled" value="false" />
        <!-- MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 
            默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 
            的不同调用将不会共享数据。 -->
        <setting name="localCacheScope" value="STATEMENT" />
        <!-- 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 
            NULL、VARCHAR 或 OTHER。 -->
        <setting name="jdbcTypeForNull" value="NULL" />
        <!-- MyBatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具 SLF4J Apache Commons Logging Log4j 
            2 Log4j JDK logging 在诸如 WebSphere的环境中——WebSphere提供了Commons Logging的私有实现,你的Log4J配置将被忽略。 
            因Commons Logging已经存 在了,按照优先级顺序,Log4J自然就被忽略了 如果你的应用部署在一个包含Commons Logging的环境, 
            而你又想用其他的日志框架, 你可以通过在MyBatis的配置文件MyBatis-config.xml里面添加一项setting(配置)来选择一个不同的日志实现。 
            可选值:SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、NO_LOGGING -->
        <setting name="logImpl" value="LOG4J2" />
    </settings>

    <!-- 设置数据库连接环境 default 数据库连接环境开关 
         development 本地环境 Product_Intranet 内网环境 
         -->
    <environments default="development">
        <environment id="development">
            <!-- 使用JDBC事务管理 -->
            <transactionManager type="JDBC" />
            <!-- 配置数据库连接池 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url"
                    value="jdbc:mysql://your ip:3306/your_db_name" />
                <property name="username" value="root" />
                <property name="password" value="test" />
                <!-- 数据库连接池配置 -->
                <!-- 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10 -->
                <property name="poolMaximumActiveConnections" value="300" />
                <!-- 任意时间可能存在的空闲连接数 默认是5,最好设置为0,否则可能会崩溃掉 -->
                <property name="poolMaximumIdleConnections" value="0" />
                <!-- 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒) -->
                <property name="poolMaximumCheckoutTime" value="20000" />
                <!-- 这是一个底层设置,如果获取连接花费的相当长的时间,它会给连接池打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),默认值:20000 
                    毫秒(即 20 秒)。 -->
                <property name="poolTimeToWait" value="20000" />

                <!-- 是否启用侦测查询。若开启,也必须使用一个可执行的 SQL 语句设置 poolPingQuery 属性(最好是一个非常快的 SQL),默认值:false。 -->
                <property name="poolPingEnabled" value="true" />
                <!-- 配置 poolPingQuery 的使用频度。这可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 
                    — 当然仅当 poolPingEnabled 为 true 时适用) -->
                <property name="poolPingConnectionsNotUsedFor" value="3600000" />
                <!-- 发送到数据库的侦测查询,用来检验连接是否处在正常工作秩序中并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一个恰当的错误消息。 -->
                <property name="poolPingQuery" value="select 1" />

            </dataSource>
        </environment>
        <!-- 上线状态内网版 -->
        <environment id="Product_Intranet">
            <!-- 使用JDBC事务管理 -->
            <transactionManager type="JDBC" />
            <!-- 配置数据库连接池 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url"
                    value="jdbc:mysql://your ip:3306/your_db_name" />
                <property name="username" value="root" />
                <property name="password" value="test" />

                <!-- 数据库连接池配置 -->
                <!-- 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10 -->
                <property name="poolMaximumActiveConnections" value="300" />
                <!-- 任意时间可能存在的空闲连接数 默认是5,最好设置为0,否则可能会崩溃掉 -->
                <property name="poolMaximumIdleConnections" value="0" />
                <!-- 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒) -->
                <property name="poolMaximumCheckoutTime" value="20000" />
                <!-- 这是一个底层设置,如果获取连接花费的相当长的时间,它会给连接池打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),默认值:20000 
                    毫秒(即 20 秒)。 -->
                <property name="poolTimeToWait" value="20000" />

                <!-- 是否启用侦测查询。若开启,也必须使用一个可执行的 SQL 语句设置 poolPingQuery 属性(最好是一个非常快的 SQL),默认值:false。 -->
                <property name="poolPingEnabled" value="true" />
                <!-- 配置 poolPingQuery 的使用频度。这可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 
                    — 当然仅当 poolPingEnabled 为 true 时适用) -->
                <property name="poolPingConnectionsNotUsedFor" value="3600000" />
                <!-- 发送到数据库的侦测查询,用来检验连接是否处在正常工作秩序中并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一个恰当的错误消息。 -->
                <property name="poolPingQuery" value="select 1" />
            </dataSource>
        </environment>
    </environments>

    <!-- 所有数据库语句映射表文件必须在这里注册 -->
    <mappers>
        <!-- 管理员表 -->
        <mapper resource="xingyun/dao/mapper/ManagerMapper.xml" />
    </mappers>
</configuration>

第四坑:代码错误导致的缓存

忘了之前在哪里复制的这段MyBatis数据库连接工具类,今天发现这么写是有问题的,这样做会导致查询的结果不是数据库最新,而是缓存中的数据。

MybatisJDBCUtil.java错误代码贴上来

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

/**
 * MyBatis 获取数据库连接工具类 错误示范代码,切勿这么使用
 * */
public class MybatisJDBCUtil {
    private static final String resource = "mybatis-jdbc-config.xml";// SRC 根目录下必须有这个文件
    private static InputStream inputStream;
    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            System.err.println("can't find "+resource);
        }
    }

    private static final ThreadLocal<SqlSession> THREAD_LOCAL = new ThreadLocal<SqlSession>();

    public static SqlSession currentSession() {
        SqlSession session = THREAD_LOCAL.get();

        if (session == null) {
            session = sqlSessionFactory.openSession();
            THREAD_LOCAL.set(session);
        }
        return session;
    }

    public static void closeSession() {
        SqlSession session = THREAD_LOCAL.get();
        THREAD_LOCAL.set(null);
        if (session != null) {
            session.close();
        }
    }
}

下面附上MyBatis链接数据库工具类修正代码:

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

/**
 * MyBatis 获取数据库连接工具类
 */
public class MybatisJDBCUtil {
    private static final String resource = "mybatis-jdbc-config.xml";// SRC
                                                                        // 根目录下必须有这个文件
    private static InputStream inputStream;
    private static SqlSessionFactory sqlSessionFactory;
    private static SqlSession session;

    static {
        try {
            inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            System.err.println("can't find " + resource);
        }
    }

    public static SqlSession currentSession() {

        session = sqlSessionFactory.openSession();

        return session;
    }

    public static void closeSession() {

        if (session != null) {
            session.clearCache();
            session.close();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/hadues/article/details/63686934
今日推荐