1. JBDCUtils工具类+数据库连接池+装饰者模式对数据库连接的封装实现详解
1.1. JBDCUtils工具类的意义
- JBDCUtils工具类的实现主要是用来封装数据库的连接,最初的数据库连接是有一个类实现,但是这个类里面包含数据库连接url、Driver、用户名、密码。这个类简单直接,但是不利于移植,因为url、Driver都被写死了,如果要换个数据库,还要重新修改类。不符合java的"高内聚,低耦合"的思想。
- 封装了数据库连接,通过读取配置文件properties的方式获取数据库连接,符合MVC架构的分层思想。有利于软件开发。
1.2. JBDCUtils都包含哪些方法
- 一个静态代码块,用于读取配置文件,加载类时连接数据库。
- 给其他类获取连接数据库的方法。
- 用于释放连接,释放结果集,关闭接口的方法。
代码:
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;
/**
* 提供获取连接和释放资源的 方法
*
*/
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;
}
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();
}
}
}
}
1.3. JDBC连接池的使用意义
由于数据库的连接关闭极大的消耗了资源,浪费cpu时间。为了降低这种资源的浪费,因此创建了连接池连接数据库的方法。当你需要数据库连接时,可以从连接池里取出一个连接,当你关闭连接时也并不是真正的关闭了连接,而是将连接释放到了连接池中,这样就减少了资源的浪费。
java提供了连接池的接口:import javax.sql.DataSource;我们需要根据自身需要去实现这个类,也就是说根据自己的情况创建一个实现这个连接池接口的类,这个类称为连接池。。
1.4. 连接池中有哪些方法
- 创建一个容器,用于存储连接数据库的连接对象,其实就是一个实体类entity。
- 一个静态代码块,类加载时就运行。主要是为了实现n(连接池容量)个连接对象,并将这些连接放到容器里面。
- 重写getConnection()方法,用于实现从连接池中获取一个连接。
- 重写backConnection()方法,用于将不用的连接归还到连接池中。
public class MyDataSource implements DataSource{
//1.创建一个容器用于存储Connection对象。
private static LinkedList pool=new LinkedList();
//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;
//使用前先判断
if(pool.size()==0) {
//4.池子里面没有我们再创建一些。
for(int i=0;i<5;i++) {
conn=JDBCUtils_V3.getConnection();
pool.add(conn);
}
}
//5.从池子里面获取一个连接对象
conn=pool.remove(0);
return conn;
}
/**
* 归还连接对象到池子。
*
*/
public void backConnection(Connection conn) {
pool.add(conn);
}
}
1.5. 自定义连接池代码改进(装饰者模式的应用)
1.5.1. 为什么要改进?
如果不改进,例如Connection 对象conn,当我们调用该对象的close()方法时,就会关闭连接,而不是回到连接池中,为了实现万一用户调用了close()方法,让连接重新回到连接池中这个功能,需要对自定义连接池代码进行改进。改进的方法采用装饰者模式。装饰者模式专门实现方法的增强。不过必须有接口。
1.5.2. 装饰者模式解决问题:
装饰者模式主要用于扩展对象的功能,是继承关系的一种替代,装饰者模式可以在不产生大量子类的情况下,将对象的功能加以扩展。
继承会产生大量子类,而且代码会有冗余。
如上图:如果没有装饰者模式会先创建手抓饼、肉夹馍两个类。然后再分别创建4个子类分别是手抓饼+煎蛋类,手抓饼+火腿片类等等,8个子类才能满足客户需求。如果配料更多,还要更多子类。
如果采用装饰者模式只需要生成四个子类就行了。
1.5.3. 本人对装饰者模式的理解:
-
装饰者模式分为抽象构件角色,Component,具体构件角色Concrete Component,装饰角色Decorator,具体装饰角色Concrete Component。
抽象构件角色 (Component):一般是接口或者抽象类,包含了一些方法。
具体构件角色(Concrete Component):是Component角色的实现类。用于实现其方法。
装饰者角色(Decorator):是一个抽象类,是具体装饰角色的抽象类,这个类很重要,也是装饰者模式的意义所在,这个抽象类实现了Component角色,并且里面 必须有一个Component角色的引用。因为有了这个引用就可以通过多态的方式调用其子类(实现类)的方法。进而起到装饰作用。
具体装饰者角色(Concrete Component):用于实现Decorator角色,这里面也必须有Component角色引用,应该说是:先在子类里面写,然后再往父类写,因为你对象实例化时是实例化的子类对象。通过引用实例化了对象。
-
我认为,装饰者模式是为了在不修改已有实现类的情况下或者减少继承子类数目冗余的情况下功能的一种扩展,在构造装饰者模式时,首先要分清角色,先将具体装饰者角色抽象起来,形成一个抽象类即:装饰者角色Decrator。再将Decrator继承抽象构件角色,Decrator这样就形成了和构件角色同一等级的情况。如下图:
-
装饰者类的使用:
首先new 具体构件类,会生成一个具体构件类对象。
其次再new具体装饰者类,并将具体构件对象传递过去,这样就形成了一个多态,通过多态调用具体装饰者的子类方法,就实现了功能的扩充。(具体装饰类扩充了功能)。
1.5.4. 对今天的jdbc+连接池的应用:
前面提到需要对连接对象的close()方法进行扩充、完善,用于达到调用close()方法时将连接返回到连接池。
-
分析角色:
抽象构件角色:Connection这是一个接口类 或者说是AutoCloseable接口。
具体构件角色:DriverManager.getConnection();这个方法实则是实现了Connection接口。Decrator装饰者角色:由于类不多,这个没有直接是具体装饰者类,
具体装饰者类:我们需要创建一个这种角色,并且实现Connection接口,此外该类里面必须含有Connection 引用。
这是一个只包含三个角色的三角关系因为具体装饰者类只有一个类没有必要再抽象出来一个抽象类,直接继承抽象构件类就可以了。
-
代码分析:
首先是实现连接池接口的连接池类,这里面调用了Connection接口,并通过JDBCUtils_v3.getConnection()实现了该接口。并传递Connection对象到了MyConnection具体装饰者类的构造器中。
public class MyDataSource1 implements DataSource{ //1.创建1个容器用于存储Connection对象 private static LinkedList
pool = new LinkedList (); //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); } } 这是MyConnection具体装饰者类。
//1.实现同一个接口Connection public class MyConnection implements Connection { //3.定义一个变量 private Connection conn; private LinkedList
pool; // 2.编写一个构造方法(参数使用了面相对象的多态特性) public MyConnection(Connection conn,LinkedList 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); } }
1.5.5. 参考链接:
- 博客连接1
- 博客连接2
- 博客连接3