赞
踩
(参考http://www.cnblogs.com/xdp-gacl/p/4002804.html 点击打开链接,以此为模板 自己做了整理、修改)
目录
用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。如下图所示:
数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库连接池正式针对这个问题提出来的。数据库连接池负责分配、管理和释放数据库连接,数据库连接池允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。如下图所示:
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。
数据库连接池的最小连接数和最大连接数的设置要考虑到以下几个因素:
最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费。
最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作。
如果最小连接数与最大连接数相差很大:那么最先连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接。不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,他将被放到连接池中等待重复使用或是空间超时后被释放。
编写连接池,需要实现java.sql.DataSource接口。DataSource接口中定义了两个重载的getConnection()方法:
Connection getConnection()
Connection getConnection(String username, String password)
实现DataSource接口,并实现连接池功能的步骤:
在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中。
实现getConnection()方法,让getConnection()方法每次调用时,从LinkedList中取一个Connection返回给用户。
当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到LinkedList中,而不要把conn还给数据库。Collection保证将自己返回到LinkedList中是此处编程的难点。
数据库连接池核心代码
使用动态代理技术构建连接池中的connection
- proxyConn = (Connection) Proxy.newProxyInstance(this.getClass()
- .getClassLoader(), conn.getClass().getInterfaces(),
- new InvocationHandler() {
- //此处为内部类,当close方法被调用时将conn还回池中,其它方法直接执行
- public Object invoke(Object proxy, Method method,
- Object[] args) throws Throwable {
- if (method.getName().equals("close")) {
- pool.addLast(conn);
- return null;
- }
- return method.invoke(conn, args);
- }
- });
数据库连接池编写范例:
- package me.gacl.demo;
-
- import java.io.InputStream;
- import java.io.PrintWriter;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.sql.Connection;
- import java.sql.DriverManager;
- import java.sql.SQLException;
- import java.util.LinkedList;
- import java.util.Properties;
- import javax.sql.DataSource;
-
- /**
- * @ClassName: JdbcPool
- * @Description:编写数据库连接池
- * @author: 孤傲苍狼
- * @date: 2014-9-30 下午11:07:23
- *
- */
- public class JdbcPool implements DataSource{
-
- /**
- * @Field: listConnections
- * 使用LinkedList集合来存放数据库链接,
- * 由于要频繁读写List集合,所以这里使用LinkedList存储数据库连接比较合适
- */
- private static LinkedList<Connection> listConnections = new LinkedList<Connection>();
-
- static{
- //在静态代码块中加载db.properties数据库配置文件
- InputStream in = JdbcPool.class.getClassLoader().getResourceAsStream("db.properties");
- Properties prop = new Properties();
- try {
- prop.load(in);
- String driver = prop.getProperty("driver");
- String url = prop.getProperty("url");
- String username = prop.getProperty("username");
- String password = prop.getProperty("password");
- //数据库连接池的初始化连接数大小
- int jdbcPoolInitSize =Integer.parseInt(prop.getProperty("jdbcPoolInitSize"));
- //加载数据库驱动
- Class.forName(driver);
- for (int i = 0; i < jdbcPoolInitSize; i++) {
- Connection conn = DriverManager.getConnection(url, username, password);
- System.out.println("获取到了链接" + conn);
- //将获取到的数据库连接加入到listConnections集合中,listConnections集合此时就是一个存放了数据库连接的连接池
- listConnections.add(conn);
- }
-
- } catch (Exception e) {
- throw new ExceptionInInitializerError(e);
- }
- }
-
- @Override
- public PrintWriter getLogWriter() throws SQLException {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public void setLogWriter(PrintWriter out) throws SQLException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void setLoginTimeout(int seconds) throws SQLException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public int getLoginTimeout() throws SQLException {
- // TODO Auto-generated method stub
- return 0;
- }
-
- @Override
- public <T> T unwrap(Class<T> iface) throws SQLException {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public boolean isWrapperFor(Class<?> iface) throws SQLException {
- // TODO Auto-generated method stub
- return false;
- }
-
- /* 获取数据库连接
- * @see javax.sql.DataSource#getConnection()
- */
- @Override
- public Connection getConnection() throws SQLException {
- //如果数据库连接池中的连接对象的个数大于0
- if (listConnections.size()>0) {
- //从listConnections集合中获取一个数据库连接
- final Connection conn = listConnections.removeFirst();
- System.out.println("listConnections数据库连接池大小是" + listConnections.size());
- //返回Connection对象的代理对象
- return (Connection) Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler(){
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- if(!method.getName().equals("close")){
- return method.invoke(conn, args);
- }else{
- //如果调用的是Connection对象的close方法,就把conn还给数据库连接池
- listConnections.add(conn);
- System.out.println(conn + "被还给listConnections数据库连接池了!!");
- System.out.println("listConnections数据库连接池大小为" + listConnections.size());
- return null;
- }
- }
- });
- }else {
- throw new RuntimeException("对不起,数据库忙");
- }
- }
-
- @Override
- public Connection getConnection(String username, String password)
- throws SQLException {
- return null;
- }
- }
db.properties配置文件如下:
- driver=com.mysql.jdbc.Driver
- url=jdbc:mysql://localhost:3306/jdbcStudy
- username=root
- password=XDP
-
- jdbcPoolInitSize=10
写一个JdbcUtil测试数据库连接池
- package me.gacl.utils;
-
- import java.sql.Connection;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Statement;
- import me.gacl.demo.JdbcPool;
-
- public class JdbcUtil {
-
- /**
- * @Field: pool
- * 数据库连接池
- */
- private static JdbcPool pool = new JdbcPool();
-
- /**
- * @Method: getConnection
- * @Description: 从数据库连接池中获取数据库连接对象
- * @Anthor:孤傲苍狼
- * @return Connection数据库连接对象
- * @throws SQLException
- */
- public static Connection getConnection() throws SQLException{
- return pool.getConnection();
- }
-
- /**
- * @Method: release
- * @Description: 释放资源,
- * 释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
- * @Anthor:孤傲苍狼
- *
- * @param conn
- * @param st
- * @param rs
- */
- public static void release(Connection conn,Statement st,ResultSet rs){
- if(rs!=null){
- try{
- //关闭存储查询结果的ResultSet对象
- rs.close();
- }catch (Exception e) {
- e.printStackTrace();
- }
- rs = null;
- }
- if(st!=null){
- try{
- //关闭负责执行SQL命令的Statement对象
- st.close();
- }catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- if(conn!=null){
- try{
- //关闭Connection数据库连接对象
- conn.close();
- }catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
现在很多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的实现,即连接池的实现。通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
也有一些开源组织提供了数据源的独立实现:
DBCP 数据库连接池
C3P0 数据库连接池
在使用了数据库连接池之后,在项目的实际开发中就不需要编写连接数据库的代码了,直接从数据源获得数据库的连接。
DBCP 是 Apache 软件基金组织下的开源连接池实现,要使用DBCP数据源,需要应用程序应在系统中增加如下两个 jar 文件:
Commons-dbcp.jar:连接池的实现
Commons-pool.jar:连接池实现的依赖库
Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
1. 导入相关jar包
commons-dbcp-1.2.2.jar、commons-pool.jar
2. 在类目录下加入dbcp的配置文件:dbcpconfig.properties
dbcpconfig.properties的配置信息如下:
- #连接设置
- driverClassName=com.mysql.jdbc.Driver
- url=jdbc:mysql://localhost:3306/jdbcstudy
- username=root
- password=XDP
-
- #<!-- 初始化连接 -->
- initialSize=10
-
- #最大连接数量
- maxActive=50
-
- #<!-- 最大空闲连接 -->
- maxIdle=20
-
- #<!-- 最小空闲连接 -->
- minIdle=5
-
- #<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
- maxWait=60000
-
- #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
- #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
- connectionProperties=useUnicode=true;characterEncoding=UTF8
-
- #指定由连接池所创建的连接的自动提交(auto-commit)状态。
- defaultAutoCommit=true
-
- #driver default 指定由连接池所创建的连接的只读(read-only)状态。
- #如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
- defaultReadOnly=
-
- #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
- #可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
- defaultTransactionIsolation=READ_UNCOMMITTED
如下图所示:
3. 在获取数据库连接的工具类(如jdbcUtils)的静态代码块中创建池
- package me.gacl.util;
-
- import java.io.InputStream;
- import java.sql.Connection;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Statement;
- import java.util.Properties;
- import javax.sql.DataSource;
- import org.apache.commons.dbcp.BasicDataSourceFactory;
-
- /**
- * @ClassName: JdbcUtils_DBCP
- * @Description: 数据库连接工具类
- * @author: 孤傲苍狼
- * @date: 2014-10-4 下午6:04:36
- *
- */
- public class JdbcUtils_DBCP {
- /**
- * 在java中,编写数据库连接池需实现java.sql.DataSource接口,每一种数据库连接池都是DataSource接口的实现
- * DBCP连接池就是java.sql.DataSource接口的一个具体实现
- */
- private static DataSource ds = null;
- //在静态代码块中创建数据库连接池
- static{
- try{
- //加载dbcpconfig.properties配置文件
- InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
- Properties prop = new Properties();
- prop.load(in);
- //创建数据源
- ds = BasicDataSourceFactory.createDataSource(prop);
- }catch (Exception e) {
- throw new ExceptionInInitializerError(e);
- }
- }
-
- /**
- * @Method: getConnection
- * @Description: 从数据源中获取数据库连接
- * @Anthor:孤傲苍狼
- * @return Connection
- * @throws SQLException
- */
- public static Connection getConnection() throws SQLException{
- //从数据源中获取数据库连接
- return ds.getConnection();
- }
-
- /**
- * @Method: release
- * @Description: 释放资源,
- * 释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
- * @Anthor:孤傲苍狼
- *
- * @param conn
- * @param st
- * @param rs
- */
- public static void release(Connection conn,Statement st,ResultSet rs){
- if(rs!=null){
- try{
- //关闭存储查询结果的ResultSet对象
- rs.close();
- }catch (Exception e) {
- e.printStackTrace();
- }
- rs = null;
- }
- if(st!=null){
- try{
- //关闭负责执行SQL命令的Statement对象
- st.close();
- }catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- if(conn!=null){
- try{
- //将Connection连接对象还给数据库连接池
- conn.close();
- }catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
测试DBCP数据源
- package me.gacl.test;
-
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import org.junit.Test;
- import me.gacl.util.JdbcUtils_DBCP;
-
- public class DataSourceTest {
-
- @Test
- public void dbcpDataSourceTest() {
- Connection conn = null;
- PreparedStatement st = null;
- ResultSet rs = null;
- try{
- //获取数据库连接
- conn = JdbcUtils_DBCP.getConnection();
- String sql = "insert into test1(name) values(?)";
- st = conn.prepareStatement(sql);
- st.setString(1, "gacl");
- st.executeUpdate();
- //获取数据库自动生成的主键
- rs = st.getGeneratedKeys();
- if(rs.next()){
- System.out.println(rs.getInt(1));
- }
- }catch (Exception e) {
- e.printStackTrace();
- }finally{
- //释放资源
- JdbcUtils_DBCP.release(conn, st, rs);
- }
- }
- }
C3P0 是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。C3P0数据源在项目开发中使用得比较多。
c3p0与dbcp区别
dbcp没有自动回收空闲连接的功能
c3p0有自动回收空闲连接功能
1. 导入相关jar包
c3p0-0.9.2-pre1.jar、mchange-commons-0.2.jar,如果操作的是Oracle数据库,那么还需要导入c3p0-oracle-thin-extras-0.9.2-pre1.jar
2. 在类目录下加入C3P0的配置文件:c3p0-config.xml
c3p0-config.xml的配置信息如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <!--
- c3p0-config.xml必须位于类路径下面
- private static ComboPooledDataSource ds;
- static{
- try {
- ds = new ComboPooledDataSource("MySQL");
- } catch (Exception e) {
- throw new ExceptionInInitializerError(e);
- }
- }
- -->
-
- <c3p0-config>
- <!--
- C3P0的缺省(默认)配置,
- 如果在代码中“ComboPooledDataSource ds = new ComboPooledDataSource();”这样写就表示使用的是C3P0的缺省(默认)配置信息来创建数据源
- -->
- <default-config>
- <property name="driverClass">com.mysql.jdbc.Driver</property>
- <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy</property>
- <property name="user">root</property>
- <property name="password">XDP</property>
-
- <property name="acquireIncrement">5</property>
- <property name="initialPoolSize">10</property>
- <property name="minPoolSize">5</property>
- <property name="maxPoolSize">20</property>
- </default-config>
-
- <!--
- C3P0的命名配置,
- 如果在代码中“ComboPooledDataSource ds = new ComboPooledDataSource("MySQL");”这样写就表示使用的是name是MySQL的配置信息来创建数据源
- -->
- <named-config name="MySQL">
- <property name="driverClass">com.mysql.jdbc.Driver</property>
- <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy</property>
- <property name="user">root</property>
- <property name="password">XDP</property>
-
- <property name="acquireIncrement">5</property>
- <property name="initialPoolSize">10</property>
- <property name="minPoolSize">5</property>
- <property name="maxPoolSize">20</property>
- </named-config>
-
- </c3p0-config>
如下图所示:
3. 在获取数据库连接的工具类(如jdbcUtils)的静态代码块中创建池
- package me.gacl.util;
-
- import java.sql.Connection;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Statement;
- import com.mchange.v2.c3p0.ComboPooledDataSource;
-
- /**
- * @ClassName: JdbcUtils_C3P0
- * @Description: 数据库连接工具类
- * @author: 孤傲苍狼
- * @date: 2014-10-4 下午6:04:36
- *
- */
- public class JdbcUtils_C3P0 {
-
- private static ComboPooledDataSource ds = null;
- //在静态代码块中创建数据库连接池
- static{
- try{
- //通过代码创建C3P0数据库连接池
- /*ds = new ComboPooledDataSource();
- ds.setDriverClass("com.mysql.jdbc.Driver");
- ds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbcstudy");
- ds.setUser("root");
- ds.setPassword("XDP");
- ds.setInitialPoolSize(10);
- ds.setMinPoolSize(5);
- ds.setMaxPoolSize(20);*/
-
- //通过读取C3P0的xml配置文件创建数据源,C3P0的xml配置文件c3p0-config.xml必须放在src目录下
- //ds = new ComboPooledDataSource();//使用C3P0的默认配置来创建数据源
- ds = new ComboPooledDataSource("MySQL");//使用C3P0的命名配置来创建数据源
-
- }catch (Exception e) {
- throw new ExceptionInInitializerError(e);
- }
- }
-
- /**
- * @Method: getConnection
- * @Description: 从数据源中获取数据库连接
- * @Anthor:孤傲苍狼
- * @return Connection
- * @throws SQLException
- */
- public static Connection getConnection() throws SQLException{
- //从数据源中获取数据库连接
- return ds.getConnection();
- }
-
- /**
- * @Method: release
- * @Description: 释放资源,
- * 释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
- * @Anthor:孤傲苍狼
- *
- * @param conn
- * @param st
- * @param rs
- */
- public static void release(Connection conn,Statement st,ResultSet rs){
- if(rs!=null){
- try{
- //关闭存储查询结果的ResultSet对象
- rs.close();
- }catch (Exception e) {
- e.printStackTrace();
- }
- rs = null;
- }
- if(st!=null){
- try{
- //关闭负责执行SQL命令的Statement对象
- st.close();
- }catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- if(conn!=null){
- try{
- //将Connection连接对象还给数据库连接池
- conn.close();
- }catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
测试C3P0数据源
- package me.gacl.test;
-
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import org.junit.Test;
- import me.gacl.util.JdbcUtils_C3P0;
- import me.gacl.util.JdbcUtils_DBCP;
-
- public class DataSourceTest {
-
- @Test
- public void c3p0DataSourceTest() {
- Connection conn = null;
- PreparedStatement st = null;
- ResultSet rs = null;
- try{
- //获取数据库连接
- conn = JdbcUtils_C3P0.getConnection();
- String sql = "insert into test1(name) values(?)";
- st = conn.prepareStatement(sql);
- st.setString(1, "gacl");
- st.executeUpdate();
- //获取数据库自动生成的主键
- rs = st.getGeneratedKeys();
- if(rs.next()){
- System.out.println(rs.getInt(1));
- }
- }catch (Exception e) {
- e.printStackTrace();
- }finally{
- //释放资源
- JdbcUtils_C3P0.release(conn, st, rs);
- }
- }
- }
在实际开发中,我们有时候还会使用服务器提供给我们的数据库连接池,比如我们希望Tomcat服务器在启动的时候可以帮我们创建一个数据库连接池,那么我们在应用程序中就不需要手动去创建数据库连接池,直接使用Tomcat服务器创建好的数据库连接池即可。要想让Tomcat服务器在启动的时候帮我们创建一个数据库连接池,那么需要简单配置一下Tomcat服务器。
JNDI(Java Naming and Directory Interface),Java命名和目录接口,它对应于J2SE中的javax.naming包,
这 套API的主要作用在于:它可以把Java对象放在一个容器中(JNDI容器),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需 通过名称检索即可。其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。
Tomcat服务器创建的数据源是以JNDI资源的形式发布的,所以说在Tomat服务器中配置一个数据源实际上就是在配置一个JNDI资源,通过查看Tomcat文档,我们知道使用如下的方式配置tomcat服务器的数据源:
- <Context>
- <Resource name="jdbc/datasource" auth="Container"
- type="javax.sql.DataSource" username="root" password="XDP"
- driverClassName="com.mysql.jdbc.Driver"
- url="jdbc:mysql://localhost:3306/jdbcstudy"
- maxActive="8" maxIdle="4"/>
- </Context>
服务器创建好数据源之后,我们的应用程序又该怎么样得到这个数据源呢,Tomcat服务器创建好数据源之后是以JNDI的形式绑定到一个JNDI容器中的,我们可以把JNDI想象成一个大大的容器,我们可以往这个容器中存放一些对象,一些资源,JNDI容器中存放的对象和资源都会有一个独一无二的名称,应用程序想从JNDI容器中获取资源时,只需要告诉JNDI容器要获取的资源的名称,JNDI根据名称去找到对应的资源后返回给应用程序。我们平时做javaEE开发时,服务器会为我们的应用程序创建很多资源,比如request对象,response对象,服务器创建的这些资源有两种方式提供给我们的应用程序使用:第一种是通过方法参数的形式传递进来,比如我们在Servlet中写的doPost和doGet方法中使用到的request对象和response对象就是服务器以参数的形式传递给我们的。第二种就是JNDI的方式,服务器把创建好的资源绑定到JNDI容器中去,应用程序想要使用资源时,就直接从JNDI容器中获取相应的资源即可。
对于上面的name="jdbc/datasource"数据源资源,在应用程序中可以用如下的代码去获取
- Context initCtx = new InitialContext();
- Context envCtx = (Context) initCtx.lookup("java:comp/env");
- dataSource = (DataSource)envCtx.lookup("jdbc/datasource");
此种配置下,数据库的驱动jar文件需放置在tomcat的lib下
1) 在Web项目的WebRoot目录下的META-INF目录创建一个context.xml文件
如下图所示:
2) 在context.xml文件配置tomcat服务器的数据源
- <Context>
- <Resource
- name="jdbc/datasource"
- auth="Container"
- type="javax.sql.DataSource"
- username="root"
- password="XDP"
- driverClassName="com.mysql.jdbc.Driver"
- url="jdbc:mysql://localhost:3306/jdbcstudy"
- maxActive="8"
- maxIdle="4"/>
- </Context>
3) 将数据库的驱动jar文件需放置在tomcat的lib下
4) 在获取数据库连接的工具类(如jdbcUtils)的静态代码块中获取JNDI容器中的数据源
- package me.gacl.util;
-
- import java.sql.Connection;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Statement;
- import javax.naming.Context;
- import javax.naming.InitialContext;
- import javax.sql.DataSource;
-
- /**
- * @ClassName: JdbcUtils_DBCP
- * @Description: 数据库连接工具类
- * @author: 孤傲苍狼
- * @date: 2014-10-4 下午6:04:36
- *
- */
- public class JdbcUtils_JNDI {
-
- private static DataSource ds = null;
- //在静态代码块中创建数据库连接池
- static{
- try{
- //初始化JNDI
- Context initCtx = new InitialContext();
- //得到JNDI容器
- Context envCtx = (Context) initCtx.lookup("java:comp/env");
- //从JNDI容器中检索name为jdbc/datasource的数据源
- ds = (DataSource)envCtx.lookup("jdbc/datasource");
- }catch (Exception e) {
- throw new ExceptionInInitializerError(e);
- }
- }
-
- /**
- * @Method: getConnection
- * @Description: 从数据源中获取数据库连接
- * @Anthor:孤傲苍狼
- * @return Connection
- * @throws SQLException
- */
- public static Connection getConnection() throws SQLException{
- //从数据源中获取数据库连接
- return ds.getConnection();
- }
-
- /**
- * @Method: release
- * @Description: 释放资源,
- * 释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
- * @Anthor:孤傲苍狼
- *
- * @param conn
- * @param st
- * @param rs
- */
- public static void release(Connection conn,Statement st,ResultSet rs){
- if(rs!=null){
- try{
- //关闭存储查询结果的ResultSet对象
- rs.close();
- }catch (Exception e) {
- e.printStackTrace();
- }
- rs = null;
- }
- if(st!=null){
- try{
- //关闭负责执行SQL命令的Statement对象
- st.close();
- }catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- if(conn!=null){
- try{
- //将Connection连接对象还给数据库连接池
- conn.close();
- }catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- }
写一个Servlet测试JNDI数据源
- package me.gacl.test;
-
- import java.io.IOException;
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import me.gacl.util.JdbcUtils_JNDI;
-
- public class JNDITest extends HttpServlet {
-
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- Connection conn = null;
- PreparedStatement st = null;
- ResultSet rs = null;
- try{
- //获取数据库连接
- conn = JdbcUtils_JNDI.getConnection();
- String sql = "insert into test1(name) values(?)";
- st = conn.prepareStatement(sql);
- st.setString(1, "gacl");
- st.executeUpdate();
- //获取数据库自动生成的主键
- rs = st.getGeneratedKeys();
- if(rs.next()){
- System.out.println(rs.getInt(1));
- }
- }catch (Exception e) {
- e.printStackTrace();
- }finally{
- //释放资源
- JdbcUtils_JNDI.release(conn, st, rs);
- }
- }
-
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doGet(request, response);
- }
-
- }
DBUtils是apache公司写的。DBUtils是Java 编程中的数据库操作实用工具,小巧简单实用。
DBUtils封装了对JDBC的操作,简化了JDBC操作。可以少写代码。
1)对于数据表的读操作,他可以把结果转换成List,Array,Set等Java 集合,便于程序员操作
2)对于数据表的写操作,也变得很简单(只需写sql语句)
3)可以使用数据源,使用JNDI,数据库连接池等技术来优化性能----重用已经构建好的数据库连接对象
1)QueryRunner类
QueryRunner中提供对sql语句操作的API.它主要有三个方法:query() 用于执行select,update() 用于执行insert update delete,batch() 批处理。等下下面的会详细的介绍这几种方法的用法。
2)ResultSetHandler接口
用于定义select操作后,怎样封装结果集.它总共有9个常用的实现类,下面我会详细的为大家介绍怎么去使用。
3)DbUtils类
它就是一个工具类,定义了关闭资源与事务处理的方法
1)使用步骤
导入相对应的jar包
创建QueryRunner对象
使用query方法执行select语句
使用ResultSetHandler封装结果集
使用DbUtils类释放资源
2)示例,(注:本人使用的是C3P0连接池),具体代码如下:
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.util.ArrayList;
- import java.util.List;
-
- import org.apache.commons.dbutils.QueryRunner;
- import org.apache.commons.dbutils.ResultSetHandler;
- import org.apache.commons.dbutils.handlers.BeanListHandler;
- import org.junit.Test;
-
- import com.jxlg.domain.User;
-
- public class TestSelect {
-
- @Test
- public void testSelect(){
- //创建一个QueryRunner对象
- QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
- try {
- // new ResultSetHandler<List<User>>告诉我们如何封装结果集
- List<User> list = qr.query("select * from user", new ResultSetHandler<List<User>>(){
- @Override
- //query语句执行select语句后,结果一返回值的形式传递过来
- public List<User> handle(ResultSet rs) throws SQLException {
- List<User> list = new ArrayList<User>();
- while(rs.next()){
- User u = new User();
- u.setId(rs.getInt(1));
- u.setUsername(rs.getString(2));
- u.setPassword(rs.getString(3));
- u.setEmail(rs.getString(4));
- u.setBirthday(rs.getDate(5));
- list.add(u);
- }
- return list;
- }
-
- });
- for (User user : list) {
- System.out.println(user);
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
-
- }
- @Test
- public void testSelect2(){
- //创建一个QueryRunner对象
- QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
- try {
- //执行sql语句,返回结果
- List<User> list = qr.query("select * from user where id=? and username=?", new BeanListHandler<User>(User.class),1,"tom");
- for (User user : list) {
- System.out.println(user);
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
-
- }
- }
1)QueryRunner对象
构造函数
new QueryRunner(); 它的事务可以手动控制。
也就是说此对象调用的方法(如:query、update、batch)参数中要有Connection对象。
new QueryRunner(DataSource ds); 它的事务是自动控制的。一个sql一个事务。
此对象调用的方法(如:query、update、batrch)参数中无需Connection对象。
常用方法
2)ResultSetHandler接口
它有9个结果处理器
ArrayHandler:适合取1条记录。把该条记录的每列值封装到一个数组中Object[]
ArrayListHandler:适合取多条记录。把每条记录的每列值封装到一个数组中Object[],把数组封装到一个List中
ColumnListHandler:取某一列的数据。封装到List中。
KeyedHandler:取多条记录,每一条记录封装到一个Map中,再把这个Map封装到另外一个Map中,key为指定的字段值。
MapHandler:适合取1条记录。把当前记录的列名和列值放到一个Map中
MapListHandler:适合取多条记录。把每条记录封装到一个Map中,再把Map封装到List中
ScalarHandler:适合取单行单列数据
BeanHandler
BeanListHandler
示例,代码如下:
- import static org.junit.Assert.*;
- import java.sql.SQLException;
- import java.util.List;
- import java.util.Map;
- import java.util.Map.Entry;
- import org.apache.commons.dbutils.QueryRunner;
- import org.apache.commons.dbutils.handlers.ArrayHandler;
- import org.apache.commons.dbutils.handlers.ArrayListHandler;
- import org.apache.commons.dbutils.handlers.BeanHandler;
- import org.apache.commons.dbutils.handlers.ColumnListHandler;
- import org.apache.commons.dbutils.handlers.KeyedHandler;
- import org.apache.commons.dbutils.handlers.MapHandler;
- import org.apache.commons.dbutils.handlers.MapListHandler;
- import org.apache.commons.dbutils.handlers.ScalarHandler;
- import org.junit.Test;
- import com.jxlg.domain.User;
-
- public class TestResultSetHandler {
- @Test
- public void test1() {
- //ArrayHandler:适合取1条记录。把该条记录的每列值封装到一个数组中Object[]
- QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
- try {
- Object[] o = qr.query("select * from user where id=?", new ArrayHandler(),5);
- for (Object object : o) {
- System.out.println(object);
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
-
- @Test
- public void test2() throws SQLException {
- //ArrayListHandler:适合取多条记录。把每条记录的每列值封装到一个数组中Object[],把数组封装到一个List中
- QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
- List<Object[]> list = qr.query("select * from user", new ArrayListHandler());
- for (Object[] objects : list) {
- for (Object object : objects) {
- System.out.println(object);
- }
- System.out.println("----------------------");
- }
- }
-
- @Test
- public void test3() throws SQLException {
- //ColumnListHandler:取某一列的数据。封装到List中
- QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
- List<Object> list = qr.query("select username,password from user ", new ColumnListHandler(1));
- for (Object object : list) {
- System.out.println(object);
- }
- }
-
- @Test
- public void test4() throws SQLException {
- //KeyedHandler:取多条记录,每一条记录封装到一个Map中,
- //再把这个Map封装到另外一个Map中,key为指定的字段值。
- QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
- //大的Map的key是表中的某列数据,小的Map的key是表的列名,所以大的map的key用的是Object类型,小的是String。
- Map<Object, Map<String, Object>> map = qr.query("select * from user", new KeyedHandler(1));
- for (Map.Entry<Object, Map<String,Object>> m : map.entrySet()) {
- System.out.println(m);//就是id至,因为设置了“1”.
- for (Map.Entry<String, Object> mm : m.getValue().entrySet()) {
- System.out.println(mm);//取出小map中的key和value
- }
- System.out.println("--------------------");
- }
- }
-
- @Test
- public void test5() throws SQLException {
- //MapHandler:适合取1条记录。把当前记录的列名和列值放到一个Map中
- QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
- Map<String, Object> map = qr.query("select * from user", new MapHandler());
- for (Map.Entry<String, Object> m : map.entrySet()) {
- System.out.println(m.getKey()+"\t"+m.getValue());
- //默认取第一行数据,需要去其它行用where加条件
- }
- }
-
- @Test
- public void test6() throws SQLException {
- //MapListHandler:适合取多条记录。把每条记录封装到一个Map中,再把Map封装到List中
- QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
- List<Map<String, Object>> list = qr.query("select * from user", new MapListHandler());
-
- for (Map<String, Object> map : list) {
- for (Map.Entry<String, Object> m : map.entrySet()) {
- System.out.println(m);
- }
- System.out.println("-----------");
- }
- }
-
- @Test
- public void test7() throws SQLException {
- //ScalarHandler:适合取单行单列数据
- QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
- Object o = qr.query("select * from user", new ScalarHandler(2));
- System.out.println(o);
- }
-
- @Test
- public void test8() throws SQLException {
- //BeanHandler:适合取单行单列数据
- QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
- User user = qr.query("select * from user", new BeanHandler<User>(User.class));
- System.out.println(user);
- }
- }
使用DBUtils做一个增删改查的例子
- import static org.junit.Assert.*;
- import java.sql.SQLException;
- import java.util.Date;
- import javax.crypto.spec.OAEPParameterSpec;
- import org.apache.commons.dbutils.QueryRunner;
- import org.junit.Test;
-
- public class TestInCURD {
-
- @Test
- public void testInsert() {
- //创建一个QueryRunner对象
- QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
- try {
- qr.update("insert into user (username,password,email,birthday)values(?,?,?,?)", "guapi","4646","guapi@163.com",new Date());
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- @Test
- public void testUpdate() {
- //创建一个QueryRunner对象
- QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
- try {
- qr.update("update user set username=?,password=? where id=4 ", "meizimeizi","520520");
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
-
- @Test
- public void testDelete() {
- //创建一个QueryRunner对象
- QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
- try {
- qr.update("delete from user where id=? ",4);
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
-
- @Test
- public void testBatch() {
- //创建一个QueryRunner对象
- QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
- try {
- Object[][] params = new Object[10][]; //高维代表执行多少次sql语句
- for(int i =0;i<params.length;i++){
- params[i] =new Object[]{"guapi"+i,"4646","guapi@163.com",new Date()};
- }
-
- qr.batch("insert into user (username,password,email,birthday)values(?,?,?,?)", params );
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
---------------------------------------------------------------------- 我是低调的分隔线 --------------------------------------------------------------------------
吾欲之南海,一瓶一钵足矣...
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。