汕头建站网站模板/宁波seo外包服务商
JDBC
- 1 JDBC概述
- 1.1 JDBC概念
- 1.2 JDBC本质
- 1.3 JDBC好处
- 2 JDBC快速入门
- 2.1 编写代码步骤
- 2.2 具体操作
- 3 JDBC API详解
- 3.1 DriverManager
- 3.2 Connection
- 3.2.1 获取执行对象
- 3.2.2 事务管理
- 3.3 Statement
- 3.3.1 概述
- 3.3.2 代码实现
- 3.4 ResultSet
- 3.4.1 概述
- 3.4.2 代码实现
- 3.5 案例
- 3.6 PreparedStatement
- 3.6.1 SQL注入
- 3.6.2 代码模拟SQL注入问题
- 3.6.3 PreparedStatement概述
- 3.6.4 使用PreparedStatement改进
- 3.6.5 PreparedStatement原理
- 4 数据库连接池
- 4.1 数据库连接池简介
- 4.2 数据库连接池实现
- 4.3 Driud使用
- druid配置详解
1 JDBC概述
在开发中我们使用的是java语言,那么势必要通过java语言操作数据库中的数据。这就是接下来要学习的JDBC。
1.1 JDBC概念
JDBC 就是使用Java语言操作关系型数据库的一套API
全称:( Java DataBase Connectivity ) Java 数据库连接
我们开发的同一套Java代码是无法操作不同的关系型数据库,因为每一个关系型数据库的底层实现细节都不一样。如果这样,问题就很大了,在公司中可以在开发阶段使用的是MySQL数据库,而上线时公司最终选用oracle数据库,我们就需要对代码进行大批量修改,这显然并不是我们想看到的。我们要做到的是同一套Java代码操作不同的关系型数据库,而此时sun公司就指定了一套标准接口(JDBC),JDBC中定义了所有操作关系型数据库的规则。众所周知接口是无法直接使用的,我们需要使用接口的实现类,而这套实现类(称之为:驱动)就由各自的数据库厂商给出。
1.2 JDBC本质
- 官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口
- 各个数据库厂商去实现这套接口,提供数据库驱动jar包
- 我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类
1.3 JDBC好处
- 各数据库厂商使用相同的接口,Java代码不需要针对不同数据库分别开发
- 可随时替换底层数据库,访问数据库的Java代码基本不变
以后编写操作数据库的代码只需要面向JDBC(接口),操作哪儿个关系型数据库就需要导入该数据库的驱动包,如需要操作MySQL数据库,就需要再项目中导入MySQL数据库的驱动包。如下图就是MySQL驱动包
2 JDBC快速入门
先来看看通过Java操作数据库的流程
第一步:编写Java代码
第二步:Java代码将SQL发送到MySQL服务端
第三步:MySQL服务端接收到SQL语句并执行该SQL语句
第四步:将SQL语句执行的结果返回给Java代码
2.1 编写代码步骤
-
创建工程,导入驱动jar包
jar包下载位置及JDBC配置 -
注册驱动
Class.forName("com.mysql.jdbc.Driver");
-
获取连接
Connection conn = DriverManager.getConnection(url, username, password);
Java代码需要发送SQL给MySQL服务端,就需要先建立连接
-
定义SQL语句
String sql = “update…” ;
-
获取执行SQL对象
执行SQL语句需要SQL执行对象,而这个执行对象就是Statement对象
Statement stmt = conn.createStatement();
-
执行SQL
stmt.executeUpdate(sql);
-
处理返回结果
-
释放资源
2.2 具体操作
- 创建新的空的项目,定义项目的名称,并指定位置
- 对项目进行设置,JDK版本、编译版本
- 创建模块,指定模块的名称及位置
-
导入驱动包
将mysql的驱动包放在模块下的lib目录(随意命名)下,并将该jar包添加为库文件
- 在添加为库文件的时候,有如下三个选项
- Global Library : 全局有效
- Project Library : 项目有效
- Module Library : 模块有效
- 在src下创建类
- 编写代码如下
com.mysql.jdbc.Driver 是 mysql-connector-java 5版本中的
com.mysql.cj.jdbc.Driver 是 mysql-connector-java 6版本及以上中的
package com.sukijdbc;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;/*** @author Eumenides-Suki* @version 1.0* @description JDBC快速入门* @date 2023/2/11 0:13*/
public class JDBCDemo {public static void main(String[] args) throws ClassNotFoundException, SQLException {//1. 注册驱动 (高版本自动注册,下面语句不注释会报错)//Class.forName("com.mysql.cj.jdbc.Diver");//2. 获取连接//这是mysql8.0新的url连接,需要设置时区,这里数据库名为db1String url = "jdbc:mysql://localhost:3306/db1?useSSL=false&characterEncoding=utf-8&serverTimezone=Hongkong";String username = "root";String password = "121121";Connection conn = DriverManager.getConnection(url,username,password);//3. 定义sqlString sql = "update account set money = 2000 where id = 1";//4.获取执行sql的对象StatementStatement stmt = conn.createStatement();//5.执行sqlint count = stmt.executeUpdate(sql);//受影响的行数//6.处理结果System.out.println(count);//7.释放资源stmt.close();conn.close();}
}}
}
然后执行一下代码:
3 JDBC API详解
3.1 DriverManager
DriverManager(驱动管理类)作用:
-
注册驱动
registerDriver方法是用于注册驱动的,但是我们之前做的入门案例并不是这样写的。而是如下实现
Class.forName("com.mysql.jdbc.Driver");
我们查询MySQL提供的Driver类,看它是如何实现的,源码如下:
在该类中的静态代码块中已经执行了
DriverManager
对象的registerDriver()
方法进行驱动的注册了,那么我们只需要加载Driver
类,该静态代码块就会执行。而Class.forName("com.mysql.jdbc.Driver");
就可以加载Driver
类。提示:
- MySQL 5之后的驱动包,可以省略注册驱动的步骤
- 自动加载jar包中META-INF/services/java.sql.Driver文件中的驱动类
-
获取数据库连接
参数说明:
-
url : 连接路径
语法:jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2…
示例:jdbc:mysql://127.0.0.1:3306/db1?useSSL=false&characterEncoding=utf-8&serverTimezone=Hongkong"
细节:
-
如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称?参数键值对
-
配置 useSSL=false 参数,禁用安全连接方式,解决警告提示
-
-
user :用户名
-
poassword :密码
-
3.2 Connection
Connection(数据库连接对象)作用:
- 获取执行 SQL 的对象
- 管理事务
3.2.1 获取执行对象
-
普通执行SQL对象
Statement createStatement()
入门案例中就是通过该方法获取的执行对象。
-
预编译SQL的执行SQL对象:防止SQL注入
PreparedStatement prepareStatement(sql)
通过这种方式获取的
PreparedStatement
SQL语句执行对象是我们一会重点要进行讲解的,它可以防止SQL注入。 -
执行存储过程的对象
CallableStatement prepareCall(sql)
通过这种方式获取的
CallableStatement
执行对象是用来执行存储过程的,而存储过程在MySQL中不常用,所以这个我们将不进行讲解。
3.2.2 事务管理
先回顾一下MySQL事务管理的操作:
- 开启事务 : BEGIN; 或者 START TRANSACTION;
- 提交事务 : COMMIT;
- 回滚事务 : ROLLBACK;
MySQL默认是自动提交事务
接下来学习JDBC事务管理的方法。
Connection接口中定义了3个对应的方法:
- 开启事务
setAutoCommit(boolen autoCommit);
参与autoCommit 表示是否自动提交事务,true表示自动提交事务,false表示手动提交事务。而开启事务需要将该参数设为为false。
- 提交事务
commit();
- 回滚事务
rollback();
具体代码实现如下:
/*** JDBC API 详解:Connection*/
public class JDBCDemo3_Connection {public static void main(String[] args) throws Exception {//1. 注册驱动//Class.forName("com.mysql.jdbc.Driver");//2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写String url = "jdbc:mysql:///db1?useSSL=false";String username = "root";String password = "1234";Connection conn = DriverManager.getConnection(url, username, password);//3. 定义sqlString sql1 = "update account set money = 3000 where id = 1";String sql2 = "update account set money = 3000 where id = 2";//4. 获取执行sql的对象 StatementStatement stmt = conn.createStatement();try {// ============开启事务==========conn.setAutoCommit(false);//5. 执行sqlint count1 = stmt.executeUpdate(sql1);//受影响的行数//6. 处理结果System.out.println(count1);int i = 3/0;//5. 执行sqlint count2 = stmt.executeUpdate(sql2);//受影响的行数//6. 处理结果System.out.println(count2);// ============提交事务==========//程序运行到此处,说明没有出现任何问题,则需求提交事务conn.commit();} catch (Exception e) {// ============回滚事务==========//程序在出现异常时会执行到这个地方,此时就需要回滚事务conn.rollback();e.printStackTrace();}//7. 释放资源stmt.close();conn.close();}
}
3.3 Statement
3.3.1 概述
Statement对象的作用就是用来执行SQL语句。而针对不同类型的SQL语句使用的方法也不一样。
- 执行DDL、DML语句
int executeUpdate (String sql) ;
-
执行DQL语句
ResultSet executeQuery (String sql) ;
3.3.2 代码实现
-
执行DML语句
/*** 执行DML语句* @throws Exception*/ @Test public void testDML() throws Exception {//1. 注册驱动//Class.forName("com.mysql.jdbc.Driver");//2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写String url = "jdbc:mysql:///db1?useSSL=false";String username = "root";String password = "1234";Connection conn = DriverManager.getConnection(url, username, password);//3. 定义sqlString sql = "update account set money = 3000 where id = 1";//4. 获取执行sql的对象 StatementStatement stmt = conn.createStatement();//5. 执行sqlint count = stmt.executeUpdate(sql);//执行完DML语句,受影响的行数//6. 处理结果//System.out.println(count);if(count > 0){System.out.println("修改成功~");}else{System.out.println("修改失败~");}//7. 释放资源stmt.close();conn.close(); }
-
执行DDL语句
/*** 执行DDL语句* @throws Exception*/ @Test public void testDDL() throws Exception {//1. 注册驱动//Class.forName("com.mysql.jdbc.Driver");//2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写String url = "jdbc:mysql:///db1?useSSL=false";String username = "root";String password = "1234";Connection conn = DriverManager.getConnection(url, username, password);//3. 定义sqlString sql = "drop database db2";//4. 获取执行sql的对象 StatementStatement stmt = conn.createStatement();//5. 执行sqlint count = stmt.executeUpdate(sql);//执行完DDL语句,可能是0//6. 处理结果System.out.println(count);//7. 释放资源stmt.close();conn.close(); }
注意:
- 以后开发很少使用java代码操作DDL语句
3.4 ResultSet
3.4.1 概述
ResultSet(结果集对象)作用:
- 封装了SQL查询语句的结果。
而执行了DQL语句后就会返回该对象,对应执行DQL语句的方法如下:
ResultSet executeQuery(sql):执行DQL 语句,返回 ResultSet 对象
那么我们就需要从 ResultSet
对象中获取我们想要的数据。ResultSet
对象提供了操作查询结果数据的方法,如下:
boolean next()
- 将光标从当前位置向前移动一行
- 判断当前行是否为有效行
方法返回值说明:
- true : 有效行,当前行有数据
- false : 无效行,当前行没有数据
xxx getXxx(参数):获取数据
- xxx : 数据类型;如: int getInt(参数) ;String getString(参数)
- 参数
- int类型的参数:列的编号,从1开始
- String类型的参数: 列的名称
如下图为执行SQL语句后的结果
一开始光标指定于第一行前,如图所示红色箭头指向于表头行。当我们调用了 next()
方法后,光标就下移到第一行数据,并且方法返回true,此时就可以通过 getInt("id")
获取当前行id字段的值,也可以通过 getString("name")
获取当前行name字段的值。如果想获取下一行的数据,继续调用 next()
方法,以此类推。
3.4.2 代码实现
/*** 执行DQL* @throws Exception*/
@Test
public void testResultSet() throws Exception {//1. 注册驱动//Class.forName("com.mysql.jdbc.Driver");//2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写String url = "jdbc:mysql:///db1?useSSL=false";String username = "root";String password = "1234";Connection conn = DriverManager.getConnection(url, username, password);//3. 定义sqlString sql = "select * from account";//4. 获取statement对象Statement stmt = conn.createStatement();//5. 执行sqlResultSet rs = stmt.executeQuery(sql);//6. 处理结果, 遍历rs中的所有数据/* // 6.1 光标向下移动一行,并且判断当前行是否有数据while (rs.next()){//6.2 获取数据 getXxx()int id = rs.getInt(1);String name = rs.getString(2);double money = rs.getDouble(3);System.out.println(id);System.out.println(name);System.out.println(money);System.out.println("--------------");}*/// 6.1 光标向下移动一行,并且判断当前行是否有数据while (rs.next()){//6.2 获取数据 getXxx()int id = rs.getInt("id");String name = rs.getString("name");double money = rs.getDouble("money");System.out.println(id);System.out.println(name);System.out.println(money);System.out.println("--------------");}//7. 释放资源rs.close();stmt.close();conn.close();
}
3.5 案例
-
需求:查询account账户表数据,封装为Account对象中,并且存储到ArrayList集合中
-
代码实现
/*** 查询account账户表数据,封装为Account对象中,并且存储到ArrayList集合中* 1. 定义实体类Account* 2. 查询数据,封装到Account对象中* 3. 将Account对象存入ArrayList集合中*/ @Test public void testResultSet2() throws Exception {//1. 注册驱动//Class.forName("com.mysql.jdbc.Driver");//2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写String url = "jdbc:mysql:///db1?useSSL=false";String username = "root";String password = "1234";Connection conn = DriverManager.getConnection(url, username, password);//3. 定义sqlString sql = "select * from account";//4. 获取statement对象Statement stmt = conn.createStatement();//5. 执行sqlResultSet rs = stmt.executeQuery(sql);// 创建集合List<Account> list = new ArrayList<>();// 6.1 光标向下移动一行,并且判断当前行是否有数据while (rs.next()){Account account = new Account();//6.2 获取数据 getXxx()int id = rs.getInt("id");String name = rs.getString("name");double money = rs.getDouble("money");//赋值account.setId(id);account.setName(name);account.setMoney(money);// 存入集合list.add(account);}System.out.println(list);//7. 释放资源rs.close();stmt.close();conn.close(); }
3.6 PreparedStatement
PreparedStatement作用:
- 预编译SQL语句并执行:预防SQL注入问题
3.6.1 SQL注入
SQL注入是通过操作输入来修改事先定义好的SQL语句,用以达到执行代码对服务器进行攻击的方法。
在今天资料下的 day03-JDBC\资料\2. sql注入演示
中修改 application.properties
文件中的用户名和密码,文件内容如下:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=1234
在MySQL中创建名为 test
的数据库
create database test;
在命令提示符中运行今天资料下的 day03-JDBC\资料\2. sql注入演示\sql.jar
这个jar包。
此时我们就能在数据库中看到user表
接下来在浏览器的地址栏输入 localhost:8080/login.html
就能看到如下页面
我们就可以在如上图中输入用户名和密码进行登陆。用户名和密码输入正确就登陆成功,跳转到首页。用户名和密码输入错误则给出错误提示,如下图
但是我可以通过输入一些特殊的字符登陆到首页。
用户名随意写,密码写成 ' or '1' ='1
这就是SQL注入漏洞,也是很危险的。当然现在市面上的系统都不会存在这种问题了,所以大家也不要尝试用这种方式去试其他的系统。
那么该如何解决呢?这里就可以将SQL执行对象 Statement
换成 PreparedStatement
对象。
3.6.2 代码模拟SQL注入问题
@Test
public void testLogin() throws Exception {//2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写String url = "jdbc:mysql:///db1?useSSL=false";String username = "root";String password = "1234";Connection conn = DriverManager.getConnection(url, username, password);// 接收用户输入 用户名和密码String name = "sjdljfld";String pwd = "' or '1' = '1";String sql = "select * from tb_user where username = '"+name+"' and password = '"+pwd+"'";// 获取stmt对象Statement stmt = conn.createStatement();// 执行sqlResultSet rs = stmt.executeQuery(sql);// 判断登录是否成功if(rs.next()){System.out.println("登录成功~");}else{System.out.println("登录失败~");}//7. 释放资源rs.close();stmt.close();conn.close();
}
上面代码是将用户名和密码拼接到sql语句中,拼接后的sql语句如下
select * from tb_user where username = 'sjdljfld' and password = ''or '1' = '1'
从上面语句可以看出条件 username = 'sjdljfld' and password = ''
不管是否满足,而 or
后面的 '1' = '1'
是始终满足的,最终条件是成立的,就可以正常的进行登陆了。
接下来我们来学习PreparedStatement对象.
3.6.3 PreparedStatement概述
PreparedStatement作用:
- 预编译SQL语句并执行:预防SQL注入问题
-
获取 PreparedStatement 对象
// SQL语句中的参数值,使用?占位符替代 String sql = "select * from user where username = ? and password = ?"; // 通过Connection对象获取,并传入对应的sql语句 PreparedStatement pstmt = conn.prepareStatement(sql);
-
设置参数值
上面的sql语句中参数使用 ? 进行占位,在之前之前肯定要设置这些 ? 的值。
PreparedStatement对象:setXxx(参数1,参数2):给 ? 赋值
-
Xxx:数据类型 ; 如 setInt (参数1,参数2)
-
参数:
-
参数1: ?的位置编号,从1 开始
-
参数2: ?的值
-
-
-
执行SQL语句
executeUpdate(); 执行DDL语句和DML语句
executeQuery(); 执行DQL语句
注意:
- 调用这两个方法时不需要传递SQL语句,因为获取SQL语句执行对象时已经对SQL语句进行预编译了。
3.6.4 使用PreparedStatement改进
@Test
public void testPreparedStatement() throws Exception {//2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写String url = "jdbc:mysql:///db1?useSSL=false";String username = "root";String password = "1234";Connection conn = DriverManager.getConnection(url, username, password);// 接收用户输入 用户名和密码String name = "zhangsan";String pwd = "' or '1' = '1";// 定义sqlString sql = "select * from tb_user where username = ? and password = ?";// 获取pstmt对象PreparedStatement pstmt = conn.prepareStatement(sql);// 设置?的值pstmt.setString(1,name);pstmt.setString(2,pwd);// 执行sqlResultSet rs = pstmt.executeQuery();// 判断登录是否成功if(rs.next()){System.out.println("登录成功~");}else{System.out.println("登录失败~");}//7. 释放资源rs.close();pstmt.close();conn.close();
}
执行上面语句就可以发现不会出现SQL注入漏洞问题了。那么PreparedStatement又是如何解决的呢?它是将特殊字符进行了转义,转义的SQL如下:
select * from tb_user where username = 'sjdljfld' and password = '\'or \'1\' = \'1'
3.6.5 PreparedStatement原理
PreparedStatement 好处:
- 预编译SQL,性能更高
- 防止SQL注入:将敏感字符进行转义
Java代码操作数据库流程如图所示:
-
将sql语句发送到MySQL服务器端
-
MySQL服务端会对sql语句进行如下操作
-
检查SQL语句
检查SQL语句的语法是否正确。
-
编译SQL语句。将SQL语句编译成可执行的函数。
检查SQL和编译SQL花费的时间比执行SQL的时间还要长。如果我们只是重新设置参数,那么检查SQL语句和编译SQL语句将不需要重复执行。这样就提高了性能。
-
执行SQL语句
-
接下来我们通过查询日志来看一下原理。
-
开启预编译功能
在代码中编写url时需要加上以下参数。而我们之前根本就没有开启预编译功能,只是解决了SQL注入漏洞。
useServerPrepStmts=true
-
配置MySQL执行日志(重启mysql服务后生效)
在mysql配置文件(my.ini)中添加如下配置
log-output=FILE general-log=1 general_log_file="D:\mysql.log" slow-query-log=1 slow_query_log_file="D:\mysql_slow.log" long_query_time=2
-
java测试代码如下:
/*** PreparedStatement原理* @throws Exception*/ @Test public void testPreparedStatement2() throws Exception {//2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写// useServerPrepStmts=true 参数开启预编译功能String url = "jdbc:mysql:///db1?useSSL=false&useServerPrepStmts=true";String username = "root";String password = "1234";Connection conn = DriverManager.getConnection(url, username, password);// 接收用户输入 用户名和密码String name = "zhangsan";String pwd = "' or '1' = '1";// 定义sqlString sql = "select * from tb_user where username = ? and password = ?";// 获取pstmt对象PreparedStatement pstmt = conn.prepareStatement(sql);Thread.sleep(10000);// 设置?的值pstmt.setString(1,name);pstmt.setString(2,pwd);ResultSet rs = null;// 执行sqlrs = pstmt.executeQuery();// 设置?的值pstmt.setString(1,"aaa");pstmt.setString(2,"bbb");// 执行sqlrs = pstmt.executeQuery();// 判断登录是否成功if(rs.next()){System.out.println("登录成功~");}else{System.out.println("登录失败~");}//7. 释放资源rs.close();pstmt.close();conn.close(); }
-
执行SQL语句,查看
D:\mysql.log
日志如下:上图中第三行中的
Prepare
是对SQL语句进行预编译。第四行和第五行是执行了两次SQL语句,而第二次执行前并没有对SQL进行预编译。
小结:
- 在获取PreparedStatement对象时,将sql语句发送给mysql服务器进行检查,编译(这些步骤很耗时)
- 执行时就不用再进行这些步骤了,速度更快
- 如果sql模板一样,则只需要进行一次检查、编译
4 数据库连接池
4.1 数据库连接池简介
数据库连接池是个容器,负责分配、管理数据库连接(Connection)
它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;
释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏
好处
- 资源重用
- 提升系统响应速度
- 避免数据库连接遗漏
之前我们代码中使用连接是没有使用都创建一个Connection对象,使用完毕就会将其销毁。这样重复创建销毁的过程是特别耗费计算机的性能的及消耗时间的。
而数据库使用了数据库连接池后,就能达到Connection对象的复用,如下图
连接池是在一开始就创建好了一些连接(Connection)对象存储起来。用户需要连接数据库时,不需要自己创建连接,而只需要从连接池中获取一个连接进行使用,使用完毕后再将连接对象归还给连接池;这样就可以起到资源重用,也节省了频繁创建连接销毁连接所花费的时间,从而提升了系统响应的速度。
4.2 数据库连接池实现
-
标准接口:DataSource
官方(SUN) 提供的数据库连接池标准接口,由第三方组织实现此接口。该接口提供了获取连接的功能:
Connection getConnection()
那么以后就不需要通过
DriverManager
对象获取Connection
对象,而是通过连接池(DataSource)获取Connection
对象。 -
常见的数据库连接池
- DBCP
- C3P0
- Druid
我们现在使用更多的是Druid,它的性能比其他两个会好一些。
-
Druid(德鲁伊)
-
Druid连接池是阿里巴巴开源的数据库连接池项目
-
功能强大,性能优秀,是Java语言最好的数据库连接池之一
-
4.3 Driud使用
- 导入jar包 druid-1.1.12.jar
- 定义配置文件
- 加载配置文件
- 获取数据库连接池对象
- 获取连接
druid配置详解
属性 | 说明 | 建议值 |
---|---|---|
url | 数据库的jdbc连接地址。一般为连接oracle/mysql。示例如下: | |
mysql : jdbc:mysql://ip:port/dbname?option1&option2&… | ||
oracle : jdbc:oracle:thin:@ip:port:oracle_sid | ||
username | 登录数据库的用户名 | |
password | 登录数据库的用户密码 | |
initialSize | 启动程序时,在连接池中初始化多少个连接 | 10-50已足够 |
maxActive | 连接池中最多支持多少个活动会话 | |
maxWait | 程序向连接池中请求连接时,超过maxWait的值后,认为本次请求失败,即连接池 | 100 |
没有可用连接,单位毫秒,设置-1时表示无限等待 | ||
minEvictableIdleTimeMillis | 池中某个连接的空闲时长达到 N 毫秒后, 连接池在下次检查空闲连接时,将 | 见说明部分 |
回收该连接,要小于防火墙超时设置 | ||
net.netfilter.nf_conntrack_tcp_timeout_established的设置 | ||
timeBetweenEvictionRunsMillis | 检查空闲连接的频率,单位毫秒, 非正整数时表示不进行检查 | |
keepAlive | 程序没有close连接且空闲时长超过 minEvictableIdleTimeMillis,则会执 | true |
行validationQuery指定的SQL,以保证该程序连接不会池kill掉,其范围不超 | ||
过minIdle指定的连接个数。 | ||
minIdle | 回收空闲连接时,将保证至少有minIdle个连接. | 与initialSize相同 |
removeAbandoned | 要求程序从池中get到连接后, N 秒后必须close,否则druid 会强制回收该 | false,当发现程序有未 |
连接,不管该连接中是活动还是空闲, 以防止进程不会进行close而霸占连接。 | 正常close连接时设置为true | |
removeAbandonedTimeout | 设置druid 强制回收连接的时限,当程序从池中get到连接开始算起,超过此 | 应大于业务运行最长时间 |
值后,druid将强制回收该连接,单位秒。 | ||
logAbandoned | 当druid强制回收连接后,是否将stack trace 记录到日志中 | true |
testWhileIdle | 当程序请求连接,池在分配连接时,是否先检查该连接是否有效。(高效) | true |
validationQuery | 检查池中的连接是否仍可用的 SQL 语句,drui会连接到数据库执行该SQL, 如果 | |
正常返回,则表示连接可用,否则表示连接不可用 | ||
testOnBorrow | 程序 申请 连接时,进行连接有效性检查(低效,影响性能) | false |
testOnReturn | 程序 返还 连接时,进行连接有效性检查(低效,影响性能) | false |
poolPreparedStatements | 缓存通过以下两个方法发起的SQL: | true |
public PreparedStatement prepareStatement(String sql) | ||
public PreparedStatement prepareStatement(String sql, | ||
int resultSetType, int resultSetConcurrency) | ||
maxPoolPrepareStatementPerConnectionSize | 每个连接最多缓存多少个SQL | 20 |
filters | 这里配置的是插件,常用的插件有: | stat,wall,slf4j |
监控统计: filter:stat | ||
日志监控: filter:log4j 或者 slf4j | ||
防御SQL注入: filter:wall | ||
connectProperties | 连接属性。比如设置一些连接池统计方面的配置。 | |
druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 | ||
比如设置一些数据库连接属性: | ||
现在通过代码实现,首先需要先将druid的jar包放到项目下的lib下并添加为库文件
编写配置文件如下:
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///db1?useSSL=false&useServerPrepStmts=true
username=root
password=1234
# 初始化连接数量
initialSize=5
# 最大连接数
maxActive=10
# 最大等待时间
maxWait=3000
使用druid的代码如下:
/*** Druid数据库连接池演示*/
public class DruidDemo {public static void main(String[] args) throws Exception {//1.导入jar包//2.定义配置文件//3. 加载配置文件Properties prop = new Properties();prop.load(new FileInputStream("jdbc-demo/src/druid.properties"));//4. 获取连接池对象DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);//5. 获取数据库连接 ConnectionConnection connection = dataSource.getConnection();System.out.println(connection); //获取到了连接后就可以继续做其他操作了//System.out.println(System.getProperty("user.dir"));}
}
学习笔记 from 黑马程序员
By – Suki 2023/2/10
相关文章:

JavaWeb--JDBC
JDBC1 JDBC概述1.1 JDBC概念1.2 JDBC本质1.3 JDBC好处2 JDBC快速入门2.1 编写代码步骤2.2 具体操作3 JDBC API详解3.1 DriverManager3.2 Connection3.2.1 获取执行对象3.2.2 事务管理3.3 Statement3.3.1 概述3.3.2 代码实现3.4 ResultSet3.4.1 概述3.4.2 代码实现3.5 案例3.6 P…...

大数据框架之Hadoop:入门(四)Hadoop运行模式
Hadoop运行模式包括:本地模式、伪分布式模式以及完全分布式模式。 Hadoop官方网站:http://hadoop.apache.org/ 4.1本地运行模式 4.1.1官方Grep案例 1.创建在hadoop文件夹下面创建一个input文件夹 [roothdp101 hadoop]# mkdir input2.将Hadoop的xml配…...

《爆肝整理》保姆级系列教程python接口自动化(十一)--发送post【data】(详解
简介 前面登录的是传 json 参数,由于其登录机制的改变没办法演示,然而在工作中有些登录不是传 json 的,如 jenkins 的登录,这里小编就以jenkins 登录为案例,传 data 参数,给各位童鞋详细演练一下。 一、…...

【微服务】Nacos注册中心
🚩本文已收录至专栏:微服务探索之旅 👍希望您能有所收获 👍Nacos和Eureka一样也可以充当服务的注册中心,让我们一起看看有何区别? 点击跳转👉【微服务】Eureka注册中心 👍Nacos除了可…...

跟开发打了半个月后,我终于get报bug的正确姿势了
在测试人员提需求的时候,大家经常会看到,测试员和开发一言不合就上BUG。然后开发一下就炸了,屡试不爽,招招致命。 曾经看到有个段子这么写道: 不要对程序员说,你的代码有BUG。他的第一反应是:…...

js万能类型检测Object.prototype.toString.call——定制Object.prototype.toString.call的检测结果
javascript的类型检测 1、typeof typeof操作符可以检测js的基础数据类型,包括number、string、boolean、undefined。因为null在二进制存储的值与object相同,所以typeof检测null会返回object。此为特例 2、instanceof instanceof操作符可以检测某个对…...

激光slam学习笔记2--激光点云数据结构特点可视化查看
背景:不同厂商的激光点云结果存在一定差异,比如有些只有xyz,有些包含其他,如反光率、时间戳、ring等。如何快速判断是个值得学习的点 概要:对于rosbag类型的激光点云,介绍使用rviz快速查看点云结构特点 如…...

SpringBoot笔记【JavaEE】
SpringBoot概念、创建和运行 1.什么是SpringBoot?为什么学习SpringBoot? Spring Boot 就是 Spring 框架的脚⼿架,它就是为了快速开发 Spring 框架⽽诞⽣的。 2.Spring Boot优点 快速集成框架【提供启动添加依赖的功能】内容运行容器【无需…...

目标检测算法之voxelNet与pointpillars对比
算法对比 3D目标检测发展简史 点云目标检测目前发展历经VoxelNet、SECOND、PointPillars、PV-RCNN。 2017年苹果提出voxelnet,是最早的一篇将点云转成voxel体素进行3D目标检测的论文。 然后2018年重庆大学的一个研究生Yan Yan在自动驾驶公司主线科技实习的时候将vo…...

电脑里的连接速度双工模式是什么?怎么设置
双工模式包括全双工、半双工模式。1.半双工1、半双工数据传输允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信。所谓半双工就是指一个时间段内只有一个动作发生。早期的对…...

springboot整合单机缓存ehcache
区别于redis的分布式缓存,ehcache是纯java进程内的单机缓存,根据不同的场景可选择使用,以下内容主要为springboot整合ehcache以及注意事项添加pom引用<dependency><groupId>net.sf.ehcache</groupId><artifactId>ehc…...

在阿里干了2年的测试,总结出来的划水经验
测试新人 我的职业生涯开始和大多数测试人一样,开始接触都是纯功能界面测试。那时候在一家电商公司做测试,做了一段时间,熟悉产品的业务流程以及熟练测试工作流程规范之后,效率提高了,工作比较轻松,这样我…...

硬盘分类及挂载硬盘知识补充和介绍
一、硬盘介绍Linux硬盘分IDE硬盘和SCSI硬盘,目前基本上是SCSI硬盘1.对于IDE硬盘,驱动器标识符为"hdx~",其中"hd"表明分区所在设备的类型,这里是指IDE硬盘了。"x"为盘号(a为基本盘,b为基…...

【MyBatis】自定义映射resultMap
8.1、resultMap处理字段和属性的映射关系 若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射 <!--resultMap:设置自定义映射属性:id:表示自定义映射的唯一标识type:查询的数据要映射的实体类的…...

mysql的锁和事务
mysql的锁 读写锁: 读锁是共享锁,多个用户在同一时刻可以读取同一资源,相互不受干扰写锁是排他锁,写锁会阻塞其他的写锁和读锁,这样可以确保在指定的时间内,只有一个用户可以写入 锁的颗粒度: …...

为什么B站中的弹幕可以不遮挡人物
上班逛B站时摸鱼时,看到了满屏的弹幕,而且还不挡脸,突然心血来潮来看看它是怎么实现的? 不难发现弹幕其实它就是有一个蒙版层div,遮挡在视频组件的上方,z-index层级设置的比较高(这里是11&…...

数据结构 第八章 查找(静态查找表)
集合 1、集合中的数据元素除了属于同一集合外,没有任何的逻辑关系 2、在集合中,每个数据元素都有一个区别于其他元素的唯一标识(键值或者关键字值) 3、集合的运算: 1 查找某一元素是否存在(内部查找、外部查找) 2 将集合中的元素按照它的唯一标识进行排序4、集合的…...

【Python基础】数据类型(元组、列表)
文章目录二. 数据类型2.1 元组 tuple2.1.1 定义特性2.1.2 拼接拷贝2.1.3 元组拆包2.1.4 元组方法 count2.2 列表 list2.2.1 基础定义2.2.2 增删操作2.2.3 连接联合2.2.4 其他常规操作2.2.5 列表推导式2.2.6 生成器表达式2.x 小结:何时使用元组或列表二. 数据类型 Py…...

你了解互联网APP搜索和推荐的背后逻辑么?
1.搜索和推荐无处不在我们习惯了百度、Google、360搜索的便捷,输入你想要搜索的关键词,立马呈现给你一批对应的结果,供你筛选。我们也经常上淘宝、京东、拼多多购物,输入想买的商品,瞬间列出一页一页的商品清单供我们选…...

Bug的级别,按照什么划分
Bug分类和定级一、bug的定义二、bug的类型三、bug的等级四、bug的优先级一、bug的定义一般是指不满足用户需求的则可以认为是bug,狭义指软件程序的漏洞或缺陷,广义指测试工程师或用户提出的软件可改进的细节、或与需求文档存在差异的功能实现等对应三个测…...

微服务项目简介
项目简介 项目模式 电商模式:市面上有5种常见的电商模式,B2B、B2C、 C2B、 C2C、O2O; 1、B2B模式 B2B (Business to Business),是指 商家与商家建立的商业关系。如:阿里巴巴 2、B2C 模式 B2C (Business to Consumer), 就是我们经常看到的供…...

SLAM中坐标轴旋转及ros的接口解释
读完几个loam算法,满篇的坐标轴旋转,还是手写的(作者,用eigen写不好嘛。。。),我滴天适应了好久…,今天就总结一下坐标轴旋转问题。 一、首先,我们看一下ros中关于欧拉角旋转的函数:setRPY、set…...

文件管理(9)
文件管理 0 引言 为什么要引入文件系统? 信息管理的需要:用户面前提供一种规格化的机制,方便用户对文件的存取、提高效率。操作系统本身需要–操作系统本身也不是常驻内存的,也有大量的信息需要存于外存。 1 文件定义 文件&a…...

PyTorch学习笔记:nn.TripletMarginLoss——三元组损失
PyTorch学习笔记:nn.TripletMarginLoss——三元组损失 torch.nn.TripletMarginLoss(margin1.0, p2.0, eps1e-06, swapFalse, size_averageNone, reduceNone, reductionmean)功能:创建一个三元组损失函数(triplet loss),用于衡量输入数据x1,x…...

冒泡排序详解
冒泡排序是初学C语言的噩梦,也是数据结构中排序的重要组成部分,本章内容我们一起探讨冒泡排序,从理论到代码实现,一步步深入了解冒泡排序。排序算法作为较简单的算法。它重复地走访过要排序的数列,一次比较两个元素&am…...

git极快上手指南超级精简版
注:本文参考https://www.liaoxuefeng.com/wiki/896043488029600 原文非常值得一读,作者学识渊博,补充了很多有意思的知识。我仅仅是拾人牙慧。 git是最先进的分布式版本控制系统。 版本控制系统——自动记录系统中文件的改动情况࿰…...

蓝桥杯-最长公共子序列(线性dp)
没有白走的路,每一步都算数🎈🎈🎈 题目描述: 已知有两个数组a,b。已知每个数组的长度。要求求出两个数组的最长公共子序列 序列 1 2 3 4 5 序列 2 3 2 1 4 5 子序列:从其中抽掉某个或多个元素而产生的新…...

GO的并发模式Context
GO的并发模式Context 文章目录GO的并发模式Context一、介绍二、Context三、context的衍生四、示例:Google Web Search4.1 server程序4.2 userip 包4.3 google 包五、使用context包中程序实体实现sync.WaitGroup同样的功能(1)使用sync.WaitGro…...

《Redis实战篇》六、秒杀优化
6、秒杀优化 6.0 压力测试 目的:测试1000个用户抢购优惠券时秒杀功能的并发性能~ ①数据库中创建1000用户 这里推荐使用开源工具:https://www.sqlfather.com/ ,导入以下配置即可一键生成模拟数据 {"dbName":"hmdp",…...

《C++ Primer Plus》第16章:string类和标准模板库(11)
其他库 C 还提供了其他一些类库,它们比本章讨论前面的例子更为专用。例如,头文件 complex 为复数提供了类模板 complex,包含用于 float、long 和 long double 的具体化。这个类提供了标准的复数运算及能够处理复数的标准函数。C11 新增的头文…...