通过Java对数据操作的基础方式(+连接池)不到连接池就不需要装饰者模式来修改close方法。)
非连接池
方式一:自己获取链接并通过硬编码方式获取
步骤:
注册驱动
获取连接
编写sql
创建预编译的语句执行者
设置参数 没有问号不用设置参数
执行sql
处理结果
释放资源
步骤代码
Class.forName("com.mysql.jdbc.Driver");//注册驱动
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/***", "root", "123456");
String sql="select * from category";
PreparedStatement st=conn.prepareStatement(sql);
ResultSet rs=st.executeQuery();//还有个int的 executeUpdate
//释放资源.
rs.close();
st.close();
conn.close();
方式二:通过自定义工具类和配置文件的方式
先上配置文件jdbc.properties
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/***
user=root
password=123456
通过一个方法读取配置文件信息,然后给工具类中定义的最终变量。说白了没有这个方法我们自己也可以通过io流实现。
代码如下,原理和上面一样。吧一些重复的代码放到了工具类中
JdbcUtils
public class JdbcUtils {
static final String DRIVERCLASS;
static final String URL;
static final String USER;
static final String PASSWORD;
static {
// 块编辑 alt+shift +a
// 变大写 ctrl+shift+x
// 变小写 ctrl+shift+y
// 向下复制一行 alt+ctrl+↓
// 向下添加一个空行 shift + enter
// 向上添加一个空行 ctrl+shift + enter
// 删除一行 选中行 ctrl+d
// 注释或者去掉注释 ctrl+/
// 向下移动一行 alt + ↓
// 获取ResourceBundle ctrl+2 l
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
// 获取指定的内容
DRIVERCLASS = bundle.getString("driverClass");
URL = bundle.getString("url");
USER = bundle.getString("user");
PASSWORD = bundle.getString("password");
}
static {
// 注册驱动 ctrl+shift+f格式化代码
try {
Class.forName(DRIVERCLASS);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 获取连接
public static Connection getConnection() throws SQLException {
// 获取连接 ctrl+o 整理包
return DriverManager.getConnection(URL, USER, PASSWORD);
}
/**
* 释放资源
*
* @param conn
* 连接
* @param st
* 语句执行者
* @param rs
* 结果集
*/
public static void closeResource(Connection conn, Statement st, ResultSet rs) {
closeResultSet(rs);
closeStatement(st);
closeConn(conn);
}
/**
* 释放连接
*
* @param conn
* 连接
*/
public static void closeConn(Connection conn) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
/**
* 释放语句执行者
*
* @param st
* 语句执行者
*/
public static void closeStatement(Statement st) {
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
st = null;
}
}
/**
* 释放结果集
*
* @param rs
* 结果集
*/
public static void closeResultSet(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
}
}
对比两种方法的代码量:
第一种 | 第二种 |
---|
DriverManager.registerDriver(new Driver()); |Connection conn=null;
Connection conn= |ResultSet rs=null;
DriverManager.getConnection("jdbc:mysql://localhost:3306/***", "root", "123456"); |PreparedStatement st=null;
String sql="select * from category"; |conn=JdbcUtils.getConnection();
PreparedStatement st=conn.prepareStatement(sql); |String sql="insert into category values(?,?)";
ResultSet rs=st.executeQuery(); |st=conn.prepareStatement(sql);
rs.close(); |st.setString(1, "a06");
st.close(); |int i=st.executeUpdate();
conn.close(); |JdbcUtils.closeResource(conn, st, rs);
可以看出,代码量并没有减少,方便的只是配置信息的修改方便了。
连接池
通过连接池(DataSource)完成操作:
方式一:自定义连接池
通过上面的类JdbcUtils获取三个conn,然后放入一个list集合中,用的时候removeFirst;
详情:
public class MyDataSource {
static LinkedList<Connection> pool=new LinkedList<>();
static{
//初始化的时候 需要放入3个连接
for (int i = 0; i < 3; i++) {
try {
Connection conn = JdbcUtils.getConnection();
pool.addLast(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//从连接池中获取连接
public static Connection getConnection(){
//获取连接的时候需要判断list是否为空
if(pool.isEmpty()){
//在添加2个连接进去
for (int i = 0; i < 3; i++) {
try {
Connection conn = JdbcUtils.getConnection();
pool.addLast(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
System.out.println("从池中获取一个连接");
Connection conn = pool.removeFirst();
//将conn进行包装
// ConnectionWarp myConn = new ConnectionWarp(conn);
ConnectionWarp myConn = new ConnectionWarp(conn,pool);
return myConn;
}
}
归还的时候两种方法
①:在MyDataSource中添加一个方法
public static void addBack(Connection conn){
//将conn放入到list的最后面即可
pool.addLast(conn);
System.out.println("连接已归还");
}
使用的时候直接ds.addBack。
②:使用装饰者模式自定义一个修饰类ConnectionWarp
这里请看我的另一篇文章:https://blog.csdn.net/yjzq2280044399/article/details/85344277
最后也是在MyDataSource中实例化修饰类ConnectionWarp的对象并将从JdbcUtils那获取的conn传给它
方式二:DBCP连接池
使用步骤:
1.导入jar包(commons-dbcp-1.4.jar和commons-pool-1.5.6.jar)
2.使用api
①硬编码
//创建连接池
BasicDataSource ds = new BasicDataSource();
//配置信息
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql:///***");
ds.setUsername("root");
ds.setPassword("123456");
Connection conn=ds.getConnection();//这个conn也是被修饰过的
String sql="insert into category values(?,?);";
PreparedStatement st=conn.prepareStatement(sql);
//设置参数
st.setString(1, "a11");
st.setString(2, "饮料");
int i = st.executeUpdate();
System.out.println(i);
JdbcUtils.closeResource(conn, st, null);
②通过配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("src/dbcp.properties"));
//设置
//prop.setProperty("driverClassName", "com.mysql.jdbc.Driver");
//创建连接池
DataSource ds = new BasicDataSourceFactory().createDataSource(prop);
//create 制造的意思
Connection conn=ds.getConnection();
String sql="insert into category values(?,?);";
PreparedStatement st=conn.prepareStatement(sql);
//设置参数
st.setString(1, "c012");
st.setString(2, "饮料1");
int i = st.executeUpdate();
System.out.println(i);
JdbcUtils.closeResource(conn, st, null);
方式三:C3P0连接池
使用步骤:
1.导入jar包(c3p0-0.9.1.2.jar)
2.使用api
a.硬编码(不推荐)
new ComboPooledDataSource()
ComboPooledDataSource ds = new ComboPooledDataSource();
//设置基本参数
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql:///***");
ds.setUser("root");
ds.setPassword("123456");
Connection conn=ds.getConnection();
String sql="insert into category values(?,?);";
PreparedStatement st=conn.prepareStatement(sql);
//设置参数
st.setString(1, "a12");
st.setString(2, "神药");
int i = st.executeUpdate();
System.out.println(i);
JdbcUtils.closeResource(conn, st, null);
b.配置文件
配置文件的名称:c3p0.properties 或者 c3p0-config.xml
配置文件的路径:src下
编码只需要一句话
new ComboPooledDataSource()//使用默认的配置
new ComboPooledDataSource(String configName)//使用命名的配置 若配置的名字找不到,使用默认的配置
ComboPooledDataSource ds =new ComboPooledDataSource("wlf12321");//若查找不到命名的配置 使用默认的配置
Connection conn=ds.getConnection();
String sql="insert into category values(?,?);";
PreparedStatement st=conn.prepareStatement(sql);
//设置参数
st.setString(1, "c124");
st.setString(2, "解药");
int i = st.executeUpdate();
System.out.println(i);
JdbcUtils.closeResource(conn, st, null);
对比DBCP少了两行代码,即不需要自己手动导入配置文件了。
最重要的工具包Dbutils(我觉得用包来理解更贴切)
在这个工具包之前,我们还用了一个自定义工具类,先分析这个
public class DataSourceUtils {
private static ComboPooledDataSource ds=new ComboPooledDataSource();
/**
* 获取数据源
* @return 连接池
*/
public static DataSource getDataSource(){
return ds;
}
/**
* 获取连接
* @return 连接
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
return ds.getConnection();
}
/**
* 释放资源
*
* @param conn
* 连接
* @param st
* 语句执行者
* @param rs
* 结果集
*/
public static void closeResource(Connection conn, Statement st, ResultSet rs) {
closeResultSet(rs);
closeStatement(st);
closeConn(conn);
}
/**
* 释放连接
*
* @param conn
* 连接
*/
public static void closeConn(Connection conn) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
/**
* 释放语句执行者
*
* @param st
* 语句执行者
*/
public static void closeStatement(Statement st) {
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
st = null;
}
}
/**
* 释放结果集
*
* @param rs
* 结果集
*/
public static void closeResultSet(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
}
}
这个工具类下面的和Jdbcutils的内容是一样的,就是归还连接和释放资源的内容。那么重点的就是getDataSource() 这个方法,直接返回的是 ComboPooledDataSource这个类的构造,读取的是c3p0.properties 或者 c3p0-config.xml。
Dbutils是apache组织的一个工具类(包),jdbc的框架,更方便我们使用
使用步骤:
1.导入jar包(commons-dbutils-1.4.jar)
2.创建一个queryrunner类
queryrunner作用:操作sql语句
构造方法:
new QueryRunner(Datasource ds);
3.编写sql
4.执行sql
query(…):执行r操作
update(…):执行cud操作
核心类或接口
QueryRunner:类名
作用:操作sql语句
构造器:
new QueryRunner(Datasource ds);
注意:
底层帮我们创建连接,创建语句执行者 ,释放资源.
常用方法:
query(…):
update(…):
DbUtils:释放资源,控制事务 类
closeQuietly(conn):内部处理了异常
commitAndClose(Connection conn):提交事务并释放连接
所以说这个官方工具类,首先我们来看下这个类的API
可以看到它内部所含的所有接口和类。这里我们使用的就是QueryRunner类,使用步骤如下:
public void insert() throws SQLException{
//1.创建queryrunner类
QueryRunner qr = new QueryRunner(DataSourceUtils.getDataSource());
//2.编写sql
String sql="insert into category values(?,?)";
//3.执行sql
qr.update(sql, "c201","厨房电器");
}
还记得连接数据库所需要的操作吗?
注册驱动
获取连接
编写sql
创建预编译的语句执行者
设置参数 没有问号不用设置参数
执行sql
处理结果
释放资源
来看QueryRunner类中的源代码
public class QueryRunner extends AbstractQueryRunner {
public QueryRunner() {
super();
}
public QueryRunner(boolean pmdKnownBroken) {
super(pmdKnownBroken);
}
public QueryRunner(DataSource ds) {
super(ds);
}
public QueryRunner(DataSource ds, boolean pmdKnownBroken) {
super(ds, pmdKnownBroken);
}
}
英文注释我都删了,这是它的四个构造方法,我们常用的是public QueryRunner(DataSource ds)
这个,可以看见是调用了父类构造并将从ComboPooledDataSource那获取的DataSource传了过去,我们再看它的父类AbstractQueryRunner,从名字上可以看出是一个抽象类,API的图上面也有。这个时候的第一步算是完成了,但是第二步我们看不到,只有在用conn的时候才知道,根据上面的update方法,我们找到它的源码
public int update(String sql, Object param) throws SQLException {
Connection conn = this.prepareConnection();
return this.update(conn, true, sql, new Object[]{param});
}
发现当执行的时候才创建conn
而这个**prepareConnection()**方法,我们发现,是在他的父类中执行的,代码如下
public abstract class AbstractQueryRunner {
protected Connection prepareConnection() throws SQLException {
if (this.getDataSource() == null) {
throw new SQLException("QueryRunner requires a DataSource to be " +
"invoked in this way, or a Connection should be passed in");
}
return this.getDataSource().getConnection();
}
}
根据执行步骤往下走,qr.update(sql, "a01","厨房电器");
,先调用上面的方法创建conn,再调用下面的方法生成语句执行者等。最后返回结果集。
private int update(Connection conn, boolean closeConn, String sql, Object... params) throws SQLException {
if (conn == null) {
throw new SQLException("Null connection");
}
if (sql == null) {
if (closeConn) {
close(conn);
}
throw new SQLException("Null SQL statement");
}
PreparedStatement stmt = null;
int rows = 0;
try {
stmt = this.prepareStatement(conn, sql);//都在抽象方法中生成的。 -
this.fillStatement(stmt, params);
rows = stmt.executeUpdate();
} catch (SQLException e) {
this.rethrow(e, sql, params);
} finally {
close(stmt);
if (closeConn) {
close(conn);
}
}
return rows;
}
总结下,QueryRunner类完成的步骤有:
获取连接
创建预编译的语句执行者
执行sql
返回结果集
释放资源