PreparedStatement
SQL注入问题
SQL注入:用户输入的内容作为了SQL语句语法的一部分,改变了原有SQL真正的意义。 假设有登录案例SQL语句如下:
SELECT * FROM 用户表 WHERE NAME = 用户输入的用户名 AND PASSWORD = 用户输的密码;
此时,当用户输入正确的账号与密码后,查询到了信息则让用户登录。但是当用户输入的账号为XXX 密码为:XXX’ OR ‘a’=’a
时,则真正执行的代码变为:
SELECT * FROM 用户表 WHERE NAME = ‘XXX’ AND PASSWORD =’ XXX’ OR ’a’=’a’;
此时,上述查询语句时永远可以查询出结果的。那么用户就直接登录成功了,显然我们不希望看到这样的结果,这便是SQL注入问题。 为此,我们使用PreparedStatement来解决对应的问题。
API详解:预处理对象
preparedStatement:预编译对象,是Statement对象的子类。
特点:
-
性能高
-
会把sql语句先编译
-
能过滤掉用户输入的关键字。
PreparedStatement预处理对象,处理的每条sql语句中所有的实际参数,都必须使用占位符?替换。
String sql = "select * from user where username = ? and password = ?";
PreparedStatement使用,需要通过以下3步骤完成:
-
PreparedStatement预处理对象代码:
// 获得预处理对象,需要提供已经使用占位符处理后的SQL语句 PreparedStatement psmt = conn.prepareStatement(sql)
-
设置实际参数
void setXxx(int index, Xxx xx) 将指定参数设置指定类型的值 参数1:index 实际参数序列号,从1开始。 参数2:xxx 实际参数值,xxx表示具体的类型。 例如: setString(2, "1234") 把SQL语句中第2个位置的占位符?替换成实际参数 "1234"
-
执行SQL语句:
int executeUpdate(); --执行insert update delete语句. ResultSet executeQuery(); --执行select语句. boolean execute(); --执行select返回true 执行其他的语句返回false.
插入
@Test
public void demo01(){
//添加:向分类表中添加数据
Connection conn = null;
PreparedStatement psmt = null;
ResultSet rs = null;
try {
//1 获得连接
conn = JdbcUtils.getConnection();
//2 处理sql语句
String sql = "insert into category(cname) values(? )";
//3获得预处理对象
psmt = conn.prepareStatement(sql);
//4设置实际参数
psmt.setString(1,"预处理");
//5执行
int r = psmt.executeUpdate();
System.out.println(r);
} catch (Exception e) {
throw new RuntimeException(e);
} finally{
//6释放资源
JdbcUtils.closeResource(conn, psmt, rs);
}
}
更新
@Test
public void demo02(){
//修改
Connection conn = null;
PreparedStatement psmt = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
//1 sql语句
String sql = "update category set cname = ? where cid = ?";
//2 获得预处理对象
psmt = conn.prepareStatement(sql);
//3设置实际参数
psmt.setString(1, "测试数据");
psmt.setInt(2, 4);
//4执行
int r = psmt.executeUpdate();
System.out.println(r);
} catch (Exception e) {
throw new RuntimeException(e);
} finally{
JdbcUtils.closeResource(conn, psmt, rs);
}
}
通过id查询详情
@Test
public void demo05(){
//通过id查询
Connection conn = null;
PreparedStatement psmt = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
String sql = "select * from category where cid = ?";
psmt = conn.prepareStatement(sql);
psmt.setInt(1, 2);
rs = psmt.executeQuery();
if(rs.next()){
System.out.println("查询到");
} else {
System.out.println("查询不到");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally{
JdbcUtils.closeResource(conn, psmt, rs);
}
}
使用连接池重写工具类
连接池原理
连接池理解为存放多个连接的集合。
使用连接池技术的目的:解决建立数据库连接耗费资源和时间很多的问题,提高性能。
编写标准的数据源(规范)
Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!
常见的连接池:C3P0、DRUID。
C3P0连接池
C3P0连接池工具类编写
C3P0开源免费的连接池!目前使用它的开源项目有:Spring、Hibernate等。使用C3P0连接池需要导入jar包,c3p0使用时还需要添加配置文件“c3p0-config.xml”
使用步骤
-
添加jar包
-
编写配置文件 c3p0-config.xml,放在src中(注:文件名一定不要写错)
-
编写工具类
-
编写配置文件 c3p0-config.xml
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day03</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">2000</property>
<property name="maxIdleTime">1000</property>
</default-config>
</c3p0-config>
c3p0连接池常用的配置参数:
参数 | 说明 |
---|---|
initialPoolSize | 初始连接数 |
maxPoolSize | 最大连接数 |
checkoutTimeout | 最大等待时间 |
maxIdleTime | 最大空闲回收时间 |
初始连接数
:刚创建好连接池的时候准备的连接数量 最大连接数
:连接池中最多可以放多少个连接 最大等待时间
:连接池中没有连接时最长等待时间 最大空闲回收时间
:连接池中的空闲连接多久没有使用就会回收
-
编写C3P0工具类
package com.doit.utils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.management.relation.RoleUnresolved;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/*
javax.sql.DataSource 连接池接口
方法
Connection getConnection()
只要使用连接池对象 调用getConnection方法 就是从连接池中 获取了连接对象
要想获取连接 必须找到DataSource接口的实现类对象 C3P0实现类这个接口
对应的实现类是ComboPooledDataSource ,我们只要创建这个类的对象
调用getConnection方法 就 从连接池中 获取到了连接对象
*/
public class C3P0Utils {
private static ComboPooledDataSource ds ;
static{
try{
ds = new ComboPooledDataSource();
//配置连接池的信息
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/day04");
ds.setUser("root");
ds.setPassword("root");
}catch (Exception e){
throw new RuntimeException(e);
}
}
public static Connection getConnection() throws SQLException, PropertyVetoException {
Connection con = ds.getConnection();
return con;
}
public static void close(Connection con, Statement stat, ResultSet rs) {
try {
if (con != null)
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (stat != null)
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (rs != null)
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
C3P0连接池工具类的使用
public class Demo {
public static void main(String[] args) throws Exception {
// 拿到连接
Connection conn = JdbcUtils.getConnection();
// 执行sql语句
String sql = "INSERT INTO student VALUES (NULL, ?, ?, ?);";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "李四");
pstmt.setInt(2, 30);
pstmt.setDouble(3, 50);
int i = pstmt.executeUpdate();
System.out.println("影响的函数: " + i);
// 关闭资源
JdbcUtils.close(conn, pstmt);
}
}
别董大二首·其一
【唐】高适
千里黄云白日曛,北风吹雁雪纷纷。
莫愁前路无知己,天下谁人不识君?