用PreparedStatement进行CRUD操作步骤:
-
拷贝mysql驱动包到模块下,并添加依赖
-
拷贝JDBCUtils.java工具类和db.properties配置文件到模块下
-
步骤:
-
注册驱动,获得连接
-
预编译sql语句,得到预编译对象
-
-
为sql语句设置参数
-
执行sql语句,处理结果
-
释放资源
-
public class Test1_查 {
public static void main(String[] args) throws Exception{
//- 注册驱动,获得连接
Connection connection = JDBCUtils.getConnection();
//- 预编译sql语句,得到预编译对象
String sql = "select * from user where id = ?";
PreparedStatement ps = connection.prepareStatement(sql);
//- 为sql语句设置参数
ps.setInt(1,2);
//- 执行sql语句,处理结果
ResultSet resultSet = ps.executeQuery();
// 定义一个User变量
User user = null;
while (resultSet.next()) {
// 取值
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String password = resultSet.getString("password");
String nickname = resultSet.getString("nickname");
// 封装数据
user = new User(id,username,password,nickname);
}
//- 释放资源
JDBCUtils.release(resultSet,ps,connection);
System.out.println(user);
}
}
public class Test2_增 {
public static void main(String[] args) throws Exception {
// 1.注册驱动,获得连接
Connection connection = JDBCUtils.getConnection();
// 2.预编译sql语句,得到预编译对象
String sql = "insert into user values(null,?,?,?)";
PreparedStatement ps = connection.prepareStatement(sql);
// 3.为sql语句设置参数
ps.setString(1, "zs");
ps.setString(2, "123456");
ps.setString(3, "老张");
// 4.执行sql语句,处理结果
int rows = ps.executeUpdate();
System.out.println("受影响的行数:" + rows);
// 5.释放资源
JDBCUtils.release(null,ps,connection);
}
}
连接池概念
为什么要使用连接池
Connection对象在JDBC使用的时候就会去创建一个对象,使用结束以后就会将这个对象给销毁了(close).每次创建和销毁对象都是耗时操作.需要使用连接池对其进行优化.
程序初始化的时候,初始化多个连接,将多个连接放入到池(集合)中.每次获取的时候,都可以直接从连接池中进行获取.使用结束以后,将连接归还到池中.
-
程序一开始就创建一定数量的连接,放在一个容器(集合)中,这个容器称为连接池。
-
使用的时候直接从连接池中取一个已经创建好的连接对象, 使用完成之后 归还到池子
-
如果池子里面的连接使用完了, 还有程序需要使用连接, 先等待一段时间(eg: 3s), 如果在这段时间之内有连接归还, 就拿去使用; 如果还没有连接归还, 新创建一个, 但是新创建的这一个不会归还了(销毁)
-
集合选择LinkedList
-
增删比较快
-
LinkedList里面的removeFirst()和addLast()方法和连接池的原理吻合
-
自定义连接池-初级版本
分析
解决办法
-
创建连接池类
-
在连接池类中,定义一个LinkedList集合(表示连接池)
-
在连接池类的静态代码块中,创建固定数量的连接,并存储到LinkedList集合中
-
提供一个公共的非静态方法来获取连接对象(getAbc)
-
提供一个公共的非静态方法来归还连接对象(addBack)
-
提供一个公共的静态方法来获取连接池中连接的数量
-
public class MyDataSource01 {
//- 在连接池类中,定义一个LinkedList集合(表示连接池)
private static LinkedList<Connection> pools = new LinkedList<>();//- 在连接池类的静态代码块中,创建固定数量的连接,并存储到LinkedList集合中
static {
try {
for (int i = 0; i < 5; i++) {
// 创建连接
Connection connection = JDBCUtils.getConnection();
// 把连接添加到连接池中
pools.add(connection);
}
} catch (SQLException e) {
e.printStackTrace();
}
}//- 提供一个公共的非静态方法来获取连接对象(getAbc)
public Connection getAbc(){
// 获取连接池中第一个连接
Connection connection = pools.removeFirst();
// 返回连接
return connection;
}//- 提供一个公共的非静态方法来归还连接对象(addBack)
public void addBack(Connection connection){
pools.addLast(connection);
}//- 提供一个公共的静态方法来获取连接池中连接的数量
public static int size(){
return pools.size();
}
}
测试: -
public class Test {
public static void main(String[] args) throws Exception {
// 创建连接池对象
MyDataSource01 dataSource = new MyDataSource01();
System.out.println("获取连接之前,连接池中连接的数量:"+MyDataSource01.size());// 5// 1.注册驱动,获得连接
Connection connection = dataSource.getAbc();
System.out.println("获取连接之后,连接池中连接的数量:"+MyDataSource01.size());// 4
// 2.预编译sql语句,得到预编译对象
String sql = "select * from user where id = ?";
PreparedStatement ps = connection.prepareStatement(sql);// 3.设置sql语句参数
ps.setInt(1, 2);// 4.执行sql语句,处理结果
ResultSet resultSet = ps.executeQuery();
// 定义一个User变量
User user = null;
while (resultSet.next()) {
// 取值
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String password = resultSet.getString("password");
String nickname = resultSet.getString("nickname");
// 封装数据
user = new User(id, username, password, nickname);
}// 归还连接
dataSource.addBack(connection);
System.out.println("获取连接之后,连接池中连接的数量:"+MyDataSource01.size());// 5// 5.释放资源
JDBCUtils.release(resultSet, ps, null);System.out.println(user);
}
} -
自定义连接池-进阶版本
-
分析
在初级版本版本中, 我们定义的方法是getAbc(). 因为是自定义的.如果改用李四的自定义的连接池,李四定义的方法是getCon(), 那么我们的源码就需要修改, 这样不方便维护. 所以sun公司定义了一个接口DataSource,让自定义连接池有了规范
实现
-
概述: javax.sql.DataSource是Java为数据库连接池提供的公共接口,各个厂商(用户)需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!
-
分析:
-
创建连接池类, 实现DataSource接口,重写方法
-
在连接池类中,定义一个LinkedList集合(表示连接池)
-
在连接池类的静态代码块中,创建固定数量的连接,并存储到LinkedList集合中
-
使用重写的方法getConnection,来获取连接对象
-
提供一个公共的非静态方法来归还连接对象(addBack)
-
提供一个公共的静态方法来获取连接池中连接的数量
-
public class MyDataSource02 implements DataSource {
//- 在连接池类中,定义一个LinkedList集合(表示连接池)
private static LinkedList<Connection> pools = new LinkedList<>();//- 在连接池类的静态代码块中,创建固定数量的连接,并存储到LinkedList集合中
static {
try {
for (int i = 0; i < 5; i++) {
// 创建连接
Connection connection = JDBCUtils.getConnection();
// 把连接添加到连接池中
pools.add(connection);
}
} catch (SQLException e) {
e.printStackTrace();
}
}//- 提供一个公共的非静态方法来获取连接对象(getAbc)
/* public Connection getAbc(){
// 获取连接池中第一个连接
Connection connection = pools.removeFirst();
// 返回连接
return connection;
}*/
@Override
public Connection getConnection() throws SQLException {
// 获取连接池中第一个连接
Connection connection = pools.removeFirst();
// 返回连接
return connection;
}
//- 提供一个公共的非静态方法来归还连接对象(addBack)
public void addBack(Connection connection){
pools.addLast(connection);
}//- 提供一个公共的静态方法来获取连接池中连接的数量
public static int size(){
return pools.size();
}@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}@Override
public void setLogWriter(PrintWriter out) throws SQLException {}
@Override
public void setLoginTimeout(int seconds) throws SQLException {}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
测试: -
public class Test {
public static void main(String[] args) throws Exception {
// 创建连接池对象
MyDataSource02 dataSource = new MyDataSource02();
System.out.println("获取连接之前,连接池中连接的数量:"+MyDataSource02.size());// 5// 1.注册驱动,获得连接
Connection connection = dataSource.getConnection();
System.out.println("获取连接之后,连接池中连接的数量:"+MyDataSource02.size());// 4
// 2.预编译sql语句,得到预编译对象
String sql = "select * from user where id = ?";
PreparedStatement ps = connection.prepareStatement(sql);// 3.设置sql语句参数
ps.setInt(1, 2);// 4.执行sql语句,处理结果
ResultSet resultSet = ps.executeQuery();
// 定义一个User变量
User user = null;
while (resultSet.next()) {
// 取值
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String password = resultSet.getString("password");
String nickname = resultSet.getString("nickname");
// 封装数据
user = new User(id, username, password, nickname);
}// 归还连接
dataSource.addBack(connection);
System.out.println("获取连接之后,连接池中连接的数量:"+MyDataSource02.size());// 5// 5.释放资源
JDBCUtils.release(resultSet, ps, null);System.out.println(user);
}
}
-
-
进阶版后存在的问题分析
-
实现DataSource接口后,addBack()又有问题了
-
在进阶版本中, 我们定义的归还连接的方法是addBack(). 因为是自定义的连接池.如果改用李四的自定义的连接池,李四定义的归还连接的方法是back(), 那么我们的源码就需要修改, 这样不方便维护.
-
DataSource接口中也没有定义归还连接的方法,所以只要自定义的连接池,归还连接的方法就可以随便定义,不方便维护.
-
-
解决办法: 能不能不引入新的api,直接调用之前的connection.close(),但是这个close不是关闭,而是归还
-
Connection原有的close方法是关闭连接(销毁连接)
-
增强close方法,把原有关闭连接的功能变成归还连接的功能,这样连接池中就不需要定义归还连接的方法了
-
-
继承
-
条件:可以控制父类, 最起码知道父类的名字
-
返回的连接对象所属类的类名无法得知,只知道该类是实现了Connection接口
-
-
装饰者模式
-
作用:改写已存在的类的某个方法或某些方法
-
条件:
-
装饰类和被装饰类要实现同一个接口
-
装饰类里面要拿到被装饰类的引用
-
在装饰类中,对需要增强的方法进行增强
-
在装饰类中,对不需要增强的方法就调用被装饰类中原有的方法
-
-
自定义连接池-终极版本
-
创建增强的连接类,对close方法进行增强,其余方法依然调用原有的连接对象的方法
-
创建连接池类, 实现DataSource接口,重写方法
-
在连接池类中,定义一个LinkedList集合(表示连接池)
-
在连接池类的静态代码块中,创建固定数量的连接,并存储到LinkedList集合中
-
使用重写的方法getConnection,来获取连接对象----->增强的连接对象
-
提供一个公共的静态方法来获取连接池中连接的数量
-
与进阶版的区别:
-
连接池类中不需要提供归还连接的方法
-
getConnection获得连接的方法不再返回被增强的连接对象,而是返回增强的连接对象
-
public class MyConnectionWrapper implements Connection {
private Connection connection;// 接收被增强的连接对象
private LinkedList<Connection> pools;// 接收连接池
public MyConnectionWrapper(Connection connection, LinkedList<Connection> pools) {
this.connection = connection;
this.pools = pools;
}
@Override
public void close() throws SQLException {
// 归还连接--->连接池,被增强的连接对象
pools.addLast(connection);
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return connection.prepareStatement(sql);
}
// ....
}
public class MyDataSource03 implements DataSource {
//- 在连接池类中,定义一个LinkedList集合(表示连接池)
private static LinkedList<Connection> pools = new LinkedList<>();
//- 在连接池类的静态代码块中,创建固定数量的连接,并存储到LinkedList集合中
static {
try {
for (int i = 0; i < 5; i++) {
// 创建连接
Connection connection = JDBCUtils.getConnection();
// 把连接添加到连接池中
pools.add(connection);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
//- 提供一个公共的非静态方法来获取连接对象(getAbc)
/* public Connection getAbc(){
// 获取连接池中第一个连接
Connection connection = pools.removeFirst();
// 返回连接
return connection;
}*/
@Override
public Connection getConnection() throws SQLException {
// 获取连接池中第一个连接
Connection connection = pools.removeFirst();// 被增强的连接对象
// 创建增强的连接对象,传入被增强的连接对象,以及连接池
MyConnectionWrapper myConnectionWrapper = new MyConnectionWrapper(connection, pools);
// 返回增强的连接对象
return myConnectionWrapper;
}
//- 提供一个公共的非静态方法来归还连接对象(addBack)
/*public void addBack(Connection connection){
pools.addLast(connection);
}*/
//- 提供一个公共的静态方法来获取连接池中连接的数量
public static int size(){
return pools.size();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
测试类
public class Test {
public static void main(String[] args) throws Exception {
// 创建连接池对象
MyDataSource03 dataSource = new MyDataSource03();
System.out.println("获取连接之前,连接池中连接的数量:"+MyDataSource03.size());// 5
// 1.注册驱动,获得连接
Connection connection = dataSource.getConnection();
System.out.println("获取连接之后,连接池中连接的数量:"+MyDataSource03.size());// 4
// 2.预编译sql语句,得到预编译对象
String sql = "select * from user where id = ?";
PreparedStatement ps = connection.prepareStatement(sql);
// 3.设置sql语句参数
ps.setInt(1, 2);
// 4.执行sql语句,处理结果
ResultSet resultSet = ps.executeQuery();
// 定义一个User变量
User user = null;
while (resultSet.next()) {
// 取值
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String password = resultSet.getString("password");
String nickname = resultSet.getString("nickname");
// 封装数据
user = new User(id, username, password, nickname);
}
// 5.释放资源
JDBCUtils.release(resultSet, ps, connection);// connection.close(); 归还连接
System.out.println("获取连接之后,连接池中连接的数量:"+MyDataSource03.size());// 5
System.out.println(user);
}
}