zoukankan      html  css  js  c++  java
  • JDBC连接池和DBUtils

    本节内容:

    • JDBC连接池
    • DBUtils

    一、JDBC连接池

    实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程,为了解决此类性能问题,通常情况我们采取连接池技术,来共享连接Connection。在程序开始的时候,可以创建几个连接,将连接放入到连接池中。用户使用连接的时候,可以从连接池中进行获取。用完之后,可以将连接归还连接池。

    连接池主要解决的是性能问题。

    1. 连接池概述

    (1)概念

    用池来管理Connection,这样可以重复使用Connection。有了池,我们就不用自己来创建Connection,而是通过池来获取Connection对象。当使用完Connection后,调用Connection的close()方法也不会真的关闭Connection,而是把Connection“归还”给池。池就可以再利用这个Connection对象了。

    (2)规范

    Java为数据库连接池提供了公共的接口,javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便地切换不同厂商的连接池。

    常见的连接池:DBCP、C3P0。还有tomcat自带的JNDI,JNDI使用的概率极低。市场上使用率最高的是C3P0。

    2. 自定义连接池(了解)

    定义一个连接池实现javax.sql.DataSource,使用List集合存放多个连接对象。如果我们需要编写自定义连接池,需要完成以下步骤:

    1. 创建连接池(作为数据源),需要实现接口javax.sql.DataDource。因为我们只使用该接口中getConnection()方法,简化本案例,我们只实现javax.sql.DataDource中的getConnection()方法,而没有实现javax.sql.DataDource中的其他接口
    2. 提供一个集合,用于存放连接,因为移除/添加操作过多,所以选择LinkedList
    3. 本案例在静态代码块中,为连接池初始化5个连接(本案例中初始化连接使用上一篇文章中实现的工具类)
    4. 之后程序如果需要连接,调用实现类的getConnection(),该方法将从连接池(容器List)获得连接。为了保证当前连接只能提供给一个线程使用,所以我们需要将连接先从连接池中移除。
    5. 当用户使用完连接,释放资源时,不执行close()方法,而是将连接添加到连接池中。

    MyDataSource.java

    package cn.itheima.jdbc.DataSource;
    
    import java.io.PrintWriter;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.sql.SQLFeatureNotSupportedException;
    import java.util.LinkedList;
    import java.util.logging.Logger;
    
    import javax.sql.DataSource;
    
    import cn.itheima.jdbc.utils.JDBCUtils_V3;
    
    public class MyDataSource implements DataSource{
    	//1.创建1个容器用于存储Connection对象
    	private static LinkedList<Connection> pool = new LinkedList<Connection>();
    	
    	//2.创建5个连接放到容器中去
    	static{
    		for (int i = 0; i < 5; i++) {
    			Connection conn = JDBCUtils_V3.getConnection();
    			pool.add(conn);
    		}
    	}
    	
    	/**
    	 * 重写获取连接的方法
    	 */
    	@Override
    	public Connection getConnection() throws SQLException {
    		Connection conn = null;
    		//3.使用前先判断
    		if(pool.size()==0){
    			//4.池子里面没有,我们再创建一些
    			for (int i = 0; i < 5; i++) {
    				conn = JDBCUtils_V3.getConnection();
    				pool.add(conn);
    			}
    		}
    		//5.从池子里面获取一个连接对象Connection
    		conn = pool.remove(0);
    		return conn;
    	}
    
    	/**
    	 * 归还连接对象到连接池中去
    	 */
    	public void backConnection(Connection conn){
    		pool.add(conn);
    	}
    
    	@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 Logger getParentLogger() throws SQLFeatureNotSupportedException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@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;
    	}
    
    	@Override
    	public Connection getConnection(String username, String password) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    }

    TestMyDataSource.java

    package cn.itheima.jdbc.test;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    
    import javax.sql.DataSource;
    
    import org.junit.Test;
    
    import cn.itheima.jdbc.DataSource.MyDataSource;
    import cn.itheima.jdbc.DataSource.MyDataSource1;
    import cn.itheima.jdbc.utils.JDBCUtils_V3;
    
    public class TestMyDataSource {
    	/**
    	 * 添加用户
    	 * 使用未改造过的connection
    	 */
    	@Test
    	public void testAddUser() {
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		// 1.创建自定义连接池对象
    		MyDataSource dataSource = new MyDataSource();
    		try {
    			// 2.从池子中获取连接
    			conn = dataSource.getConnection();
    			String sql = "insert into user values(null,?,?)";
    			pstmt = conn.prepareStatement(sql);
    			pstmt.setString(1, "吕布");
    			pstmt.setString(2, "貂蝉");
    			int rows = pstmt.executeUpdate();
    			if (rows > 0) {
    				System.out.println("添加成功!");
    			} else {
    				System.out.println("添加失败!");
    			}
    		} catch (Exception e) {
    			throw new RuntimeException(e);
    		} finally {
    			dataSource.backConnection(conn);
    		}
    	}
    	
    }

    JDBCUtils_V3.java

    package cn.itheima.jdbc.utils;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.Properties;
    import java.util.ResourceBundle;
    
    /**
     * 提供获取连接和释放资源的 方法
     * 
     * @author Never Say Never
     * @date 2017年10月29日
     * @version V1.0
     */
    public class JDBCUtils_V3 {
    	private static String driver;
    	private static String url;
    	private static String username;
    	private static String password;
    
    	/**
    	 * 静态代码块加载配置文件信息
    	 */
    	static {
    		try {
    			// 1.通过当前类获取类加载器
    			ClassLoader classLoader = JDBCUtils_V3.class.getClassLoader();
    			// 2.通过类加载器的方法获得一个输入流
    			InputStream is = classLoader.getResourceAsStream("db.properties");
    			// 3.创建一个properties对象
    			Properties props = new Properties();
    			// 4.加载输入流
    			props.load(is);
    			// 5.获取相关参数的值
    			driver = props.getProperty("driver");
    			url = props.getProperty("url");
    			username = props.getProperty("username");
    			password = props.getProperty("password");
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    
    	}
    
    	/**
    	 * 获取连接方法
    	 * 
    	 * @return
    	 */
    	public static Connection getConnection() {
    		Connection conn = null;
    		try {
    			Class.forName(driver);
    			conn = DriverManager.getConnection(url, username, password);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return conn;
    	}
    
    	/**
    	 * 释放资源方法
    	 * 
    	 * @param conn
    	 * @param pstmt
    	 * @param rs
    	 */
    	public static void release(Connection conn, PreparedStatement pstmt, ResultSet rs) {
    		if (rs != null) {
    			try {
    				rs.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    		}
    		if (pstmt != null) {
    			try {
    				pstmt.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    		}
    		if (conn != null) {
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    		}
    
    	}
    }

    3. 自定义连接池(方法增强)

    上面的自定义连接池示例中存在严重问题,用户调用getConnection()获得连接后,必须使用backConnection(conn)方法进行连接的归还,如果用户调用conn.close()将连接真正的释放,连接池中将出现无连接可用。

    我们希望用户即使调用了close()方法,连接仍归还给连接池。我们需要对close()方法进行增强,从而实现将连接归还给连接池的功能。

    (1)方法增强的方式

    • 继承:子类继承父类,将父类的方法进行重写,从而进行增强
      • 使用前提:必须有父类,且存在继承关系。
    • 装饰者设计模式,此设计模式专门用于增强方法
      • 使用前提:必须有接口
      • 缺点:需要将接口的所有方法都实现
    • 动态代理:在运行时动态的创建代理类,完成增强操作,与装饰者类似。
      • 使用前提:必须有接口
      • 难点:需要反射技术
    • 字节码增强,运行时创建目标类子类,从而进行增强
      • 常见第三方框架:cglib、javassist等

    (2)装饰者设计模式

    设计模式:专门为解决某一类问题,而编写的固定格式的代码。

    装饰者固定结构:接口A,已知实现类C,需要装饰者创建代理类B

    1. 创建类B,并实现接口A
    2. 提供类B的构造方法,参数类型A,用于接收A接口的其他实现类(C)
    3. 给类B添加类型为A成员变量,用于存放A接口的其他实现类
    4. 增强需要的方法
    5. 实现不需要增强的方法,方法里调用成员变量存放的其他实现类对应的方法

    MyConnection.java

    package cn.itheima.jdbc.DataSource;
    
    import java.sql.Array;
    import java.sql.Blob;
    import java.sql.CallableStatement;
    import java.sql.Clob;
    import java.sql.Connection;
    import java.sql.DatabaseMetaData;
    import java.sql.NClob;
    import java.sql.PreparedStatement;
    import java.sql.SQLClientInfoException;
    import java.sql.SQLException;
    import java.sql.SQLWarning;
    import java.sql.SQLXML;
    import java.sql.Savepoint;
    import java.sql.Statement;
    import java.sql.Struct;
    import java.util.LinkedList;
    import java.util.Map;
    import java.util.Properties;
    import java.util.concurrent.Executor;
    
    //1.实现同一个接口Connection
    public class MyConnection implements Connection {
    	//3.定义一个变量
    	private Connection conn;
    	
    	private LinkedList<Connection> pool;
    	
    	// 2.编写一个构造方法(参数使用了面相对象的多态特性)
    	public MyConnection(Connection conn,LinkedList<Connection> pool) {
    		this.conn=conn;
    		this.pool=pool;
    	}
    	
    	//4.书写需要增强的方法
    	@Override
    	public void close() throws SQLException {
    		pool.add(conn);
    	}
    
    	/**
    	 * 此方法必须覆盖!否则会出现空指针异常!!!
    	 */
    	@Override
    	public PreparedStatement prepareStatement(String sql) throws SQLException {
    		return conn.prepareStatement(sql);
    	}
    	
    	@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;
    	}
    
    	@Override
    	public Statement createStatement() throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public CallableStatement prepareCall(String sql) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public String nativeSQL(String sql) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public void setAutoCommit(boolean autoCommit) throws SQLException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public boolean getAutoCommit() throws SQLException {
    		// TODO Auto-generated method stub
    		return false;
    	}
    
    	@Override
    	public void commit() throws SQLException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public void rollback() throws SQLException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public boolean isClosed() throws SQLException {
    		// TODO Auto-generated method stub
    		return false;
    	}
    
    	@Override
    	public DatabaseMetaData getMetaData() throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public void setReadOnly(boolean readOnly) throws SQLException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public boolean isReadOnly() throws SQLException {
    		// TODO Auto-generated method stub
    		return false;
    	}
    
    	@Override
    	public void setCatalog(String catalog) throws SQLException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public String getCatalog() throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public void setTransactionIsolation(int level) throws SQLException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public int getTransactionIsolation() throws SQLException {
    		// TODO Auto-generated method stub
    		return 0;
    	}
    
    	@Override
    	public SQLWarning getWarnings() throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public void clearWarnings() throws SQLException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
    			throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public Map<String, Class<?>> getTypeMap() throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public void setHoldability(int holdability) throws SQLException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public int getHoldability() throws SQLException {
    		// TODO Auto-generated method stub
    		return 0;
    	}
    
    	@Override
    	public Savepoint setSavepoint() throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public Savepoint setSavepoint(String name) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public void rollback(Savepoint savepoint) throws SQLException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public void releaseSavepoint(Savepoint savepoint) throws SQLException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
    			throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
    			int resultSetHoldability) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
    			int resultSetHoldability) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public Clob createClob() throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public Blob createBlob() throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public NClob createNClob() throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public SQLXML createSQLXML() throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public boolean isValid(int timeout) throws SQLException {
    		// TODO Auto-generated method stub
    		return false;
    	}
    
    	@Override
    	public void setClientInfo(String name, String value) throws SQLClientInfoException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public void setClientInfo(Properties properties) throws SQLClientInfoException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public String getClientInfo(String name) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public Properties getClientInfo() throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public void setSchema(String schema) throws SQLException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public String getSchema() throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public void abort(Executor executor) throws SQLException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public int getNetworkTimeout() throws SQLException {
    		// TODO Auto-generated method stub
    		return 0;
    	}
    
    }

    MyDataSource1.java

    package cn.itheima.jdbc.DataSource;
    
    import java.io.PrintWriter;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.sql.SQLFeatureNotSupportedException;
    import java.util.LinkedList;
    import java.util.logging.Logger;
    
    import javax.sql.DataSource;
    
    import cn.itheima.jdbc.utils.JDBCUtils_V3;
    
    public class MyDataSource1 implements DataSource{
    	//1.创建1个容器用于存储Connection对象
    	private static LinkedList<Connection> pool = new LinkedList<Connection>();
    	
    	//2.创建5个连接放到容器中去
    	static{
    		for (int i = 0; i < 5; i++) {
    			Connection conn = JDBCUtils_V3.getConnection();
    			//放入池子中connection对象已经经过改造了
    			MyConnection myconn = new MyConnection(conn, pool);
    			pool.add(myconn);
    		}
    	}
    	
    	/**
    	 * 重写获取连接的方法
    	 */
    	@Override
    	public Connection getConnection() throws SQLException {
    		Connection conn = null;
    		//3.使用前先判断
    		if(pool.size()==0){
    			//4.池子里面没有,我们再创建一些
    			for (int i = 0; i < 5; i++) {
    				conn = JDBCUtils_V3.getConnection();
    				//放入池子中connection对象已经经过改造了
    				MyConnection myconn = new MyConnection(conn, pool);
    				pool.add(myconn);
    			}
    		}
    		//5.从池子里面获取一个连接对象Connection
    		conn = pool.remove(0);
    		return conn;
    	}
    
    	/**
    	 * 归还连接对象到连接池中去
    	 */
    	public void backConnection(Connection conn){
    		pool.add(conn);
    	}
    
    	@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 Logger getParentLogger() throws SQLFeatureNotSupportedException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@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;
    	}
    
    	@Override
    	public Connection getConnection(String username, String password) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    }
    

    TestMyDataSource.java

    package cn.itheima.jdbc.test;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    
    import javax.sql.DataSource;
    
    import org.junit.Test;
    
    import cn.itheima.jdbc.DataSource.MyDataSource;
    import cn.itheima.jdbc.DataSource.MyDataSource1;
    import cn.itheima.jdbc.utils.JDBCUtils_V3;
    
    public class TestMyDataSource {
    	/**
    	 * 添加用户
    	 * 使用未改造过的connection
    	 */
    	@Test
    	public void testAddUser() {
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		// 1.创建自定义连接池对象
    		MyDataSource dataSource = new MyDataSource();
    		try {
    			// 2.从池子中获取连接
    			conn = dataSource.getConnection();
    			String sql = "insert into user values(null,?,?)";
    			pstmt = conn.prepareStatement(sql);
    			pstmt.setString(1, "吕布");
    			pstmt.setString(2, "貂蝉");
    			int rows = pstmt.executeUpdate();
    			if (rows > 0) {
    				System.out.println("添加成功!");
    			} else {
    				System.out.println("添加失败!");
    			}
    		} catch (Exception e) {
    			throw new RuntimeException(e);
    		} finally {
    			dataSource.backConnection(conn);
    		}
    	}
    	
    	
    	/**
    	 * 添加用户
    	 * 使用改造过的connection
    	 */
    	@Test
    	public void testAddUser1() {
    		Connection conn = null;
    		PreparedStatement pstmt = null;
    		// 1.创建自定义连接池对象
    		DataSource dataSource = new MyDataSource1();
    		try {
    			// 2.从池子中获取连接
    			conn = dataSource.getConnection();
    			String sql = "insert into user values(null,?,?)";
    			//3.必须在自定义的connection类中重写prepareStatement(sql)方法
    			pstmt = conn.prepareStatement(sql);
    			pstmt.setString(1, "吕布1");
    			pstmt.setString(2, "貂蝉1");
    			int rows = pstmt.executeUpdate();
    			if (rows > 0) {
    				System.out.println("添加成功!");
    			} else {
    				System.out.println("添加失败!");
    			}
    		} catch (Exception e) {
    			throw new RuntimeException(e);
    		} finally {
    			JDBCUtils_V3.release(conn, pstmt, null);
    		}
    	}
    }

    JDBCUtils_V3.java

    package cn.itheima.jdbc.utils;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.Properties;
    import java.util.ResourceBundle;
    
    /**
     * 提供获取连接和释放资源的 方法
     * 
     * @author Never Say Never
     * @date 2017年10月29日
     * @version V1.0
     */
    public class JDBCUtils_V3 {
    	private static String driver;
    	private static String url;
    	private static String username;
    	private static String password;
    
    	/**
    	 * 静态代码块加载配置文件信息
    	 */
    	static {
    		try {
    			// 1.通过当前类获取类加载器
    			ClassLoader classLoader = JDBCUtils_V3.class.getClassLoader();
    			// 2.通过类加载器的方法获得一个输入流
    			InputStream is = classLoader.getResourceAsStream("db.properties");
    			// 3.创建一个properties对象
    			Properties props = new Properties();
    			// 4.加载输入流
    			props.load(is);
    			// 5.获取相关参数的值
    			driver = props.getProperty("driver");
    			url = props.getProperty("url");
    			username = props.getProperty("username");
    			password = props.getProperty("password");
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    
    	}
    
    	/**
    	 * 获取连接方法
    	 * 
    	 * @return
    	 */
    	public static Connection getConnection() {
    		Connection conn = null;
    		try {
    			Class.forName(driver);
    			conn = DriverManager.getConnection(url, username, password);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return conn;
    	}
    
    	/**
    	 * 释放资源方法
    	 * 
    	 * @param conn
    	 * @param pstmt
    	 * @param rs
    	 */
    	public static void release(Connection conn, PreparedStatement pstmt, ResultSet rs) {
    		if (rs != null) {
    			try {
    				rs.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    		}
    		if (pstmt != null) {
    			try {
    				pstmt.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    		}
    		if (conn != null) {
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    		}
    
    	}
    }

    4. C3P0连接池

    C3P0开源免费的连接池。目前使用它的开源项目有:Spring、Hibernate等。使用第三方工具需要导入jar包,c3p0使用时可以添加配置文件c3p0-config.xml(或者c3p0.properties,大部分使用c3p0-config.xml),也可以不添加配置文件。它有两种使用方式,但是多数情况下都是用需要配置文件的那种方式。

    (1)导入jar包

    如果使用的是0.9.2版本,需要导入两个jar包。

    如果使用的是0.9.1版本,只需要导入c3p0-0.9.1.2.jar。

    (2)配置文件

    • 配置文件名称:c3p0-config.xml(固定)
    • 配置文件位置:src(类路径)
    • 配置文件内容:命名配置
    <c3p0-config>
    
        <named-config name="itheima">
            <property name="driverClass">com.mysql.jdbc.Driver</property>
            <property name="jdbcUrl">jdbc:mysql:///web08</property>
            <property name="user">root</property>
            <property name="password">123456</property>
        </named-config>
    
    </c3p0-config>
    命名配置
    • 配置文件内容:默认配置
    <c3p0-config>
      <default-config>
        <property name="automaticTestTable">con_test</property>
        <property name="checkoutTimeout">30000</property>
        <property name="idleConnectionTestPeriod">30</property>
        <property name="initialPoolSize">10</property>
        <property name="maxIdleTime">30</property>
        <property name="maxPoolSize">100</property>
        <property name="minPoolSize">10</property>
        <property name="maxStatements">200</property>
    
      </default-config>
    </c3p0-config>
    默认配置

    (3)示例

    c3p0提供核心工具类:ComboPooledDataSource,如果要使用连接池,必须创建该类的实例对象。

    • new ComboPooledDataSource("itcast");  使用配置文件“命名配置”
      • <name-config name="itcast">
    • new ComboPooledDataSource();  使用配置文件“默认配置”
      • <default-config>
    <?xml version="1.0" encoding="UTF-8"?>
    <c3p0-config>
    
        <default-config>
            <property name="driverClass">com.mysql.jdbc.Driver</property>
            <property name="jdbcUrl">jdbc:mysql:///web08</property>
            <property name="user">root</property>
            <property name="password">123456</property>
            <property name="initialPoolSize">5</property>
            <property name="maxPoolSize">20</property>
        </default-config>
    
        <named-config name="itheima">
            <property name="driverClass">com.mysql.jdbc.Driver</property>
            <property name="jdbcUrl">jdbc:mysql:///web08</property>
            <property name="user">root</property>
            <property name="password">123456</property>
        </named-config>
    
    </c3p0-config>
    c3p0-config.xml
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    
    import org.junit.Test;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    import cn.itheima.jdbc.utils.JDBCUtils_V3;
    
    public class TestC3P0 {
        @Test
        public void testAddUser() {
            Connection conn = null;
            PreparedStatement pstmt = null;
            // 1.创建自定义连接池对象
            ComboPooledDataSource dataSource = new ComboPooledDataSource();// 加载默认的配置
            // ComboPooledDataSource dataSource = new ComboPooledDataSource("itheima");//加载有名称的配置
            
            try {
                // 2.从池子中获取连接
                conn = dataSource.getConnection();
                String sql = "insert into tbl_user values(null,?,?)";
                pstmt = conn.prepareStatement(sql);
                pstmt.setString(1, "吕布2");
                pstmt.setString(2, "貂蝉2");
                int rows = pstmt.executeUpdate();
                if (rows > 0) {
                    System.out.println("添加成功!");
                } else {
                    System.out.println("添加失败!");
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                JDBCUtils_V3.release(conn, pstmt, null);
            }
        }
    }
    TestC3P0.java
    import java.io.IOException;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.Properties;
    import java.util.ResourceBundle;
    
    /**
     * 提供获取连接和释放资源的 方法
     * 
     * @author Never Say Never
     * @date 2017年10月29日
     * @version V1.0
     */
    public class JDBCUtils_V3 {
        private static String driver;
        private static String url;
        private static String username;
        private static String password;
    
        /**
         * 静态代码块加载配置文件信息
         */
        static {
            try {
                // 1.通过当前类获取类加载器
                ClassLoader classLoader = JDBCUtils_V3.class.getClassLoader();
                // 2.通过类加载器的方法获得一个输入流
                InputStream is = classLoader.getResourceAsStream("db.properties");
                // 3.创建一个properties对象
                Properties props = new Properties();
                // 4.加载输入流
                props.load(is);
                // 5.获取相关参数的值
                driver = props.getProperty("driver");
                url = props.getProperty("url");
                username = props.getProperty("username");
                password = props.getProperty("password");
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    
        /**
         * 获取连接方法
         * 
         * @return
         */
        public static Connection getConnection() {
            Connection conn = null;
            try {
                Class.forName(driver);
                conn = DriverManager.getConnection(url, username, password);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return conn;
        }
    
        /**
         * 释放资源方法
         * 
         * @param conn
         * @param pstmt
         * @param rs
         */
        public static void release(Connection conn, PreparedStatement pstmt, ResultSet rs) {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (pstmt != null) {
                try {
                    pstmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    JDBCUtils_V3.java

    可以写一个工具类,修改下相关代码:

    import java.sql.Connection;
    import java.sql.SQLException;
    
    import javax.sql.DataSource;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    public class C3P0Utils {
        private static ComboPooledDataSource dataSource = new ComboPooledDataSource("itheima");
    
        public static DataSource getDataSource() {
            return dataSource;
        }
    
        public static Connection getConnection() {
            try {
                return dataSource.getConnection();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
    C3P0Utils.java
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    
    import org.junit.Test;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    import cn.itheima.jdbc.utils.C3P0Utils;
    import cn.itheima.jdbc.utils.JDBCUtils_V3;
    
    public class TestC3P0 {
        @Test
        public void testAddUser1() {
            Connection conn = null;
            PreparedStatement pstmt = null;
            try {
                // 2.从池子中获取连接
                conn = C3P0Utils.getConnection();
                String sql = "insert into tbl_user values(null,?,?)";
                pstmt = conn.prepareStatement(sql);
                pstmt.setString(1, "吕布3");
                pstmt.setString(2, "貂蝉3");
                int rows = pstmt.executeUpdate();
                if (rows > 0) {
                    System.out.println("添加成功!");
                } else {
                    System.out.println("添加失败!");
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                JDBCUtils_V3.release(conn, pstmt, null);
            }
        }
    
    }
    TestC3P0.java

    5. DBCP连接池

    DBCP也是一个开源的连接池,是Apache上的一个java连接池项目,在企业开发中也比较常见,tomcat内置的连接池。

    (1)导入jar包

    commons-dbcp-1.4.jar和commons-pool-1.5.6.jar

    (2)配置文件

    • 配置文件名称:*.properties
    • 配置文件位置:任意,建议src(classpath/类路径)
    • 配置文件内容:properties不能写中文,不支持在STS(Eclipse的一个变种)中修改,必须使用记事本修改内容,否则中文注释就乱码了
    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/web08?useUnicode=true&characterEncoding=utf8
    username=root
    password=123456
    db.properties
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.Properties;
    
    import javax.sql.DataSource;
    
    import org.apache.commons.dbcp.BasicDataSourceFactory;
    
    public class DBCPUtils {
        private static DataSource dataSource;
        static{
            try {
                //1.加载找properties文件输入流
                InputStream is = DBCPUtils.class.getClassLoader().getResourceAsStream("db.properties");
                //2.加载输入流
                Properties props = new Properties();
                props.load(is);
                //3.创建数据源
                dataSource = BasicDataSourceFactory.createDataSource(props);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        
        public static DataSource getDataSource(){
            return dataSource;
        }
        
        public static Connection getConnection(){
            try {
                return dataSource.getConnection();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
    DBCPUtils.java
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    
    import org.junit.Test;
    
    import cn.itheima.jdbc.utils.DBCPUtils;
    
    public class TestDBCP {
    
        @Test
        public void testUpdateUserById(){
            Connection conn = null;
            PreparedStatement pstmt = null;
            try {
                conn = DBCPUtils.getConnection();
                String sql ="update tbl_user set upassword=? where uid=?";
                pstmt= conn.prepareStatement(sql);
                pstmt.setString(1, "柳岩");
                pstmt.setInt(2, 20);
                int rows = pstmt.executeUpdate();
                if(rows>0){
                    System.out.println("更新成功!");
                }else{
                    System.out.println("更新失败!");
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
    TestDBCP.java

    二、DBUtils

    如果在某些项目中,不使用框架,只使用JDBC开发,我们会发现冗余代码过多:

    为了简化JDBC开发,我们采用apache commons组件的一个成员:DBUtils。

    DBUtils就是JDBC的简化开发工具包。需要使用的技术:连接池(只用到里面的获得连接),SQL语句并没有变少以及设置sql语句中的参数。

    1. JavaBean组件

    JavaBean就是一个类,在开发中常用语封装数据。具有如下特性:

    1. 需要实现接口:java.io.Serializable,通常偷懒省略了。
    2. 提供私有字段:private 类型 字段名;
    3. 提供getter/setter方法
    4. 提供无参构造

    一般将JavaBean类放到domain包或model包里。

    2. DBUtils介绍

    (1)概述

    DBUtils封装了对JDBC的操作,简化了JDBC操作,可以少写代码。在不使用框架的情况下,使用DBUtils的概率非常大。

    DBUtils三个核心功能介绍:

    • QueryRunner类,提供对sql语句操作的API
    • ResultSetHandler接口,用于定义select操作后,怎样封装结果集
    • DbUtils类,它就是一个工具类,定义了关闭资源与事务处理的方法

    (2)QueryRunner核心类

    • QueryRunner(Database ds),提供了数据源(连接池),DBUtils底层自动维护connection
    • update(String sql, Object... params),执行更新数据。(增、删、改)
    • query(String sql, ResultSetHandler<T> rsh, Object... params),执行查询

    (3)ResultSetHandler结果处理集类

    我们知道在执行select语句之后得到的是ResultSet,然后我们还需要对ResultSet进行转换,得到最终我们想要的数据。你可能希望把ResultSet的数据放到一个List中,也可能想把数据放到一个Map中,或是一个Bean中。

    DBUtils提供了一个接口ResultSetHandler,它就是用来ResultSet转换成目标类型的工具。你可以自己去实现这个接口,把ResultSet转换成你想要的类型。

    DBUtils提供了很多个ResultSetHandler接口的实现,这些实现已经基本够用了,我们通常不用自己去实现ResultSet接口了。

    • MapHandler:单行处理器!把结果集转换成Map<String,Object>,其中列名为键
    • MapListHandler:多行处理器!把结果集转换成List<Map<String,Object>>;
    • BeanHandler:单行处理器!把结果集转换成Bean,该处理器需要Class参数,即Bean的类型;
    • BeanListHandler:多行处理器!把结果集转换成List<Bean>;
    • ColumnListHandler:多行单列处理器!把结果集转换成List<Object>,使用ColumnListHandler时需要指定某一列的名称或编号,例如:new ColumListHandler(“name”)表示把name列的数据放到List中。
    • ScalarHandler:单行单列处理器!把结果集转换成Object。一般用于聚集查询,例如select count(*) from tab_student。
    ArrayHandler 将结果集中的第一条记录封装到一个Object[]数组中,数组中的每一个元素就是这条记录中的每一个字段的值
    ArrayListHangler 将结果集中的第一条记录封装到一个Object[]数组中,将这些数组在封装到List集合中
    BeanHandler 将结果集中的每一条记录封装到一个指定的javaBean中
    BeanListHandler 将结果集中每一条记录封装到指定的javaBean中,将这些JavaBean在封装到List集合中
    ColumnListHandler 将结果集中指定的列的字段值,封装到一个List集合中
    KeyedHandler 将结果集中每一条记录封装到Map<String,Object>,在将这个map结婚作为另一个Map的value,另一个Map集合的key是指定的字段的名称
    MapHandler 将结果集中第一条记录封装到Map<String,Object>集合中,key就是字段名称,value就是字段值
    MapListHandler 将结果集中每一条记录封装到了Map<String,Object>集合中,key就是字段名称,value就是字段值,在将这些Map封装到List集合中
    ScalarHandler 它用于单数据。例如select count(*) from table 操作

    Map处理器:
      每一条记录作为一个Map,Map的key是字段名称。

    Bean处理器 :
      必须搞一个javaBean。


    Column处理器:


    Scalar处理器:

     

    (4)DbUtils工具类

    • closeQuietly(Connection conn)  关闭连接,如果有异常try后不抛
    • commitAndCloseQuietly(Connection conn)  提交并关闭连接
    • rollbackAndCloseQuietly(Connection conn)  回滚并关闭连接

    3. 示例:使用DBUtils完成增删改查操作

    public class User {
        private int uid;
        private String uname;
        private String upassword;
    
        public User() {
        }
    
        public int getUid() {
            return uid;
        }
    
        public void setUid(int uid) {
            this.uid = uid;
        }
    
        public String getUname() {
            return uname;
        }
    
        public void setUname(String uname) {
            this.uname = uname;
        }
    
        public String getUpassword() {
            return upassword;
        }
    
        public void setUpassword(String upassword) {
            this.upassword = upassword;
        }
    
    }
    User.java
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import javax.sql.DataSource;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    public class C3P0Utils {
        private static ComboPooledDataSource dataSource = new ComboPooledDataSource("itheima");
    
        public static DataSource getDataSource() {
            return dataSource;
        }
    
        public static Connection getConnection() {
            try {
                return dataSource.getConnection();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
    C3P0Utils.java
    import java.sql.SQLException;
    
    import org.apache.commons.dbutils.QueryRunner;
    import org.junit.Test;
    
    import cn.itheima.jdbc.utils.C3P0Utils;
    
    /**
     * 测试DBUtils工具类的增删改操作
     * 
     * @author Never Say Never
     * @date 2017年10月31日
     * @version V1.0
     */
    public class TestDBUtils1 {
    
        /**
         * 添加所有用户方法
         */
        @Test
        public void testAddUser() {
            try {
                // 1.创建核心类QueryRunner
                QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
                // 2.编写SQL语句
                String sql = "insert into user values(null,?,?)";
                // 3.为占位符设置值
                Object[] params = { "余淮", "耿耿" };
                // 4.执行添加操作
                int rows = qr.update(sql, params);
                if (rows > 0) {
                    System.out.println("添加成功!");
                } else {
                    System.out.println("添加失败!");
                }
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        /**
         * 根据id修改用户方法
         * 
         */
        @Test
        public void testUpdateUserById() {
            try {
                // 1.创建核心类QueryRunner
                QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
                // 2.编写SQL语句
                String sql = "update user set upassword=? where uid=?";
                // 3.为站位符设置值
                Object[] params = { "xxx", 21 };
                // 4.执行添加操作
                int rows = qr.update(sql, params);
                if (rows > 0) {
                    System.out.println("修改成功!");
                } else {
                    System.out.println("修改失败!");
                }
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        /**
         * 根据id删除用户方法
         */
        @Test
        public void testDeleteUserById() {
            try {
                // 1.创建核心类QueryRunner
                QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
                // 2.编写SQL语句
                String sql = "delete from user where uid=?";
                // 3.为站位符设置值
                Object[] params = {19};
                // 4.执行添加操作
                int rows = qr.update(sql, params);
                if (rows > 0) {
                    System.out.println("删除成功!");
                } else {
                    System.out.println("删除失败!");
                }
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    TestDBUtils1.java --添加、删除、更新
    import java.sql.SQLException;
    import java.util.List;
    import java.util.Map;
    
    import org.apache.commons.dbutils.QueryRunner;
    import org.apache.commons.dbutils.handlers.BeanHandler;
    import org.apache.commons.dbutils.handlers.BeanListHandler;
    import org.apache.commons.dbutils.handlers.ColumnListHandler;
    import org.apache.commons.dbutils.handlers.MapListHandler;
    import org.apache.commons.dbutils.handlers.ScalarHandler;
    import org.junit.Test;
    
    import cn.itheima.domain.User;
    import cn.itheima.jdbc.utils.C3P0Utils;
    
    /**
     * 测试DBUtils查询操作
     * 
     * @author Never Say Never
     * @date 2017年10月31日
     * @version V1.0
     */
    public class TestDBUtils2 {
    
        /*
         * 查询所有用户方法
         */
        @Test
        public void testQueryAll() {
            try {
                // 1.获取核心类queryRunner
                QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
                // 2.编写sql语句
                String sql = "select * from user";
                // 3.执行查询操作
                List<User> users = qr.query(sql, new BeanListHandler<User>(User.class));
                // 4.对结果集集合进行遍历
                for (User user : users) {
                    System.out.println(user.getUname() + " : " + user.getUpassword());
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        
        /*
         * 根据id查询用户方法
         */
        @Test
        public void testQueryUserById() {
            try {
                // 1.获取核心类queryRunner
                QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
                // 2.编写sql语句
                String sql = "select * from user where uid=?";
                //3.为占位符设置值
                Object[] params = {21};
                // 4.执行查询操作
                User user = qr.query(sql, new BeanHandler<User>(User.class), params);
                System.out.println(user.getUname() + " : " + user.getUpassword());
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        
        /*
         * 根据所有用户的总个数
         */
        @Test
        public void testQueryCount() {
            try {
                // 1.获取核心类queryRunner
                QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
                // 2.编写sql语句
                String sql = "select count(*) from tbl_user";
                // 4.执行查询操作
                Long count = (Long) qr.query(sql, new ScalarHandler());
                System.out.println(count);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        
        /*
         * 查询所有用户方法
         */
        @Test
        public void testQueryAll1() {
            try {
                // 1.获取核心类queryRunner
                QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
                // 2.编写sql语句
                String sql = "select * from user";
                // 3.执行查询操作
                List<Map<String, Object>> list = qr.query(sql, new MapListHandler());
                // 4.对结果集集合进行遍历
                for (Map<String, Object> map : list) {
                    System.out.println(map);
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        
        /*
         * 查询所有用户方法
         */
        @Test
        public void testQueryAll2() {
            try {
                // 1.获取核心类queryRunner
                QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
                // 2.编写sql语句
                String sql = "select * from user";
                // 3.执行查询操作
                List<Object> list = qr.query(sql, new ColumnListHandler("uname"));
                // 4.对结果集集合进行遍历
                for (Object object : list) {
                    System.out.println(object);
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
    TestDBUtils2.java --查询操作

    【注意】:DBUtils在创建QueryRunner时传入dataSource对象,每次在执行完之后都会自动关闭Connection连接对象。(就相当于把conn交给dbutils管理了,他会帮我们关掉)

    如果没有传入dataSource的话,需要手动关闭。

  • 相关阅读:
    PTA考试几点注意事项
    网易云信在融合通信场景下的探索和实践之 SIPGateway 服务架构
    破旧立新,精准测试之道
    从 0 到 1 构建实时音视频引擎
    云信小课堂|如何实现音视频通话
    Python 回调函数实现异步处理
    数据结构--链表--约瑟夫问题
    Python 轻松实现ORM
    leetcode 递归编程技巧-链表算法题
    Tornado 初识
  • 原文地址:https://www.cnblogs.com/zhaojiankai/p/7891521.html
Copyright © 2011-2022 走看看