JDBC

JDBC工具类的抽取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
* JDBC的工具类
* @author Kidnapper
*
*/
public class JDBCUtils {
private static final String driverClassName;
private static final String url;
private static final String username;
private static final String password;

static{
//获取属性文件的内容:
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src/db.properties"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

driverClassName=properties.getProperty("driverClassName");
url=properties.getProperty("url");
username=properties.getProperty("username");
password=properties.getProperty("password");
}

/**
* 注册驱动的方法
*/
public static void loadDriver(){
try {
Class.forName(driverClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

/**
* 获得连接的方法
*/
public static Connection getConnection(){
Connection conn = null;
try {
// 将驱动也一并注册
loadDriver();
// 获得连接
conn = DriverManager.getConnection(url,username,password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}

/**
* 释放资源的方法
*/
public static void release(Statement stmt, Connection conn){
if(stmt != null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
conn = null;
}
}

public static void release(ResultSet rs, Statement stmt, Connection conn){
if(rs != null){
try{
rs.close();
}catch(SQLException e){
e.printStackTrace();
}
rs = null;
}
if(stmt != null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
conn = null;
}
}
}

JDBC的配置信息提取到配置文件

配置文件

  • 属性文件
    • 格式:拓展名是.properties
    • 内容:key=value(键值对)
  • XML文件

提取信息到配置文件

  • 定义一个配置文件

    配置文件
    • driverClassName:驱动名,5.8.0版本的为:com.mysql.jdbc.Driver

8.0.12版本的为:com.mysql.cj.jdbc.Driver
- url:驱动链接:以图中为例,格式为:jdbc:mysql://本机名:端口号/数据库名?是否采用SSL链接(一般为false)&Unicode编码&编码格式为UTF-8&时区设置(不加会出错)
- username:mysql用户名
- password:数据库连接密码

![JDBC出现乱码问题](https://img-blog.csdnimg.cn/20201002224931796.png#pic_center)

在工具类中解析属性文件

获取到具体内容为常量赋值
解析属性文件
注:红框为配置文件路径

数据库连接池

连接池的概述

什么是连接池

连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的进程使用。

连接池是装有连接的容器,使用连接的话,可从连接中进行获取,使用完成之后将连接归还给连接池。

为什么要学习连接池

连接对象创建和是需要耗费时间的,在服务器初始化的时候就初始化一些连接。把这些连接放入到内存中,使用的时候可以从内存中获取,使用完成之后将连接放入连接池中。从内存中获取和归还的效率要远远高于创建和销毁的效率。(提升性能)

连接池原理

连接池原理

自定义连接池

自定义连接池的实现

  1. 编写一个类实现DataSource接口
  2. 重写getConnection方法
  3. 初始化多个连接在内存中
  4. 编写归还连接的方法

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package com.jdbc.connectpool;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.sql.DataSource;

import com.jdbc.utils.JDBCUtils;
/**
* 自定义连接池
* @author Kidnapper
*
*/
public class MyDataSourse implements DataSource {
//将一些连接存入到内存中,可以定义一个集合,用户存储连接对象
private List<Connection> connList = new ArrayList<Connection>();

//在初始化的时候提供一些连接
public MyDataSourse(){
//初始化连接
for(int i = 1;i<=3;i++){
//向集合中存入连接
connList.add(JDBCUtils.getConnection());
}
}
//从连接池中获得连接的方法
@Override
public Connection getConnection() throws SQLException {
Connection conn = connList.remove(0);
return conn;
}

//编写一个归还连接的方法
public void addBack(Connection conn){
connList.add(conn);
}

@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO 自动生成的方法存根
return null;
}

@Override
public void setLogWriter(PrintWriter out) throws SQLException {
// TODO 自动生成的方法存根

}

@Override
public void setLoginTimeout(int seconds) throws SQLException {
// TODO 自动生成的方法存根

}

@Override
public int getLoginTimeout() throws SQLException {
// TODO 自动生成的方法存根
return 0;
}

@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO 自动生成的方法存根
return null;
}

@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO 自动生成的方法存根
return null;
}

@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO 自动生成的方法存根
return false;
}


@Override
public Connection getConnection(String username, String password) throws SQLException {
// TODO 自动生成的方法存根
return null;
}

}

代码测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package com.jdbc.connectpool;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.junit.Test;

import com.jdbc.utils.JDBCUtils;
/**
* 自定义连接池的测试类
* @author Kidnapper
*
*/
public class DataSourseDemo1 {

@Test
/**
* 此时自定义连接池
*/
public void demo1(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
MyDataSource dataSource = null;
try{
//获得连接
//conn = JDBCUtils.getConnection();
//从连接池中获得连接
dataSource = new MyDataSource();
conn = dataSource.getConnection();
//编写SQL
String sql = "select * from user";
//预编译SQL
pstmt = conn.prepareStatement(sql);
//设置参数
//执行SQL
rs = pstmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("id")+rs.getString("username")+" "+rs.getString("password"));
}
}catch(Exception e){
e.printStackTrace();
}finally{
//JDBCUtils.release(rs,pstmt, conn);
if(rs != null){
try{
rs.close();
}catch(SQLException e){
e.printStackTrace();
}
}

if(pstmt != null){
try{
pstmt.close();
}catch(SQLException e){
e.printStackTrace();
}
}
//归还连接
dataSource.addBack(conn);
}
}
}

