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>

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

  • 如果SqlSessionFactory 如果不是单例的,会容易产生大量的连接对象,导致挂掉。
  • 现在想想,我觉得当年的我可能有个误解,查询数据不是最新,而是缓存,并不是这个工具类的问题,我后来修正的方案虽然也能实现多线程下安全访问,但是加锁会减慢速度。
  • 凡是插入,更新,删除,甚至是查询,使用完session之后必须 调用closeSession方法关闭Session, 否则也会出现更新了数据,但是查询出来的不是最新的数据的问题。
  • 我之前一直错误以为只有插入,修改,删除操作后需要关闭session,查询可以直接 return session.getUserList(); 再次使用就不用再次打开一次session ,减少性能开销.但是忽略了多线程下
  • 试想下,多线程环境下,如果一个线程先执行一次查询,这个线程获取了一个session, 然后另外一个线程修改了数据,然后关闭了session.,再次查询由于这个session不为空,获取的还是旧的session,这样就会获取到的不是最新的数据而是缓存了。

我的那种方法就删了,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();
		}
	}
}
  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

极客星云

谢谢认可,希望对你的学习有帮助

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值