文章目录
一、什么是JDBC
1.1、 JDBC概述
概念:JDBC(Java DataBase Connectivity Java ) 数据库连接, 使用Java语言操作数据库。
1.2、JDBC的本质
JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
图解如下:
二、如何连接数据库
2.1、环境准备
1、导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
2、复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下
3、右键–>Add As Library
2.2、代码实现
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class Test {
public static void main(String[] args) throws Exception {
//1. 导入驱动jar包
//2.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库连接对象
Connection com = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "username", "password");
//4.定义sql语句测试代码
String sql = "update account set balance = 5000 where id = 1 ";
//5.获取执行sql的对象 Statement
Statement stmt = com.createStatement();
//6.执行sql
int count = stmt.executeUpdate(sql);
//7.处理结果
System.out.println(count);
//8.释放资源
stmt.close();
com.close();
}
}
三、语法详解
3.1、DriverManager:驱动管理对象
3.1.1、注册驱动
注册驱动:告诉程序该使用哪一个数据库驱动jar
static void registerDriver(Driver driver)
//注册与给定的驱动程序 DriverManager
代码使用:
Class.forName("com.mysql.jdbc.Driver");
通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
注意:MySQL5之后的驱动jar包可以省略注册驱动的步骤(因为jar包下的META-INF下的services中的java.sql.Driver会自动导入mysql.jdbc.Driver)
3.2.2、获取数据库连接
方法:
static Connection getConnection(String url, String user, String password)
参数:url:指定连接的路径
语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
例子:jdbc:mysql://localhost:3306/db3
细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
user:用户名
password:密码
3.2、Connection:数据库连接对象(功能)
3.2.1、获取执行sql 的对象
方法:
Statement createStatement()//此方法以后较为少用,存在SQL注入问题
PreparedStatement prepareStatement(String sql)//此方法在以后的开发中较为常用
3.2.2、管理事务
回顾:事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
1.开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
2.提交事务:commit()
3.回滚事务:rollback()
3.2.3、使用Connection对象来管理事务
开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
在执行sql之前开启事务(利用静态代码块)
提交事务:commit()
当所有sql都执行完提交事务
回滚事务:rollback()
在catch中回滚事务(做非空判断)
代码:
public class JDBCDemo10 {//利用事务实现转账功能
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
try {
//1.获取连接
conn = JDBCUtils.getConnection();
//开启事务
conn.setAutoCommit(false);
//2.定义sql
//2.1 张三 - 500
String sql1 = "update account set balance = balance - ? where id = ?";
//2.2 李四 + 500
String sql2 = "update account set balance = balance + ? where id = ?";
//3.获取执行sql对象
pstmt1 = conn.prepareStatement(sql1);
pstmt2 = conn.prepareStatement(sql2);
//4. 设置参数
pstmt1.setDouble(1,500);
pstmt1.setInt(2,1);
pstmt2.setDouble(1,500);
pstmt2.setInt(2,2);
//5.执行sql
pstmt1.executeUpdate();
// 手动制造异常
int i = 3/0;
pstmt2.executeUpdate();
//提交事务
conn.commit();
} catch (Exception e) {
//事务回滚
try {
if(conn != null) {
conn.rollback();
}
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
JDBCUtils.close(pstmt1,conn);
JDBCUtils.close(pstmt2,null);
}
}
3.3、Statement:执行sql的对象
3.3.1、执行sql
方法:
boolean execute(String sql)
//可以执行任意的sql
int executeUpdate(String sql)
//1、执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句
//2、返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。
ResultSet executeQuery(String sql)
//执行DQL(select)语句
3.3.2、练习
练习目标:
//1. account表 添加一条记录
String sql = "insert into account values(null ,'王五',3000)";
//2. account表 修改记录
String sql = "update account set balance =1500 where id = 4 ";
//3. account表 删除一条记录
String Sql = "delete from account where id =5 ";
删除为例:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCDemo03 {
public static void main(String[] args) {
Connection conn = null;
Statement stat = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///db3", "username", "password");
String Sql = "delete from account where id =5 ";
stat = conn.createStatement();
int count = stat.executeUpdate(Sql);
if (count > 0) {
System.out.println("执行成功");
} else {
System.out.println("执行失败");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
if (stat != null) {
try {
stat.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
stat.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
3.4、ResultSet:结果集对象,封装查询结果
boolean next(): 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true
getXxx(参数):获取数据
如:int getInt() String getString()
参数列表:int:则为列的编号,列编号从1开始,如: getString(1)
String:代表列名称,如: getDouble("balance")
注:游标越界问题。
避免越界的使用方法:
while(rs.next()){
int id = rs.getInt(1);
String name = rs.getString("name");
double balance = rs.getDouble(3);
System.out.println(id + "---" + name + "---" + balance);
}
3.5、将连接和释放封装成工具类
由于连接数据库和释放资源过于频繁,现在将这两个功能封装成工具类,便于以后的使用。
/* 获取连接和释放资源的工具类*/
public class JDBCUtils {
//获取连接的方法
public static Connection getConnection() {
Connection conn = null;
//1、注册数据库驱动
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
//2、获取连接
return conn;
}
//释放资源的方法
public static void releaseConnection(Connection conn, Statement st, ResultSet rs) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.6、Statement(存在的问题)
3.6.1、拼接SQL过于繁琐
例如以下sql语句
String sql = "insert into employees(last_name,email,salary,deptId,m_id) values('" + employee.getLast_name() + "','" + employee.getEmail() + "'," + employee.getSalary() + "," + employee.getId() + "," + employee.getM_id() + ")";
3.6.2 、SQL注入问题
使用静态的SQL语句,容易产生SQL注入的问题,而预编译SQL则不会。
看下面一段SQL代码:
String sql = "select * from user where username = '"+username+"' and password = '"+password+"' ";
1、由于username 和password都是拼接的字符串,所以在传参的时候,会存在安全问题。
如果username随意,密码为 x or ‘x’ = 'x 以c为例
2、sql的代码就是:select * from user where username = ‘fhdsjkf’ and password = ‘b’ or ‘a’ = ‘b’ ,显然可以为true,这样就能跳过登录了啊!多危险啊。
改进的代码:
String sql = "select *from user where username= ? and password =?";
3、解决sql注入问题:使用PreparedStatement对象来解决
4、预编译的SQL:参数使用?作为占位符,这样就解决了SQL注入的问题。
3.6.3 、不能插入Blob类型的值
无法通过拼接插入Blob类型的值。
3.7、PreparedStatement:执行sql的对象
3.7.1 、使用步骤
- 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
- 注册驱动
- 获取数据库连接对象 Connection
- 定义sql
注意:sql的参数使用?作为占位符。 如:select * from user where username = ? and password = ?;- 获取执行sql语句的对象 PreparedStatement Connection.prepareStatement(String sql)
- 给?赋值:
方法: setXxx(参数1,参数2)
参数1:?的位置编号 从1 开始
参数2:?的值- 执行sql,接受返回结果,不需要传递sql语句
- 处理结果
- 释放资源
3.7.2 、曾删查
增
@Test
public void testPreparedStatementUpdate() throws Exception { // 增加一个admin
//1、获取数据库连接
Connection conn = JDBCUtils.getConnection();
//2、编写sql语句
String sql = "insert into users(username,password,email) values(?,?,?)";
//3、预编译SQL
PreparedStatement pst = conn.prepareStatement(sql);
//4、填充占位符
pst.setString(1, "admin2");
pst.setString(2, "666666");
pst.setString(3, "admin2@qq.com");
//5、执行更新操作
int len = pst.executeUpdate();
System.out.println(len > 0 ? "添加成功!" : "添加失败!");
//6、释放资源
JDBCUtils.releaseConnection(conn, pst, null);
}
删
@Test
public void testPreparedStatementDelete() throws Exception { // 删除admin
// 1、获取数据库连接
Connection conn = JDBCUtils.getConnection();
//2、定义sql语句
String sql = "delete from users where id =? or id = ?";
//3、预编译SQL
PreparedStatement pst = conn.prepareStatement(sql);
//4、填充占位符
pst.setInt(1, 3);
pst.setInt(2, 4);
//5、执行sql语句
int len = pst.executeUpdate();
System.out.println((len > 0 ? "操作成功!" : "操作失败!") + "影响行数:" + len);
//6、释放资源
JDBCUtils.releaseConnection(conn, pst, null);
}
查
@Test
public void testPreparedStatementQuery() throws Exception { // 查询演示
//1、获取数据库连接
Connection conn = JDBCUtils.getConnection();
//2、定义SQL语句
String sql = "select *from users where username = ? and password = ?";
//3、预编译sql
PreparedStatement pst = conn.prepareStatement(sql);
//4、填充占位符
pst.setString(1, "admin");
pst.setString(2, "1234562");
//5、执行sql语句 获取查询结果集
ResultSet rs = pst.executeQuery();
//6、处理结果集打印输出
while (rs.next()) {
int id = rs.getInt(1);
String username = rs.getString(2);
String password = rs.getString(3);
String email = rs.getString(4);
User user = new User(id, username, password, email);
System.out.println(user);
}
//7、释放资源
JDBCUtils.releaseConnection(conn, pst, rs);
}
3.7.2 、获取自增长的键和值
1)通过Statement接口中的常量RETURN_GENERATED_KEYS返回自增长的键值
2)通过getGenerateKeys()方法来获取自增长的结果集
@Test
public void testGetAutoKey() throws SQLException {
//1、获取数据库连接
Connection conn = JDBCUtils.getConnection();
//2、定义SQL
//String sql = "insert into users(username,password,email) values(?,?,?)";
String sql = "insert into users(username,password,email) values(?,?,?)";
//3、预编译SQL语句
PreparedStatement pst = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
//4、填充占位符
pst.setString(1, "admin3");
pst.setString(2, "888888");
pst.setString(3, "admin3@taobao.com");
//5、执行sql语句
int len = pst.executeUpdate();
// System.out.println(len > 0 ? "插入成功!" : "插入失败!");
if (len > 0) {
// 获取自增长的键值
//因为一次可以插入多条数据 可能有多个键值
ResultSet rs = pst.getGeneratedKeys();
while (rs.next()) {
// 获取自增长的值为
System.out.println("自增长的id值为" + rs.getInt(1));
}
//关闭资源
rs.close();
}
//6、关闭资源
JDBCUtils.releaseConnection(conn, pst, null);
}
3.7.2 、批量插入数据
建表
CREATE TABLE t_batch(
id INT PRIMARY KEY,
`name` VARCHAR(255) NOT NULL
);
不加参数
String url = "jdbc:mysql://localhost:3306/test";
@Test
public void testBatch() throws ClassNotFoundException, SQLException {
//获取开始的时间
long start = System.currentTimeMillis();
//1、注册数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//2、设置数据库连接的相关信息
String url = "jdbc:mysql://localhost:3306/test";
String username = "root";
String password = "root";
//3、获取连接
Connection conn = DriverManager.getConnection(url, username, password);
//4、定义sql语句
String sql = "insert into t_batch values(?,?)";
//5、预编译SQL
PreparedStatement pst = conn.prepareStatement(sql);
//6、填充占位符
for (int i = 1; i <= 1000; i++) {
pst.setObject(1, i);
pst.setObject(2, "dept" + i);
//将sql语句积攒起来 然后一起执行
pst.addBatch();
}
//7、执行SQL
pst.executeBatch();
//8、释放资源
JDBCUtils.releaseConnection(conn, pst, null);
//获取结束的时间
long end = System.currentTimeMillis();
System.out.println("总耗时为:" + (end - start));
}
不加参数的耗时:
加了参数后:
String url = "jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true";
truncate t_batch 删除表并重建
3.7.3 、开启事务
1)mysql中每个连接是自动提交的,如果要开启事务,需要设置成手动提交。
2)Connection.setAutoCommit(false) 开启事务
Connection.commit() 提交事务
Connection.rollback() 回滚事务’
Users表
books表
CREATE TABLE books(
id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
price DOUBLE(11,2),
sales INT NOT NULL,
stock INT NOT NULL
);
插入数据
INSERT INTO books(title,price,sales,stock) VALUES
("Java的美",120.00,100,100),
("MySQL的美",100.00,100,100);
需求: 模拟一下,购买图书,一次只能买一本,用户花钱。
1)更新书的销售量
2)更新书的库存
3)更新用户的余额
未开启事务之前:
Java代码
@Test
public void testTransaction() {
//1、获取数据库的链接
Connection conn = JDBCUtils.getConnection();
//2、编写SQL语句
String sql = "update books set sales = sales + 1 where id = ?";
String sql2 = "update books set stock = stock - 1 where id = ?";
String sql3 = "update users set balance = balance - ? where id = ?";
PreparedStatement pst1 = null;
PreparedStatement pst2 = null;
PreparedStatement pst3 = null;
try {
//3、预编译SQL语句
pst1 = conn.prepareStatement(sql);
pst1.setInt(1, 1);
pst2 = conn.prepareStatement(sql2);
pst2.setInt(1, 1);
pst3 = conn.prepareStatement(sql3);
pst3.setDouble(1, 120);
pst3.setInt(2, 1);
//5、执行sql
int len1 = pst1.executeUpdate();
System.out.println(len1 > 0 ? "第一条SQL语句执行成功!" : "第一条SQL语句执行成功!");
int len2 = pst2.executeUpdate();
System.out.println(len1 > 0 ? "第二条SQL语句执行成功!" : "第二条SQL语句执行成功!");
int len3 = pst3.executeUpdate();
System.out.println(len1 > 0 ? "第三条SQL语句执行成功!" : "第三条SQL语句执行成功!");
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.releaseConnection(null, pst1, null);
JDBCUtils.releaseConnection(null, pst2, null);
JDBCUtils.releaseConnection(conn, pst3, null);
}
}
运行截图:
将数据恢复并开启事务:
代码:
@Test
public void testTransaction() {
//1、获取数据库的链接
Connection conn = JDBCUtils.getConnection();
//2、编写SQL语句
String sql = "update books set sales = sales + 1 where id = ?";
String sql2 = "update books set stock = stock - 1 where id = ?";
String sql3 = "update users set balance = balance - ? where id = ?";
PreparedStatement pst1 = null;
PreparedStatement pst2 = null;
PreparedStatement pst3 = null;
try {
//开启事务:
conn.setAutoCommit(false);
//3、预编译SQL语句
pst1 = conn.prepareStatement(sql);
pst1.setInt(1, 1);
pst2 = conn.prepareStatement(sql2);
pst2.setInt(1, 1);
pst3 = conn.prepareStatement(sql3);
pst3.setDouble(1, 120);
pst3.setInt(2, 1);
//5、执行sql
int len1 = pst1.executeUpdate();
System.out.println(len1 > 0 ? "第一条SQL语句执行成功!" : "第一条SQL语句执行成功!");
int len2 = pst2.executeUpdate();
System.out.println(len1 > 0 ? "第二条SQL语句执行成功!" : "第二条SQL语句执行成功!");
int len3 = pst3.executeUpdate();
System.out.println(len1 > 0 ? "第三条SQL语句执行成功!" : "第三条SQL语句执行成功!");
//如果没有出现异常
conn.commit();
} catch (SQLException e) {
//出现异常 回滚事务
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
} finally {
JDBCUtils.releaseConnection(null, pst1, null);
JDBCUtils.releaseConnection(null, pst2, null);
JDBCUtils.releaseConnection(null, pst2, null);
}
//将事务设置为自动提交
try {
conn.setAutoCommit(true);
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
还是得到同未开启事务的样子而数据库中的数据库确没有改变
修改图书的价格为 66元
UPDATE books SET price = 66.00 WHERE id = 1;
//吸怪此处的pt3.setDouble
pst3 = conn.prepareStatement(sql3);
pst3.setDouble(1, 66.00);
执行结果:
books表
Users表
注:
1、后期都会使用PreparedStatement来完成增删改查的所有操作
2、可以防止SQL注入
3、效率更高
四、数据库连接池
4.1、什么是数据库连池
连接对象的缓冲区。负责申请,分配管理,释放连接的操作。
4.2、为什么要使用数据库连接池
不使用数据库连接池,每次都通过DriverManager获取新连接,用完直接抛弃断开,连接的利用率太低,太浪费。
对于数据库服务器来说,压力太大了。我们数据库服务器和Java程序对连接数也无法控制,很容易导致数据库服务器崩溃。
我们就希望能管理连接。
我们可以建立一个连接池,这个池中可以容纳一定数量的连接对象,一开始,我们可以先替用户先创建好一些连接对象,
等用户要拿连接对象时,就直接从池中拿,不用新建了,这样也可以节省时间。然后用户用完后,放回去,别人可以接着用。
可以提高连接的使用率。当池中的现有的连接都用完了,那么连接池可以向服务器申请新的连接放到池中。
直到池中的连接达到“最大连接数”,就不能在申请新的连接了,如果没有拿到连接的用户只能等待。
4.3、市面上有很多现成的数据库连接池技术:
JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口(通常被称为数据源),该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现:
- DBCP 是Apache提供的数据库连接池,速度相对c3p0较快,但因自身存在BUG,Hibernate3已不再提供支持
- C3P0 是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以
- Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
- BoneCP 是一个开源组织提供的数据库连接池,速度快
- Druid 是阿里提供的数据库连接池,据说是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池
4.4、阿里的德鲁伊连接池技术
(1)加入jar包
例如:druid-1.1.10.jar
(2)代码步骤
第一步:建立一个数据库连接池
第二步:设置连接池的参数
第三步:获取连接
配置 | 缺省 | 说明 |
---|---|---|
name | 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this) | |
jdbcUrl | 连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto | |
username | 连接数据库的用户名 | |
password | 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/使用ConfigFilter | |
driverClassName | 根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下) | |
initialSize | 0 | 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 |
maxActive | 8 | 最大连接池数量 |
maxIdle | 8 | 已经不再使用,配置了也没效果 |
minIdle | 最小连接池数量 | |
maxWait | 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 | |
poolPreparedStatements | false | 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。 |
maxOpenPreparedStatements | -1 | 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 |
validationQuery | 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。 | |
testOnBorrow | true | 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
testOnReturn | false | 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 |
testWhileIdle | false | 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 |
timeBetweenEvictionRunsMillis | 有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明 | |
numTestsPerEvictionRun | 不再使用,一个DruidDataSource只支持一个EvictionRun | |
minEvictableIdleTimeMillis | ||
connectionInitSqls | 物理连接初始化的时候执行的sql | |
exceptionSorter | 根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接 | |
filters | 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall | |
proxyFilters | 类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系 |
public class TestPool {
public static void main(String[] args) throws SQLException {
//1、创建数据源(数据库连接池)对象
DruidDataSource ds =new DruidDataSource();
//2、设置参数
//(1)设置基本参数
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/test");
ds.setUsername("root");
ds.setPassword("123456");
//(2)设置连接数等参数
ds.setInitialSize(5);//一开始提前申请好5个连接,不够了,重写申请
ds.setMaxActive(10);//最多不超过10个,如果10都用完了,还没还回来,就会出现等待
ds.setMaxWait(1000);//用户最多等1000毫秒,如果1000毫秒还没有人还回来,就抛异常了
//3、获取连接
for (int i = 1; i <=15; i++) {
Connection conn = ds.getConnection();
System.out.println("第"+i+"个:" + conn);
//如果这里没有关闭,就相当于没有还
// conn.close();#这里关闭,是还回池中
}
}
}
借了就还的情况:
/*
* 测试德鲁伊数据库连接池
* */
@Test
public void testDruidDataSource() {
//1、创建数据库连接池的对象
DruidDataSource dds = new DruidDataSource();
//2、设置连接数据库的信息
dds.setDriverClassName("com.mysql.jdbc.Driver");
dds.setUrl("jdbc:mysql://localhost:3306/test");
dds.setUsername("root");
dds.setPassword("root");
//3、设置链接参数
dds.setInitialSize(5);
//4、设置最大连接数
dds.setMaxActive(10);
//5、设置最大等待时间
dds.setMaxWait(5000);
//3、获取链接
for (int i = 1; i < 11; i++) {
try {
Connection conn = dds.getConnection();
System.out.println("第" + i + "个" + conn);
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
借了不还的情况:
4.5、DruidDataSourceFactory
创建druid.properties配置文件
username=root
password=root
url=jdbc:mysql://localhost:3306/test
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxWait=10
maxActive=10000
@Test
public void testDruidDataFactory() throws Exception {
//1、创建properties对象
Properties pro = new Properties();
//2、加载类路径下的properties文件 作为一个流
InputStream is = PoolTest.class.getClassLoader().getResourceAsStream("druid.properties");
//3、加载properties文件
pro.load(is);
//4、创建数据源
DataSource dataSource = DruidDataSourceFactory.createDataSource(pro);
//5、获取连接
Connection conn = dataSource.getConnection();
System.out.println(conn);
//6、关闭连接
conn.close();
conn.close();
}
结果截图:
五、封装JDBCTools
配置文件:src/jdbc.properties
*
* 通过德鲁伊连接池获取连接的工具类
* */
public class JDBCUtils2 {
private static DataSource dataSource;
static {
//1、创建properties对象
Properties pro = new Properties();
//2、将类路径下的properties文件读成一个流
InputStream is = PoolTest.class.getClassLoader().getResourceAsStream("druid.properties");
try {
//3、将做为流的properties加载进来
pro.load(is);
//4、创建数据源
dataSource = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接的方法
public static Connection getConnection() {
//从数据源中获取连接
Connection conn = null;
try {
conn = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
//释放连接的方法
public static void relreaseConnection(Connection conn, ResultSet rs, Statement st) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
/*测试JDBCUtils2
* */
@Test
public void testJDBCUtils() {
//1、获取连接
Connection conn = JDBCUtils2.getConnection();
System.out.println(conn);
//2、释放连接
JDBCUtils2.relreaseConnection(conn, null, null);
}
测试JDBCUtils2
更新操作
//通过JDBCUtils2来实现更新的功能
@Test
public void testPreparedStatementUpdateJDBCUtils2() throws Exception {
//1、获取连接
Connection conn = JDBCUtils2.getConnection();
//2、写sql语句
String sql = "update users set password = ?,email = ? where id = ?";
//3、预编译sql语句
PreparedStatement pst = conn.prepareStatement(sql);
//4、填充占位符
pst.setString(1, "111111");
pst.setString(2, "admin@sina.com");
pst.setInt(3, 1);
//5、执行sql
pst.executeUpdate();
//6、释放资源
JDBCUtils2.relreaseConnection(conn, null, pst);
}
删除操作
//通过JDBCUtils2来实现删除的操作
@Test
public void testPreparedStatementDeleteJDBCUtils2() throws Exception {
//1、获取数据库连接
Connection conn = JDBCUtils2.getConnection();
//2、定义sql语句
String sql = "delete from users where id = ?";
//3、预编译sql
PreparedStatement pst = conn.prepareStatement(sql);
//4、填充占位符
pst.setInt(1, 5);
//5、执行sql语句
pst.executeUpdate();
//6、释放资源
JDBCUtils2.relreaseConnection(conn, null, pst);
}