自定义连接池的问题

使用接口的实现类完成的构造

1
MyDatasource dataSource = new MyDatasource();

这种写法不方便程序的扩展。

额外提供了方法归还连接

1
2
//归还连接:
daraSource.addBack(conn);

这种方式增加使用连接池的用户的难度。

自定义连接池的问题解决

如果不提供自定义的方法就可以解决这个问题,但是连接要如何归还到连接池呢?

解决分析的思路

原来在 Connection中是有一个close方法的,close方法完成了连接的销毁。能不能做一个事情,将原有的连接的 close方法改为归还。

  • 现在要做的事情就是将原有的close方法的逻辑改为归还。(增强一个类中的方法)

如何增强一个类中的方法

  • 一种:采用维承的方式:
1
2
3
4
5
6
7
8
9
10
11
12
*****维承这种增强是最简单,但是是有使用条件的:必须能够控制这个类的构造!!!
class Man{
public void run(){
System.out.ptintln(“跑….”);
}
}

class SuperMan extends Man{
public void run(){
System.out.println(“飞….”);
}
}
  • 二种:采用装饰者模式:

*****装饰者模式使用条件:
*一、增强的类和被增强的类实现相同的接口
*二、在增强的类中获得被增强的类的引用

装饰器模式

使用装饰者模式增强Connection中的close方法:
为了简化编程,提供一个模板类(模板类原封不动的将接口中的所有方法都实现,但是都没有增强,即所有方法又重新调用了一遍)。编写一个装饰类模板类。在装饰类中只需要增强某个方法即可。

代码修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
/**
* 使用装饰者增强Connection中的close方法
* @author Kidnapper
*
*/
public class MyConnectionWrapper extends ConnectionWrapper{

private Connection conn;
private List<Connection> connList;

public MyConnectionWrapper(Connection conn, List<Connection> connList) {
super(conn);
this.conn = conn;
this.connList = connList;
}

//增强某个方法
@Override
public void close() throws SQLException {
//super.close();
//归还连接
connList.add(conn);
}
}

对另外两个文件的修改如下:
对文件的修改1
对文件的修改2

开源连接池的介绍和使用

Druid的概述

Druid是阿里旗下开源连接池产品,使用非常简单,可以与Spring框架进行快速整合。配置文件使用属性文件(.properties)。

C3P0的连接池概述

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等。配置文件可用属性文件(.properties),也可使用XML文件,一般用XML文件。

JDBCUtils的优化改写

将数据库连接的工具类改写为以数据库连接池的形式,这里以C3P0为例,也可使用Druid。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

/**
* JDBC的工具类
* @author Kidnapper
*
*/
public class JDBCUtils2 {
//创建一个连接池:但是这个连接池只需要创建一次即可。
private static final ComboPooledDataSource dataSource = new ComboPooledDataSource();


/**
* 获得连接的方法
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
return dataSource.getConnection();
}

/**
* 获得连接池
*/
public static DataSource getDataSource(){
return dataSource;
}

/**
* 释放资源的方法
*/
public static void release(Statement stmt, Connection conn){
if(stmt != null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
conn = null;
}
}

public static void release(ResultSet rs, Statement stmt, Connection conn){
if(rs != null){
try{
rs.close();
}catch(SQLException e){
e.printStackTrace();
}
rs = null;
}
if(stmt != null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
stmt = null;
}
if(conn != null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
conn = null;
}
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import org.junit.Test;
import com.jdbc.utils.JDBCUtils;
import com.jdbc.utils.JDBCUtils2;


public class C3P0Demo2 {
@Test
/**
* 使用新的工具类测试
*/
public void demo1(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
//获得连接:从连接池中获取
//从连接池中获得连接
conn = JDBCUtils2.getConnection();
//编写SQL
String sql = "select * from user";
//预编译SQL
pstmt = conn.prepareStatement(sql);
//执行SQL
rs = pstmt.executeQuery();
while(rs.next()){
System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.release(rs, pstmt, conn);
}
}
}

其中C3P0的配置文件如下:
C3P0的配置文件内容