数据库连接池
1.定义:
数据库连接池是管理并发访问数据库连接的理想解决方案.
DriverManager管理数据库连接适合单线程使用,在多线程并发情况下,为了能够重用数据库连接,同时控制
并发连接总数,保护数据库,避免连接过载,一定要使用数据库连接池
2.DHCP连接池(之后试下其他开源实现方式)
1)导入 commons-dbcp2-2.8.0.jar 包,pom.xml依赖路径
<< span="">dependency>
<< span="">groupId>org.apache.commonsgroupId>
<< span="">artifactId>commons-dbcp2artifactId>
<< span="">version>2.8.0version>
dependency>
2)DBCP连接池实现步骤:
1> 错误!未指定文件名。导入连接池jar包
2>创建连接池对象
3>设置数据库必须的链接参数
4>设置可选连接池管理
5>从连接池中获得活动的数据库连接
6>使用连接访问数据库
7>使用以后关闭数据库连接,这个关闭不是真的关闭连接,而是将使用过的连接归还给连接池.
3)将连接池封装为连接管理工剧烈 DBUtils
1> 配套参数配置文件 db.properties
# db.properties
jdbc.driver=oracle.jdbc.OracleDriver
jdbc.url=jdbc:oracle:thin:@localhost:1521:orcl
jdbc.username=system
jdbc.password=123456
# parameter for BasicDataSource
initSize=2
maxActive=2
2>封装DBCP连接池codeDemo
package wei.jdbc;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
import org.apache.commons.dbcp2.BasicDataSource;
/**
* 将DBCP连接池封装为工具类
*/
public class DBUtils_1 {
静态方法调用静态变量
private static String driver;
private static String url;
private static String username;
private static String password;
private static int initSize;
private static int maxActive;
private static BasicDataSource ds;
静态代码块加载静态资源并初始化静态变量
static {
ds = new BasicDataSource();
Properties cfg = new Properties();
try {
读取db.properties 配制文件中的参数
InputStream in = DBUtils_1.class.getClassLoader().getResourceAsStream("db.properties");
/加载流(要加载流呀..20201005)
cfg.load(in);;
driver = cfg.getProperty("jdbc.driver");
url = cfg.getProperty("jdbc.url");
username = cfg.getProperty("jdbc.username");
password = cfg.getProperty("jdbc.password");
initSize = Integer.parseInt(cfg.getProperty("initSize"));
maxActive = Integer.parseInt(cfg.getProperty("maxActive"));
in.close();
//初始化连接池对象
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
ds.setInitialSize(initSize);
ds.setMaxTotal(maxActive);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
public static Connection getConnection() {
try {
/*
* getConnection() 从连接池中获取重用的连接,如果连接池满了,则等待.
* 如果有连接归还,则获取重用的连接
*/
Connection conn = ds.getConnection();
return conn;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
public static void close(Connection conn) {
if(conn!=null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
执行计划
1.定义:
1)任何SQL都是先编译 "执行计划",再执行"执行计划"
2)数据库为了优化性能,在SQL形同时,会重用执行计划
1>执行计划编译较慢
2>重用执行计划可以提高数据库性能
3)数据库只在SQL语句完成一样时才重用相同的执行计划
1>如果SQL语句汇总有一个字符的更改,也会执行不同的执行计划
2>SQL中一个空格或者一个大小写不同也会创建不同的执行计划
2.PreparedStatement 对象
1)定义:PreparedStatement 对象用于执行带参数的预编译执行计划
2)作用:可以重复使用执行计划,提高DB效率
3)实现步骤:
1>将带参数的SQL发送到数据库创建执行计划
2>替换执行计划中的参数
3>执行 "执行计划",得到执行结果
4)codeDemo:演示预编译的SQL执行计划
package wei.jdbc.day02;
import java.sql.Connection;
import java.sql.PreparedStatement;
import wei.jdbc.DBUtils_1;
/**
* 演示预编译的SQL执行计划
*/
public class Demo_PS_DML {
public static void main(String[] args) {
Connection conn = null;
try {
conn = DBUtils_1.getConnection();
String sql = "INSERT INTO ismodelgp(id, symbol, secname) VALUES(?, ?, ?) ";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, 3);
ps.setString(2, "600036");
ps.setString(3, "招商银行");
int n = ps.executeUpdate();
System.out.println(n);
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtils_1.close(conn);
}
}
}
SQL注入
1.定义:
用户输入了含有SQL成分的参数,参数在拼接SQL时造成SQL语句的语义改变,从而改变SQL语句的执行计划,最终的执行结果就完全改变了.
2.如何避免SQL注入:
1)拦截用户输入的SQL成分(字符串校验)
2)固定执行计划,避免改变执行逻辑
3.SQL注入demo
1)输入用户名 tom 和密码 1' or '1' = '1 时候出现注入攻击现象
使用 PS 就可以避免注入攻击,更新login方法如下:
package wei.jdbc.day02;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
import wei.jdbc.DBUtils_1;
publicclassDemo_SqlInjection {
publicstaticvoid main(String[] args) {
//获取用户输入
Scanner scan =new Scanner(System.in);
System.out.println("用户名:");
String name = scan.nextLine();
System.out.println("密码:");
String pwd = scan.nextLine();
//检查登录情况
// boolean pass = login(name, pwd);
boolean pass = loginPS(name, pwd);
if(pass) {
System.out.println("欢迎你!"+ name);
}else {
System.out.println("用户名或密码错误!");
}
}
/** 通过PreparedStatement 固定执行计划,避免SQL注入 */
publicstaticboolean loginPS(String name, String pwd) {
String sql ="SELECT COUNT(*) ck FROM robin_user WHERE name = ? AND pwd = ? ";
Connection conn =null;
try {
conn = DBUtils_1.getConnection();
PreparedStatement ps =conn.prepareStatement(sql);
ps.setString(1, name);
ps.setString(2, pwd);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
int n = rs.getInt("ck");
return n >=1;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtils_1.close(conn);
}
returnfalse;
}
/** 检查用户登录用户名、密码
*
* 1' or '1' = '1 ,sql中 and 优先级高于or, '1' = '1' 为 true,造成SQL语句的语义改变
*/
publicstaticboolean login(String name, String pwd) {
String sql ="SELECT COUNT(*) AS ck FROM robin_user WHERE name = \'"+name+"\' AND pwd = \'"+pwd+"\' ";
Connection conn =null;
try {
conn = DBUtils_1.getConnection();
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);
while(rs.next()) {
int n = rs.getInt("ck");
return n >=1; //如果N>-1则登录成功
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtils_1.close(conn);
}
returnfalse;
}
}
HomeWork
1.测试数据库连接池的使用
2.测试数据库连接池的并发性(用Rnunable实现)
3.封装连接管理工具类 DBUtils
4.利用PS实现数据库插入功能
5.利用PS实现数据库更新功能
6.利用PS实现数据库删除功能
7.利用PS实现数据库查询功能
8.完成SQL注入案例