先讲下数据库连接池的优势:
对于一个简单的数据库应用,由于对于数据库的访问不是很频繁。这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不会带来什么明显的性能上的开销。但是对于一个复杂的数据库应用,情况就完全不同了。频繁的建立、关闭连接,会极大的减低系统的性能,因为对于连接的使用成了系统性能的瓶颈。
连接复用。通过建立一个数据库连接池以及一套连接使用管理策略,使得一个数据库连接可以得到高效、安全的复用,避免了数据库连接频繁建立、关闭的开销。
对于共享资源,有一个很著名的设计模式:资源池。该模式正是为了解决资源频繁分配、释放所造成的问题的。把该模式应用到数据库连接管理领域,就是建立一个数据库连接池,提供一套高效的连接分配、使用策略,最终目标是实现连接的高效、安全的复用。
数据库连接池的基本原理是在内部对象池中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法。如:
外部使用者可通过getConnection 方法获取连接,使用完毕后再通过releaseConnection方法将连接返回,注意此时连接并没有关闭,而是由连接池管理器回收,并为下一次使用做好准备。
数据库连接池技术带来的优势:
1. 资源重用
由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。
2. 更快的系统响应速度
数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
3. 新的资源分配手段
对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接池技术,几年钱也许还是个新鲜话题,对于目前的业务系统而言,如果设计中还没有考虑到连接池的应用,那么…….快在设计文档中加上这部分的内容吧。某一应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源。
4. 统一的连接管理,避免数据库连接泄漏
在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。一个最小化的数据库连接池实现。
---------------------------------------------------------------------------------
下面开讲项目:
1、导包:导入一系列jar文件:spring.jar,commons-logging.jar包,数据源所依赖的commoms-dpcp.jar,commons-pool.jar,aop所依赖的aspectjrt.jar,aspectjveaver.jar,cglib-nodep-2.1.3.jar,注解所依赖的common-annotation.jar,数据库所依赖的mysql-connector-java-5.0.4.jar
2、配置数据源(bean.xml文件中)
<!-- 应用路径下的jdbc.properties文件,读取的话使用${名称}的方式-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--一个bean交给spring容器管理。当类对象销毁时候,执行close方法-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!--数据库驱动类-->
<property name="driverClassName" value="${driverClassName}"/>
<!--数据库的链接URL-->
<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}"/>
<!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
<property name="maxIdle" value="${maxIdle}"/>
<!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
<property name="minIdle" value="${minIdle}"/>
</bean>
其中使用了第一行中的jdbc.properties文件,内容如下:
driverClassName=org.gjt.mm.mysql.Driver
url=jdbc\:mysql\://localhost\:3306/itcast?useUnicode\=true&characterEncoding\=UTF-8
username=root
password=123
initialSize=1
maxActive=500
maxIdle=2
minIdle=1
3、配置事务,配置事务时,需要在xml配置文件中引入用于声明事务的tx命名空间,事务的配置方式有两种:注解方式和基于XML配置方式。
<!--配置JDBC数据源的事务管理器,不再手动管理事务,而是交给spring容器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>//ref指向的是上方的<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"的id
</bean>
<!--启用spring的事务管理器-->
<tx:annotation-driven transaction-manager="txManager"/>//transaction-manager="txManager"指向的是事务管理器中的<bean id="txManager"的id
其中tx命名空间未声明,所以需要引入声明:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" @新加入
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx @新加入
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> @新加入
通过以上步骤,我们就已经配好spring+JDBC集成了。
所以,beans.xml整个文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${driverClassName}"/>
<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}"/>
<!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
<property name="maxIdle" value="${maxIdle}"/>
<!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
<property name="minIdle" value="${minIdle}"/>
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 尝试使用自动扫描的方式来处理下,总结下原因:
之前的<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean" >
<property name="personDao" ref="chen"/>
</bean>这里的personDao是PersonServiceBean类的一个属性,有出现的,而这次你想要的用注解方式
而不是XML文件配置的方式的话,你的datasource这个属性找不到哇我也没办法
<context:component-scan base-package="cn.itcast"/>
-->
</beans>
4、在数据库中建表,table person,两个属性id和name,id是主键自增长
后建立一个Person.java,里面对应数据库的两个属性,如下:
package cn.itcast.bean;
public class Person {
private Integer id;
private String name;
public Person(){}
public Person(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
5、建立接口和实现类:
接口:
package cn.itcast.service;
import java.util.List;
import cn.itcast.bean.Person;
public interface PersonService {
/**
* 保存person
* @param person
*/
public void save(Person person);
/**
* 更新person
* @param person
*/
public void update(Person person);
/**
* 获取person
* @param personid
* @return
*/
public Person getPerson(Integer personid);
/**
* 获取所有person
* @return
*/
public List<Person> getPersons();
/**
* 删除指定id的person
* @param personid
*/
public void delete(Integer personid);
}
实现类:
package cn.itcast.service.impl;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import cn.itcast.bean.Person;
import cn.itcast.service.PersonService;
//该实现类需要交给spring容器托管,
//<bean id="personService" class="cn.itcast.service.impl.PersonServiceBean">
// <property name="dataSource" ref="dataSource"/>
//</bean>
@Transactional
//启动声明事务,告诉spring,PersonServiceBean是受spring事务管理的
//表示将该类交给事务管理器管理,不然的话,可能会导致下方方法会在各自事务中执行,处理事务时候不同步
public class PersonServiceBean implements PersonService {
//private DataSource dataSource;//哇哇哇哇!问题出在这里,他使用的的jdbcTemplate来实现的而不是datasource
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public void delete(Integer personid) {
jdbcTemplate.update("delete from person where id=?", new Object[]{personid},
new int[]{java.sql.Types.INTEGER});
}
public Person getPerson(Integer personid) {
return (Person)jdbcTemplate.queryForObject("select * from person where id=?", new Object[]{personid},
new int[]{java.sql.Types.INTEGER}, new PersonRowMapper());
}
@SuppressWarnings("unchecked")
public List<Person> getPersons() {
return (List<Person>)jdbcTemplate.query("select * from person", new PersonRowMapper());
}
public void save(Person person) {
jdbcTemplate.update("insert into person(name) values(?)", new Object[]{person.getName()},
new int[]{java.sql.Types.VARCHAR});
}
public void update(Person person) {
jdbcTemplate.update("update person set name=? where id=?", new Object[]{person.getName(), person.getId()},
new int[]{java.sql.Types.VARCHAR, java.sql.Types.INTEGER});
}
}
6、测试,PersonServiceTest.java
package junit.test;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.itcast.bean.Person;
import cn.itcast.service.PersonService;
public class PersonServiceTest {
private static PersonService personService;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
try {
ApplicationContext cxt = new ClassPathXmlApplicationContext("beans.xml");
personService = (PersonService) cxt.getBean("personService");
} catch (RuntimeException e) {
e.printStackTrace();
}
}
@Test public void save(){
for(int i=1; i<6; i++)
personService.save(new Person("陈宁"+ i));
}
@Test public void getPerson(){
Person person = personService.getPerson(18);
System.out.println(person.getName());
}
@Test public void update(){
Person person = personService.getPerson(8);
person.setName("张xx");
personService.update(person);
}
@Test public void delete(){
personService.delete(2);
}
@Test public void getBeans(){
for(Person person : personService.getPersons()){
System.out.println(person.getName());
}
}
}
在单元测试中,分别单独执行各个办法即可,注意里面参数是写死的,需要自己根据数据库表的数据来设置。
完成。
源码下载: