对于数据库连接池的一些思考和MyBatis的集成与使用
Java应用要连接数据库需要先通过jdbc与数据库之间产生connection,然后通过sql语句产生statment,再执行这个statment查询的到ResultSet返回给应用,应用解析ResultSet获得Java支持类型的结果。
这里存在以下几个问题:
- 产生连接本身需要,而且这个延时比起常规的查询操作来说是较高的,对于有响应时限的操作来说这是一个致命问题。
- 建立连接需要消耗数据库资源,数据库本身开放的连接数量是有限的,即使不断的建立与关闭连接保证总数的稳定,也会极大消耗数据库资源影响性能。而数据库资源比起应用服务器来说又是非常宝贵的,数据库的分流比应用要困难很多。
所以要避免这种情况就有了连接池。连接池有几个基本属性:初始连接数、最大连接数、最小空闲数、最大空闲数、最大等待毫秒数,分别假设为5 10 1 5 60。连接池可以想象为一个池子,先建立5个连接放入池子中。应用的一个线程需要与数据库交互时,向连接池要一个连接,然后去执行,执行完后会将连接还给连接池。当并发量很高时,连接池内的连接会被取完,此时又有线程要连接,就会检查是否达到了最大连接数,如果没达到就会继续新建连接,将连接交给线程。如果达到上限,则让线程进行等待,知道有线程把连接还回来。
当连接池内的连接数超过了最大空闲数时,当这个连接被归还连接池,它就会被强制关闭。而当连接池内的连接数小于最小空闲数时,连接池会自动去新建连接来补充到这个数量。为什么连接数量自己会变化呢?因为数据库存在一个连接的等待时间,超过这个时间始终没有活动的话,连接会被数据库关闭,连接池会周期性的去探知所有连接是否存活,以管理连接的数量。
所谓持久层,是指将数据长期存储到硬盘当中。因为在webapp开发中数据通过数据库存储到硬盘中,所以持久层可以简单的认为就是负责对数据库的增删改查。用过Java原始jdbc的查询的话都知道ResultSet要转换成Java Bean中间的重复操作还是挺多的;要把一个List<T>转换成SQL语句也挺麻烦的,持久层框架的好处之一就是省略掉这中间的重复操作,根据写好的映射关系,直接完成数据库与Java代码之间交互。
常用的持久层框架有两个:MyBatis和Hibernate。MyBatis最大的特点是SQL语句基本就是没有任何封装的原生SQL。缺点当然是开发起来工作量较大,同时不同数据库之间的迁移性收到影响。优点是学习简单,SQL自由性大,也就有更大的优化空间。毕竟正常情况下你能够控制的东西越多,开发就会越复杂,但是性能极限会更高,当然这里的前提是开发者足够资深,毕竟一般人写的代码不如编译器优化出来的。
下面开始SSM框架下的这个M的搭建。首先,pom.xml里面引入相关的依赖,包括mybatis本身、Spring的支持插件、mysql驱动,以及连接池创建工具这里采用dbcp
<!--mysql数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <!-- 连接池创建 --> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency>
然后配置数据源的话有两种方式,一种是直接配置数据源,还有一种是基于容器的数据源比如tomcat下的jndi连接。
直接配置的话,在applicationContext.xml,即通过ContextLoaderListener监听的配置文件里面添加如下:
<!-- 配置jdbc文件 --> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations" value="classpath:jdbc.properties" /> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${driver}" /> <property name="url" value="${url}" /> <property name="username" value="${username}" /> <property name="password" value="${password}" /> <!-- 初始化连接大小 --> <property name="initialSize" value="${initialSize}" /> <!-- 连接池最大数量 --> <property name="maxActive" value="${maxActive}" /> <!-- 连接池最大空闲 --> <property name="maxIdle" value="${maxIdle}" /> <!-- 连接池最小空闲 --> <property name="minIdle" value="${minIdle}" /> <!-- 获取连接最大等待时间 --> <property name="maxWait" value="${maxWait}" /> </bean> <!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!-- 自动扫描mapping.xml文件,**表示迭代查找 --> <property name="mapperLocations" value="classpath:com/web/**/*.xml" /> </bean> <!-- DAO接口所在包名,Spring会自动查找其下的类 ,包下的类需要使用@MapperScan注解,否则容器注入会失败 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.web" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean> <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
这里采用外部读入参数的方式加载用户名信息,便于统一管理。resources目录下添加jdbc.properties
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8 username=root password=123456 initialSize=5 maxActive=10 maxIdle=5 minIdle=1 maxWait=60
然后添加dao层Bean层与对应的xml映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.web.dao.TestMapper"> <select id="getTest" resultType="com.web.bean.HelloBean"> select id,txt from test </select> </mapper>
package com.web.dao; import java.util.List; import org.mybatis.spring.annotation.MapperScan; import com.web.bean.HelloBean; @MapperScan public interface TestMapper { public List<HelloBean> getTest(); }
package com.web.bean; public class HelloBean { private String id; private String txt; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getTxt() { return txt; } public void setTxt(String txt) { this.txt = txt; } }
全部添加完后,项目的路径如下:
然后通过注入dao层类之后执行testMapper.getTest(),表与插入的测试数据可以参见我上一篇文章。结果下:
说明数据已经直接映射到了结果的bean上面。
第二种方法,就是利用tomcat来创建连接池,然后mybatis直接只用tomcat的数据源。先配置context.xml
<Resource name="jdbc/mybatis-jndi" auth="Container" type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000" username="root" password="123456" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/demo" />
然后在applicationContext.xml文件上添加jndi数据源,并修改mybatis使用的数据源java:comp/env/这里是固定字段,后面的jdbc/mybatis-jndi对应context里面配置的name属性
<bean id="jndiDataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"> <value>java:comp/env/jdbc/mybatis-jndi</value> </property> </bean> <!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- ref是使用的数据源 --> <property name="dataSource" ref="jndiDataSource" /> <!-- 自动扫描mapping.xml文件,**表示迭代查找 --> <property name="mapperLocations" value="classpath:com/web/**/*.xml" /> </bean>
MyBatis的集成至此完成。