JDBC之数据库连接池
使用数据库连接池优化程序性能
不用连接池缺点:
使用连接池的话:
编写一个简单的连接池
连接池实现原理:
1 public class SimpleConnectionPool {
2 //池:存链接
3 private static List<Connection> pool = new ArrayList<Connection>();
4 static{
5 try {
6 //初始化10个链接
7 for(int i=0;i<10;i++){
8 Connection conn = JdbcUtil.getConnection();
9 pool.add(conn);
10 }
11 } catch (Exception e) {
12 throw new ExceptionInInitializerError("连接池初始化失败");
13 }
14 }
15 //从池中取出一个链接
16 public synchronized static Connection getConnection(){
17 if(pool.size()>0){
18 Connection conn = pool.remove(0);
19 return conn;
20 }else{
21 //继续创建一个链接供用户使用
22 //再向池中放一定数量的链接
23 //让用户等:超时。
24 //通知用户:服务器忙
25 throw new RuntimeException("对不起!服务器真忙,请稍后再试");
26 }
27 }
28 //用完连接后还回池中
29 public static void release(Connection conn){
30 pool.add(conn);
31 }
32 }
测试
1 @Test
2 public void save(){
3 Connection conn = null;
4 Statement stmt = null;
5 try{
6 conn = SimpleConnectionPool.getConnection();//从池中取的数据
7 stmt = conn.createStatement();
8 //...
9 }catch(Exception e){
10 e.printStackTrace();
11 }finally{
12 if(stmt!=null){
13 try {
14 stmt.close();
15 } catch (SQLException e) {
16 e.printStackTrace();
17 }
18 }
19 if(conn!=null){
20 SimpleConnectionPool.release(conn);//还回池中
21 }
22 }
23 }
编写连接池需实现javax.sql.DataSource接口
DataSource编写
利用动态代理实现
MyDataSource1
1 public class MyDataSource1 implements DataSource {
2 private static List<Connection> pool = new ArrayList<Connection>();
3 static{
4 try {
5 //初始化10个链接
6 for(int i=0;i<10;i++){
7 Connection conn = JdbcUtil.getConnection();
8 pool.add(conn);
9 }
10 } catch (Exception e) {
11 throw new ExceptionInInitializerError("连接池初始化失败");
12 }
13 }
14 //要从池中获取链接,利用动态代理机制。若调用获取连接对象,并使用其方法,是close方法的话,就使用代理规定的方法。
15 public Connection getConnection() throws SQLException {
16 if(pool.size()>0){
17 final Connection conn = pool.remove(0);//驱动的
18 //返回驱动的实例的代理对象
19
20 /*
21 * 产生代理对象的实例
22 * public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
23 *
24 * ClassLoader loader:既然代理类也是类,必定要类加载器加载。固定写法:和被代理对象一样
25 * Class<?>[] interfaces:代理对象要实现的接口。固定写发:和被代理对象一样。目的就是拥有相同的行为
26 * InvocationHandler h:策略设计模式。是一个接口,你必须传入一个实现了该接口的对象(具体代理方案)。
27 */
28 Connection proxy = (Connection)Proxy.newProxyInstance(conn.getClass().getClassLoader(),
29 conn.getClass().getInterfaces(),
30 new InvocationHandler() {
31
32 /**
33 * //调用代理对象的任何方法,该方法都会被执行
34 * @param proxy 代理对象本身的引用,一般用不着
35 * @param method 当前执行的方法
36 * @param args当前方法用到的参数
37 * @return 当前方法的返回值
38 * @throws Throwable
39 */
40 public Object invoke(Object proxy, Method method, Object[] args)
41 throws Throwable {
42 //判断是不是close方法,如果是,把链接放回池中
43 if("close".equals(method.getName())){
44 return pool.add(conn);
45 }
46 return method.invoke(conn, args);
47 }
48 });
49 return proxy;
50 }else{
51 //继续创建一个链接供用户使用
52 //再向池中放一定数量的链接
53 //让用户等:超时。
54 //通知用户:服务器忙
55 throw new RuntimeException("对不起!服务器真忙,请稍后再试");
56 }
57 }
58
开源数据源(数据库连接池) DBCP和C3P0
DBCP
搭建开发环境:
a、拷贝jar包:commons-dbcp.jar commons-pool.jar
b、在classpath中添加DBCP的配置文件:
c、编写工具类:
配置文件;
1 #连接设置
2 driverClassName=com.mysql.jdbc.Driver
3 url=jdbc:mysql://localhost:3306/wsj
4 username=root
5 password=root
6
7 #<!-- 初始化连接 -->
8 initialSize=10
9
10 #最大连接数量
11 maxActive=50
12
13 #<!-- 最大空闲连接 -->
14 maxIdle=20
15
16 #<!-- 最小空闲连接 -->
17 minIdle=5
18
19 #<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
20 maxWait=60000
21
22
23 #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
24 #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
25 connectionProperties=useUnicode=true;characterEncoding=utf8
26
27 #指定由连接池所创建的连接的自动提交(auto-commit)状态。
28 defaultAutoCommit=true
29
30 #driver default 指定由连接池所创建的连接的只读(read-only)状态。
31 #如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
32 defaultReadOnly=
33
34 #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
35 #可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
36 defaultTransactionIsolation=READ_COMMITTED
工具类
1 public class DBCPUtil {
2 private static DataSource dataSource;
3 static{
4 try {
5 Properties props = new Properties();
6 ClassLoader cl = DBCPUtil.class.getClassLoader();
7 InputStream in = cl.getResourceAsStream("dbcpconfig.properties");
8 props.load(in);
9 dataSource = BasicDataSourceFactory.createDataSource(props);
10 } catch (Exception e) {
11 throw new ExceptionInInitializerError(e);
12 }
13 }
14 public static DataSource getDataSource(){
15 return dataSource;
16 }
17 public static Connection getConnection(){
18 try {
19 return dataSource.getConnection();
20 } catch (SQLException e) {
21 throw new RuntimeException(e);
22 }
23 }
24 }
测试
public class DBCPUtilTest {
@Test
public void test1() throws SQLException{
Connection conn = DBCPUtil.getConnection();
System.out.println(conn.getClass().getName());
conn.close();
}
}
C3P0
a、拷贝jar包:c3p0-0.9.1.2.jar c3p0-oracle-thin-extras-0.9.1.2.jar(oracle瘦客户端需要)
b、在classpath中添加配置文件:
c、编写工具类:
配置文件
工具类
public class C3P0Util {
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
public static DataSource getDataSource(){
return dataSource;
}
public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
测试
public class C3P0UtilTest {
@Test
public void test1() throws SQLException{
Connection conn = C3P0Util.getConnection();
System.out.println(conn.getClass().getName());
conn.close();
}
}
利用JNDI获取数据源:更加接近真实
1、拷贝数据库的驱动到Tomcat\lib目录中。 (既mysql-connector-java.jar包)
2、在web应用的META-INF目录下,建立一个context.xml的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource name="jdbc/wsj" auth="Container" type="javax.sql.DataSource"
maxActive="50" maxIdle="30" maxWait="10000"
username="root" password="sorry" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/wsj"/>
</Context>
3.重启Tomcat
测试:
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");//Tomcat的放的资源的路径
DataSource ds = (DataSource)envContext.lookup("jdbc/wsj");//name
Connection conn = ds.getConnection();
out.write(conn.toString());
JDBC简单框架
数据源信息的获取
1 //数据库元信息的获取
2 public class MetaDataDemo {
3 //数据库本身的元信息
4 @Test
5 public void test1() throws Exception{
6 Connection conn = DBCPUtil.getConnection();
7 DatabaseMetaData dbmd = conn.getMetaData();
8 System.out.println(dbmd.getDatabaseProductName());
9 System.out.println(dbmd.getDatabaseProductVersion());
10 conn.close();
11 }
12 //SQL语句占位符的元信息:计算?的个数
13 @Test
14 public void test2()throws Exception{
15 Connection conn = DBCPUtil.getConnection();
16 PreparedStatement stmt = conn.prepareStatement("?????");
17 ParameterMetaData pmd = stmt.getParameterMetaData();
18 int paramNum = pmd.getParameterCount();//得到占位符的数量
19 System.out.println(paramNum);
20 conn.close();
21 }
22 //查询结果集的元数据
23 @Test
24 public void test3()throws Exception{
25 Connection conn = DBCPUtil.getConnection();
26 PreparedStatement stmt = conn.prepareStatement("select * from account");
27 ResultSet rs = stmt.executeQuery();
28 ResultSetMetaData rsmd = rs.getMetaData();
29 //有几列? 每列的列名是啥?类型又是啥?
30 int columnCount = rsmd.getColumnCount();
31 for(int i=0;i<columnCount;i++){
32 String columnName = rsmd.getColumnName(i+1);//列名。第一列的索引是1
33 int columnType = rsmd.getColumnType(i+1);//列类型。用的是整数常量表示的。
34 System.out.println(columnName+":"+columnType);
35 }
36 conn.close();
37 }
38 }
工具类编写
封装数据;
BeanHandler
1 /**
2 * 适合结果集只有一条的情况。
3 * 使用前提:数据库的字段名和目标类的字段名保持一致。约定。
4 * 目标对象到底啥:谁用谁知道
5 */
6 public class BeanHandler implements ResultSetHandler {
7
8 private Class clazz;//目标类型
9 public BeanHandler(Class clazz){
10 this.clazz = clazz;
11 }
12
13 public Object handle(ResultSet rs) {
14
15 try {
16 Object bean = clazz.newInstance();
17 //封装数据
18 if(rs.next()){
19 ResultSetMetaData rsmd = rs.getMetaData();
20 int columnCount = rsmd.getColumnCount();//列数
21 for(int i=0;i<columnCount;i++){
22 String fieldName = rsmd.getColumnName(i+1);//列名。和javabean的字段名一样
23 Object fieldValue = rs.getObject(i+1);
24 //给javabean的字段赋值
25 Field field = clazz.getDeclaredField(fieldName);// private int id;
26 field.setAccessible(true);//强暴他
27 field.set(bean, fieldValue);// private int id=1;
28 }
29 }
30 return bean;
31 } catch (Exception e) {
32 throw new RuntimeException(e);
33 }
34 }
35
36 }
ResultSetHandler
public interface ResultSetHandler {
/**
* 把结果集中的数据封装到对象中
* @param rs
* @return
*/
Object handle(ResultSet rs);
}
BeanListHandler 多条
1 public class BeanListHandler implements ResultSetHandler {
2 private Class clazz;//目标类型
3 public BeanListHandler(Class clazz){
4 this.clazz = clazz;
5 }
6 public Object handle(ResultSet rs) {
7 try {
8 List list = new ArrayList();
9 //封装数据
10 while(rs.next()){
11 Object bean = clazz.newInstance();
12 ResultSetMetaData rsmd = rs.getMetaData();
13 int columnCount = rsmd.getColumnCount();//列数
14 for(int i=0;i<columnCount;i++){
15 String fieldName = rsmd.getColumnName(i+1);//列名。和javabean的字段名一样
16 Object fieldValue = rs.getObject(i+1);
17 //给javabean的字段赋值
18 Field field = clazz.getDeclaredField(fieldName);// private int id;
19 field.setAccessible(true);//强暴他
20 field.set(bean, fieldValue);// private int id=1;
21 }
22 list.add(bean);
23 }
24 return list;
25 } catch (Exception e) {
26 throw new RuntimeException(e);
27 }
28 }
29
30 }
增删改查DBAssist
1 public class DBAssist {
2
3 private DataSource dataSource;
4 public DBAssist(DataSource dataSource){
5 this.dataSource = dataSource;
6 }
7 /**
8 * 查询
9 * @param sql 只能是查询语句
10 * @param params 如果没有参数,传null
11 * @return 封装后的信息
12 */
13 public Object query(String sql,Object[] params,ResultSetHandler rsh) {
14 Connection conn = null;
15 PreparedStatement stmt = null;
16 ResultSet rs = null;
17 try{
18 conn = dataSource.getConnection();
19 stmt = conn.prepareStatement(sql);
20 //参数的一些处理
21 //得到sql中的参数元信息
22 ParameterMetaData pmd = stmt.getParameterMetaData();
23 int paramCount = pmd.getParameterCount();
24 if(paramCount>0){
25 //有参数
26 if(params==null)
27 throw new RuntimeException("有占位符,不能不传递参数");
28 if(paramCount!=params.length)
29 throw new RuntimeException("参数个数与占位符个数不匹配");
30 //个数匹配
31 for(int i=0;i<paramCount;i++){
32 stmt.setObject(i+1, params[i]);
33 }
34 }
35 rs = stmt.executeQuery();
36 //查出来的数据在rs中。 用户执行的sql语句确定吗?不确定
37 //封装到JavaBean中或List中。封装到什么对象中确定吗?不确定
38
39 //确定:把结果集中的东西封装到JavaBean中。怎么封装?谁用谁知道。把路子铺好(接口),由使用着来走。策略设计模式
40
41 return rsh.handle(rs);
42 }catch(Exception e){
43 throw new RuntimeException(e);
44 }finally{
45 release(rs, stmt, conn);
46 }
47 }
48 /**
49 * 完成曾删改操作
50 * @param sql sql语句,参数要使用占位符
51 * @param params 顺序要和占位符的顺序对应.Java基础加强之可变参数
52 */
53 public void update(String sql,Object[] params) {
54 Connection conn = null;
55 PreparedStatement stmt = null;
56 ResultSet rs = null;
57 try{
58 conn = dataSource.getConnection();
59 stmt = conn.prepareStatement(sql);
60 //参数的一些处理
61 //得到sql中的参数元信息
62 ParameterMetaData pmd = stmt.getParameterMetaData();
63 int paramCount = pmd.getParameterCount();
64 if(paramCount>0){
65 //有参数
66 if(params==null)
67 throw new RuntimeException("有占位符,不能不传递参数");
68 if(paramCount!=params.length)
69 throw new RuntimeException("参数个数与占位符个数不匹配");
70 //个数匹配
71 for(int i=0;i<paramCount;i++){
72 stmt.setObject(i+1, params[i]);
73 }
74 }
75 stmt.executeUpdate();
76 }catch(Exception e){
77 throw new RuntimeException(e);
78 }finally{
79 release(rs, stmt, conn);
80 }
81 }
82 private void release(ResultSet rs,Statement stmt,Connection conn){
83 if(rs!=null){
84 try {
85 rs.close();
86 } catch (SQLException e) {
87 e.printStackTrace();
88 }
89 rs = null;
90 }
91 if(stmt!=null){
92 try {
93 stmt.close();
94 } catch (SQLException e) {
95 e.printStackTrace();
96 }
97 stmt = null;
98 }
99 if(conn!=null){
100 try {
101 conn.close();
102 } catch (SQLException e) {
103 e.printStackTrace();
104 }
105 conn = null;
106 }
107 }
108 }
bean类
1 public class Account implements Serializable {
2 private int id;
3 private String name;
4 private float money;
5 public int getId() {
6 return id;
7 }
8 public void setId(int id) {
9 this.id = id;
10 }
11 public String getName() {
12 return name;
13 }
14 public void setName(String name) {
15 this.name = name;
16 }
17 public float getMoney() {
18 return money;
19 }
20 public void setMoney(float money) {
21 this.money = money;
22 }
23 @Override
24 public String toString() {
25 return "Account [id=" + id + ", name=" + name + ", money=" + money
26 + "]";
27 }
28
29 }
测试:
DBAssistTest
1 public class DBAssistTest {
2 private DBAssist da = new DBAssist(DBCPUtil.getDataSource());
3 @Test
4 public void testAdd(){
5 da.update("insert into account (id,name,money) values (?,?,?)",
6 new Object[]{4,"ddd",10000});
7 }
8 @Test
9 public void testUpdate(){
10 da.update("update account set money=? where id=?",
11 new Object[]{1000,4});
12 }
13 @Test
14 public void testDel(){
15 da.update("delete from account where id=?",
16 new Object[]{4});
17 }
18 @Test
19 public void testFindOne(){
20 Account a = (Account)da.query("select * from account", null, new BeanHandler(Account.class));
21 System.out.println(a);
22 }
23 @Test
24 public void testFindAll(){
25 List<Account> list = (List<Account>)da.query("select * from account", null, new BeanListHandler(Account.class));
26 for(Account a:list)
27 System.out.println(a);
28 }
29 }