1.JDBC连接池
(1)使用连接池改造JDBC工具类
传统JDBC的操作,每次创建和销毁连接都非常花费时间。可以使用连接池优化的程序。在程序开始的时候,可以创建几个连接,将连接放入到连接池中.用户使用连接的时候,可以从连接池中进行获取.用完之后,可以将连接归还连接池.
自定义连接池:sun公司提供了一个连接池的接口:javax.sql.DataSource。各个厂商需要让自己的连接池实现这个接口,这样应用程序就可以方便地切换不同厂商的连接池。常见连接池:DBCP,C3P0
自定义连接池代码如下(实现dataSource接口):(连接数据库只需要mysql-connector-xxx.jar)
public class MyDataSource implements DataSource{
// 创建一个List集合用于存放多个连接对象.
private List<Connection> list = new ArrayList<Connection>();
// 在程序开始的时候,初始化几个连接,将连接存放到list中.
public MyDataSource() {
// 初始化3个连接:
for(int i=1;i<=3;i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
@Override
// 获得连接的方法:
public Connection getConnection() throws SQLException {
if(list.size() <= 0){
for(int i=1;i<=3;i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
Connection conn = list.remove(0);
return conn;
}
// 归还连接的方法:
public void addBack(Connection conn){
list.add(conn);
}
}
但是自定义连接池还需要记住自定义的API,解决:增强Connection的close方法,把原有的销毁变为归还。
(2)增强一个Java类中的某个方法有几种方式?
- 一种方式:继承的方式.
能够控制这个类的构造的时候,才可以使用继承.
- 二种方式:装饰者模式方式.
包装对象和被包装的对象都要实现相同的接口.
包装的对象中需要获得被包装对象的引用.
缺点:如果接口的方法比较多,增强其中的某个方法.其他的功能的方法需要原有调用.
- 三种方式:动态代理的方式.
被增强的对象实现接口就可以.
- 字节码增强
案例代码:
//继承和装饰者的案例:
/**
* 继承的方式增强一个类中某个方法:
*/
class Man{
public void run(){
System.out.println("跑....");
}
}
class SuperMan extends Man{
public void run(){
// super.run();
System.out.println("飞....");
}
}
/**
* 使用装饰者的方式完成类的方法的增强
*/
interface Waiter{
public void server();
}
class Waiteress implements Waiter{
@Override
public void server() {
System.out.println("服务...");
}
}
class WaiteressWrapper implements Waiter{
private Waiter waiter;
public WaiteressWrapper(Waiter waiter) {
this.waiter = waiter;
}
@Override
public void server() {
System.out.println("微笑...");
// this.waiter.server();
}
}
(3)使用装饰者模式增强Connection的close方法
代码:
package com.xing.utils.datasource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
public class MyDatasource{
//创建连接池,存放conn.因为移除/添加操作较多,所以选择LinkedList
private static LinkedList<Connection> pool=new LinkedList<Connection>();
//静态代码块初始化连接池中的连接
static {
Connection conn=null;
try {
Class.forName("com.mysql.jdbc.Driver");
//创建三个连接放入连接池
for(int i=0;i<3;i++) {
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/user", "root", "123");
//将连接使用装饰类包装一下,然后添加到连接池,构造方法中将pool传递进去,便于连接归还
StrongDataSource ds=new StrongDataSource(conn, pool);//包装对象
// pool.add(conn);//将连接添加到连接池
pool.add(ds);//将连接添加到连接池
}
} catch (Exception e) {
e.printStackTrace();
}
}
//获得连接的方法
public static Connection getConn() {
if(!pool.isEmpty()) {
Connection conn = pool.removeFirst();//一个Connection只能提供给当前的一个线程使用,因此要进行移除操作
return conn;
}else {
try {
Thread.sleep(5000);//池子空了,等待归还
} catch (InterruptedException e) {
e.printStackTrace();
}
return getConn();
}
}
//归还连接
public static void release(Connection conn) {
if(conn!=null) {
try {
//close()缺点:把连接关闭了,而不是归还给连接池,连接池要重新创建连接。改进:方法增强
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
// pool.add(conn);
}
}
}
//方法增强:装饰着设计模式
//装饰类
public class StrongDataSource implements Connection{
private Connection conn;
private LinkedList<Connection> pool;
//编写构造方法
public StrongDataSource(Connection conn,LinkedList<Connection> pool) {
this.conn=conn;
this.pool=pool;
}
//需要增强的方法
@Override
public void close() throws SQLException {
System.out.println("归还:"+this);
pool.add(conn);
}
//此方法必须覆盖!否则会出现空指针异常!!!
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
// TODO Auto-generated method stub
return conn.prepareStatement(sql);
}
//以下是不需要增强的方法
...
}
package com.xing.utils.datasource;
import java.sql.Connection;
import java.sql.SQLException;
public class Test {
//为了体现连接池的优势,开启十个线程,采用多线程并发访问,使同一个连接在不同时间被不同线程使用
public static void main(String[] args) {
for(int i=0;i<4;i++) {
new MyThread().start();
}
}
}
class MyThread extends Thread{
public void run() {
Connection conn = MyDatasource.getConn();
if(conn==null) {
System.out.println("conn空了");
}else {
System.out.println("使用"+conn+"获得连接"+Thread.currentThread());
// MyDatasource.release(conn);
try {
conn.close();//调用的实际是StrongDataSource的colse()方法
} catch (SQLException e) {
e.printStackTrace();
}
}
}
};
2.常见的连接池:C3P0连接池
导入一个jar包c3p0....jar
配置文件c3p0-config.xml和测试:
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="user">root</property>
<property name="password">123</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///user</property>
</default-config>
</c3p0-config>
package com.xing.utils.c3p0;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.xing.utils.datasource.MyDatasource;
public class C3P0Utils {
/*private static ComboPooledDataSource source=new ComboPooledDataSource();
public static void main(String[] args) {
Connection connection=null;
try {
connection = source.getConnection();
PreparedStatement ps = connection.prepareStatement("select * from user");
ResultSet rs = ps.executeQuery();
while(rs.next()) {
System.out.println(rs.getObject(2));
}
} catch (SQLException e) {
e.printStackTrace();
}
}*/
public static void main(String[] args) {
for(int i=0;i<4;i++) {
new MyThread().start();
}
}
}
class MyThread extends Thread{
private static ComboPooledDataSource source=new ComboPooledDataSource();
public void run() {
Connection conn=null;;
try {
conn = source.getConnection();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if(conn==null) {
System.out.println("conn空了");
}
System.out.println("使用" + conn + "获得连接" + Thread.currentThread());
try {
conn.close();// 调用的实际是StrongDataSource的colse()方法
} catch (SQLException e) {
e.printStackTrace();
}
}
};
3.DBCP连接池
DBCP连接池也是一个开源的连接池,是tomcat内置的连接池
导入jar包:commons-dbcp-1.4.jar和commons-pool.1.6.jar
配置文件和代码。注意:配置文件前面的key格式固定,不能更改
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/user
username=root
password=123
package com.xing.utils.DBCP;
import java.io.IOException;
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 source;
static {
try {
//加载配置文件
InputStream input = DBCPUtils.class.getClassLoader().getResourceAsStream("com/xing/utils/test002.properties");
Properties prop=new Properties();
prop.load(input);
//创建连接池
source = BasicDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
e.printStackTrace();
}
}
public static DataSource getDataSource() {
return source;
}
public static Connection getConn() {
Connection conn=null;
try {
conn=source.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
}
4.DBUtils和JavaBean
如果只使用jdbc开发,那么冗余代码较多。为了简化,使用DBUtils(需要导入dbutils.jar),DBUtils是jdbc的简化开发工具包,需要使用技术:连接池,sql语句
相关知识:JavaBean组件:JavaBean就是一个类,常用于封装数据
DBUtils封装了对JDBC的操作,简化了实际的操作,减少了代码量。DBUtils三个核心供能介绍:
- QueryRunner提供了对sql语句操作的API
- ResultSetHandler接口,用于定义select操作后,怎样封装结果集
- DBUtils类,它就是一个工具类,定义了关闭资源与事务处理的方法
QueryRunner:
QueryRunner(Datasource ds):提供数据源
Update()
Query(String sql , ResultSetHandler<T>rsh , Object...params)
ResultSetHandler:
BeanHandler:将结果集中第一条记录封装到一个指定的javaBean中。
BeanListHandler:将结果集中每一条记录封装到指定的javaBean中,将这些javaBean封装到List中
ScalarHandler:它是用于单数据,例如select count(*)from表操作。
DBUtils类:
closeQuietly(Connection)关闭连接,如果有异常,try后不抛
commitAndCloseQuietly(Connection):提交并关闭连接
rollbackAndCloseQuietly(Connection):回滚并关闭连接
package com.xing.utils.DBUtils;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import com.xing.utils.DBCP.DBCPUtils;
import com.xing.utils.c3p0.C3P0Utils;
public class DBUtils {
public static void query() {
QueryRunner runner=new QueryRunner(C3P0Utils.getDataSource());
String sql="select*from user";
try {
List<User> query = runner.query(sql, new BeanListHandler<User>(User.class));
for (User user : query) {
System.out.println(user.getUsername());
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
query();
}
}