防止SQL注入
sql注入:利用sql语句的语法特点,应用层输入特殊格式,让原有的sql语句失效
创建表结构 并加入数据
create table login(
lid int primary key auto_increment,
lname varchar(20),
lpwd varchar(20),
lsex varchar(2),
laddr varchar(50)
);
insert into login(lname,lpwd,lsex,laddr)
values('zhangsan','123456','男','西安北大街');
正常写法:
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Scanner input = new Scanner(System.in);
// 1. 加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/myhomework03?serverTimezone=GMT",
"root", "123456");
System.out.println("请输入账号");
String uname = input.next();
System.out.println("请输入密码");
String upwd = input.next();
// 3.sql
String sql = "select * from login where lname ='" + uname + "' and lpwd=' " + upwd + "'";
Statement statement = conn.createStatement();// 不能防止sql注入
ResultSet rs = statement.executeQuery(sql);
// 4.执行sql语句
if (rs.next()) {
int lid = rs.getInt("lid");
String lname = rs.getString("lname");
String lpwd = rs.getString("lpwd");
String lsex = rs.getString("lsex");
String laddr = rs.getString("laddr");
System.out.println(lid + lname + lpwd + lsex + laddr);
} else {
System.out.println("登录失败");
}
}
防止sql注入:
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Scanner input = new Scanner(System.in);
// 1. 加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/myhomework03?serverTimezone=GMT",
"root", "123456");
System.out.println("请输入账号");
String uname = input.next();
System.out.println("请输入密码");
String upwd = input.next();
// // 3.sql
// String sql = "select * from login where lname ='" + uname + "' and lpwd=' " + upwd + "'";
// Statement statement = conn.createStatement();// 不能防止sql注入
// ResultSet rs = statement.executeQuery(sql);
// 防止sql注入 -- 利用sql语句的语法特点,应用层输入特殊格式,让原有的sql语句失效
// 1.sql语句要变 -- 参数位置要使用 ?占位
String sql = "select * from login where lname =? and lpwd =?";
// 2. 执行对象要变 -- 预处理sql语句
PreparedStatement prepareStatement = conn.prepareStatement(sql);
// 3.给sql语句传参
prepareStatement.setObject(1, uname);
prepareStatement.setObject(2, upwd);
ResultSet rs = prepareStatement.executeQuery();
// 4.执行sql语句
if (rs.next()) {
int lid = rs.getInt("lid");
String lname = rs.getString("lname");
String lpwd = rs.getString("lpwd");
String lsex = rs.getString("lsex");
String laddr = rs.getString("laddr");
System.out.println(lid + lname + lpwd + lsex + laddr);
} else {
System.out.println("登录失败");
}
}
使用PreparedStatement的优点:
- 安全性不同: PreparedStatement可以有效防止sql注入,而Statment不能防止sql注入。
- 语法不同:PreparedStatement可以使用预编译的sql,而Statment只能使用静态的sql
- 效率不同:对于更改参数的同一SQL语句,PreparedStatement可以使用sql缓存区,效率比Statment高
数据库连接池
其实就是一个容器(集合),存放数据库连接的容器。
当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
优点:
节约资源
用户访问高效
实现:
-
标准接口:DataSource javax.sql包下的
方法:
获取连接:getConnection()
归还连接:Connection.close()。如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭连接了。而是归还连接。
- 一般我们不去实现它,有数据库厂商来实现
- C3P0:数据库连接池技术
- Druid:数据库连接池实现技术,由阿里巴巴提供的
C3P0数据库连接池技术
- *导入jar包 (两个) c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar ,
**** 不要忘记导入数据库驱动jar包
- 定义配置文件
名称: c3p0.properties 或者 c3p0-config.xml文件名称不可以修改
路径:直接将文件放在src目录下即可,路径不可修改
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://127.0.0.1/db6?serverTimezone=GMT
</property>
<property name="user">root</property>
<property name="password">12345678</property>
<!-- 连接池参数 -->
<!-- 初始化连接数 -->
<property name="initialPoolSize">5</property>
<!-- 最大连接数 -->
<property name="maxPoolSize">10</property>
<!-- 超时时间:当连接池耗尽时,客户端调用getConnection()后等待获取新连接的时间,超时后将抛出 SQLException,如设为0则无限期等待。单位毫秒。Default:
0 -->
<property name="checkoutTimeout">3000</property>
</default-config>
<named-config name="otherc3p0">
<!-- 连接参数 -->
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://127.0.0.1/db5?serverTimezone=GMT
</property>
<property name="user">root</property>
<property name="password">12345678</property>
<!-- 连接池参数 -->
<!-- 初始化连接数 -->
<property name="initialPoolSize">5</property>
<!-- 最大连接数 -->
<property name="maxPoolSize">8</property>
<!-- 超时时间:当连接池耗尽时,客户端调用getConnection()后等待获取新连接的时间,超时后将抛出 SQLException,如设为0则无限期等待。单位毫秒。Default:
0 -->
<property name="checkoutTimeout">3000</property>
</named-config>
</c3p0-config>
- 创建核心对象 数据库连接池对象 ComboPooledDataSource
- 获取连接: getConnection
package c3p0_conn_pool;
import java.sql.Connection;
import java.sql.SQLException;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class Test01 {
// c3p0数据库连接池对象
static ComboPooledDataSource datasource = null;
public static void main(String[] args) throws SQLException {
// 创建连接池对象
datasource = new ComboPooledDataSource();
// 基础参数配置
datasource.setJdbcUrl(
"jdbc:mysql://127.0.0.1:3306/db6?charset=utf8mb4&useSSL=false&useTimezone=true&serverTimezone=GMT%2B8");
datasource.setUser("root");
datasource.setPassword("12345678");
// 设置连接池的配置参数
datasource.setInitialPoolSize(3);// 初始化连接对象
// datasource.setMinPoolSize(2);// 最小连接数量
datasource.setMaxPoolSize(10);// 最大连接池数量
datasource.setCheckoutTimeout(3000);// 等待时间(单位:毫秒)
for (int i = 1; i <= 11; i++) {
Connection connection = datasource.getConnection();
System.out.println(connection);
if (i == 5) {
connection.close();// 将连接对象归还给连接池,进行复用
}
}
}
}
package c3p0_conn_pool;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class Test02 {
static DataSource dataSource = null;
public static void main(String[] args) throws SQLException {
// 1.创建连接池对象
dataSource = new ComboPooledDataSource();
// for (int i = 1; i <= 11; i++) {
// 2.获取连接对象
// Connection connection = dataSource.getConnection();
// System.out.println(connection);
// if (i == 5) {
// connection.close();// 将连接对象归还给连接池,进行复用
// }
// }
// 2.获取连接对象
Connection conn = dataSource.getConnection();
System.out.println(conn);
PreparedStatement pre = conn.prepareStatement("select * from emp");
ResultSet resultSet = pre.executeQuery();
while (resultSet.next()) {
System.out.println(resultSet.getInt(1));
}
}
}
package c3p0_conn_pool;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class Test03 {
static DataSource dataSource = null;
public static void main(String[] args) throws SQLException {
// 1.创建连接池对象
dataSource = new ComboPooledDataSource("otherc3p0");
// 2.获取连接对象
Connection conn = dataSource.getConnection();
System.out.println(conn);
PreparedStatement pre = conn.prepareStatement("select * from emp");
ResultSet resultSet = pre.executeQuery();
while (resultSet.next()) {
System.out.println(resultSet.getInt(1));
}
}
}
Druid:数据库连接池实现技术,由阿里巴巴提供的
- 导入jar包 druid-1.0.9.jar
- 定义配置文件
是properties形式的
可以叫任意名称,可以放在任意目录下
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/db6?serverTimezone=GMT
username=root
password=12345678
initialSize=5
maxActive=10
maxWait=3000
validationQuery=SELECT 1
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
poolPreparedStatements=false
- 加载配置文件。Properties
- 获取数据库连接池对象:通过工厂来来获取 DruidDataSourceFactory
- 获取连接:getConnection
package druid_conn_pool;
import java.io.IOException;
import java.sql.Connection;
import java.util.Properties;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
public class Test01 {
public static void main(String[] args) throws Exception {
//1.导入jar包
//2.定义配置文件
//3.加载配置文件
Properties properties = new Properties();
properties.load(Test01.class.getResourceAsStream("/druid.properties"));
//4.获取连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//5.获取数据库连接Connection
Connection connection = dataSource.getConnection();
System.out.println(connection);
}
}