当你在使用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" />
你有没有发现当你和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?
<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%nPattern>
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 即可。
正确配置如图所示:
<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%nPattern>
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">
<property name="poolMaximumActiveConnections" value="300" />
<property name="poolMaximumIdleConnections" value="0" />
<property name="poolMaximumCheckoutTime" value="20000" />
<property name="poolTimeToWait" value="20000" />
<property name="poolPingEnabled" value="true" />
<property name="poolPingConnectionsNotUsedFor" value="3600000" />
<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 配置模板文件:
<configuration>
<settings>
<setting name="cacheEnabled" value="false" />
<setting name="localCacheScope" value="STATEMENT" />
<setting name="jdbcTypeForNull" value="NULL" />
<setting name="logImpl" value="LOG4J2" />
settings>
<environments default="development">
<environment id="development">
<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" />
<property name="poolMaximumActiveConnections" value="300" />
<property name="poolMaximumIdleConnections" value="0" />
<property name="poolMaximumCheckoutTime" value="20000" />
<property name="poolTimeToWait" value="20000" />
<property name="poolPingEnabled" value="true" />
<property name="poolPingConnectionsNotUsedFor" value="3600000" />
<property name="poolPingQuery" value="select 1" />
dataSource>
environment>
<environment id="Product_Intranet">
<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" />
<property name="poolMaximumActiveConnections" value="300" />
<property name="poolMaximumIdleConnections" value="0" />
<property name="poolMaximumCheckoutTime" value="20000" />
<property name="poolTimeToWait" value="20000" />
<property name="poolPingEnabled" value="true" />
<property name="poolPingConnectionsNotUsedFor" value="3600000" />
<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();
}
}
}