JavaEE学习日志持续更新----> 必看!JavaEE学习路线(文章总汇)
Java学习日志(三十三)
JDBC预处理对象
登陆注册案例模拟:用户注入攻击数据库
首先创建一张用户表
-- 创建数据库day04
CREATE DATABASE day04;
-- 使用数据库day04
USE day04;
-- 创建用户表users 字段:用户主键,用户名,密码
CREATE TABLE users(
-- 用户逐渐
uid INT PRIMARY KEY AUTO_INCREMENT,
-- 用户名
username VARCHAR(20),
-- 密码
PASSWORD VARCHAR(20)
);
注册
INSERT INTO users(username,PASSWORD) VALUES('a','123');
登陆
SELECT * FROM users WHERE username='a' AND PASSWORD='123';-- 登陆成功
SELECT * FROM users WHERE username='a' AND PASSWORD='124';-- 登陆失败
使用sql语句注入攻击数据库:sql并不严谨,在条件的末尾or上一个为true的值,那么前边的条件是什么则无所谓。
输入的密码:2' OR '1=1
SELECT * FROM users WHERE username='a' AND PASSWORD='2' OR '1=1';
使用Java程序模拟用户的登陆操作
- 使用Scanner获取用户输入的用户名和密码
- 使用JDBC技术操作数据库
1. 注册驱动,获取数据库连接对象Connection,使用JDBCUtils完成
2. 获取执行者对象Statement
3. 执行sql语句,获取结果
4. 处理结果
- 有结果:登陆成功
- 没有结果:您输入的用户名或密码有误
5. 释放资源,使用JDBCUtils完成
public class Demo01Login {
public static void main(String[] args) throws SQLException {
//1.使用Scanner获取用户输入的用户名和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.nextLine();//nextLine直接获取一行
System.out.println("请输入密码:");
String password = sc.nextLine();//nextLine直接获取一行
//2.使用JDBC技术操作数据库
//注册驱动,获取数据库连接对象Connection,使用JDBCUtils完成
Connection conn = JDBCUtils.getConnection();
//获取执行者对象Statement
Statement stat = conn.createStatement();
//执行sql语句,获取结果
String sql = "SELECT * FROM users WHERE username='"+username+"' AND PASSWORD='"+password+"';";
System.out.println(sql);
ResultSet rs = stat.executeQuery(sql);
//处理结果
if(rs.next()){
//有结果:登陆成功
System.out.println("登陆成功"+rs.getString("username")+"\t"+rs.getString("password"));
}else {
//没有结果:您输入的用户名或密码有误
System.out.println("您输入的用户名密码有误");
}
//释放资源,使用JDBCUtils完成
JDBCUtils.close(rs,stat,conn);
}
}
注入式攻击
解决用户注入式攻击
使用statement接口的子接口PreparedStatement实现:
java.sql.PreparedStatement接口 extends Statement接口
,表示预编译的sql语句对象,SQL语句已预编译并存储在PreparedStatement对象中。 然后,可以使用此对象多次有效地执行此语句。
注意:PreparedStatement执行sql语句比statement效率更高
PreparedStatement使用:
-
获取PreparedStatement对象,可以使用collection中的方法
PreparedStatement prepareStatement(String sql)
创建一个 PreparedStatement对象,用于将参数化SQL语句发送到数据库。参数:String sql:传递sql语句,可以使用?占位符
-
设置
?
占位符的实际参数 -
执行sql语句,获取结果
设置?占位符的实际使用参数,可以使用PreparedStatement对象
中的方法:
void setObject(int parameterIndex, Object x)
使用给定对象设置指定参数的值。
参数:
int parameterIndex:要设置的第几个?占位符,从1开始
Object x:?占位符的实际参数
注意:有几个问号占位符,就调用几次setObject方法,设置几个参数
代码示例:
public class Demo02Login {
public static void main(String[] args) throws SQLException {
//1.使用Scanner获取用户输入的用户名和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.nextLine();//nextLine直接获取一行
System.out.println("请输入密码:");
String password = sc.nextLine();//nextLine直接获取一行
//2.使用JDBC技术操作数据库
//注册驱动,获取数据库连接对象Connection,使用JDBCUtils完成
Connection conn = JDBCUtils.getConnection();
//获取预编译的执行者对象
//查询的sql语句,可以使用?代替实际参数
String sql = "SELECT * FROM users WHERE username= ? AND PASSWORD= ?;";
PreparedStatement pst = conn.prepareStatement(sql);
/*
设置?占位符的实际使用参数,可以使用PreparedStatement对象中的方法
void setObject(int parameterIndex, Object x) 使用给定对象设置指定参数的值。
参数:
int parameterIndex:要设置的第几个?占位符,从1开始
Object x:?占位符的实际参数
注意:有几个问号占位符,就调用几次setObject方法,设置几个参数
*/
pst.setObject(1,username);
pst.setObject(2,password);
/*
执行sql语句,获取结果
使用PreparedStatement对象中的方法,和statement相同
*/
ResultSet rs = pst.executeQuery();
//处理结果
if(rs.next()){
//有结果:登陆成功
System.out.println("登陆成功"+rs.getString("username")+"\t"+rs.getString("password"));
}else {
//没有结果:您输入的用户名或密码有误
System.out.println("您输入的用户名密码有误");
}
//释放资源,使用JDBCUtils完成
JDBCUtils.close(rs,pst,conn);
}
}
注入式攻击失败
PreparedStatement对象对数据库表进行增删改查
增删改
@Test
public void testInsert() throws SQLException {
//获取Connection对象
Connection conn = JDBCUtils.getConnection();
//获取预编译的执行者对象
PreparedStatement pst = conn.prepareStatement("INSERT INTO users(username,PASSWORD) VALUES(?,?);");
//PreparedStatement pst = conn.prepareStatement("UPDATE users SET PASSWORD=? WHERE username=?;");
//PreparedStatement pst = conn.prepareStatement("DELETE FROM users WHERE uid=?;");
//设置问号占位符的实际参数
//增加
pst.setObject(1, "老王");
pst.setObject(2, "abc");
//修改
// pst.setObject(1,"123");
// pst.setObject(2,"老王");
//删除
// pst.setObject(1,2);
//执行sql语句获取结果
int row = pst.executeUpdate();
//处理结果集
if (row > 0) {
System.out.println("数据库操作成功");
}else {
System.out.println("数据库操作失败!");
}
//释放资源
JDBCUtils.close(null, pst, conn);
}
查询
@Test
public void testSelect() throws SQLException {
//获取Connection对象
Connection conn = JDBCUtils.getConnection();
//获取预编译的执行者对象
PreparedStatement pst = conn.prepareStatement("select * from users where uid = ?");
//设置问号占位符的实际参数
pst.setObject(1,2);
//执行sql语句,获取结果
ResultSet rs = pst.executeQuery();
//处理结果
while (rs.next()){
System.out.println(rs.getString("username")+'\t'+rs.getString("password"));
}
JDBCUtils.close(rs,pst,conn);
}
连接池
连接池的原理
连接池的规范接口DataSource
javax.sql.DataSource
创建并测试C3P0连接池的工具类
C3P0连接池的工具类:使用C3P0连接池获取数据库连接对象Connection并返回
连接池规范接口:javax.sql.DataSource接口
定义了一个从连接池中获取连接的方法:
Connection getConnection()
尝试与此 DataSource对象表示的数据源建立连接。
C3P0实现了连接池的规范接口DataSource,重写了getConnection方法
com.mchange.v2.c3p0.ComboPooledDataSource implements DataSource接口
使用步骤:
- 在成员位置创建一个静态的ComboPooledDataSource对象
- 在静态代码中,使用ComboPooledDataSource对象的setXXX方法,设置数据库的连接信息
- 定义一个静态方法,从ComboPooledDataSource对象中获取数据库连接对象Connection并返回
- 定义一个释放资源的方法
创建C3P0连接池的工具类:
public class C3P0Utils {
//1.在成员位置创建一个静态的ComboPooledDataSource对象
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
//2.在静态代码中,使用ComboPooledDataSource对象的setXXX方法,设置数据库的连接信息
//优先执行,只执行一次
static {
try {
//注册驱动
dataSource.setDriverClass("com.mysql.jdbc.Driver");
//设置url
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/day04");
//设置用户名
dataSource.setUser("root");
//设置密码
dataSource.setPassword("root");
} catch (PropertyVetoException e) {
e.printStackTrace();
}
}
//3.定义一个静态方法,从ComboPooledDataSource对象中获取数据库连接对象Connection并返回
public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
//获取Connection失败,让程序停止,把编译异常转为运行时异常
throw new RuntimeException("获取数据库连接对象失败"+e);
}
}
//4.定义一个释放资源的方法
public static void close(ResultSet rs, Statement stat, Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stat!=null){
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();//把连接归还给连接池
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
测试工具类:
public class TestC3P0Utils {
@Test
public void test01() throws SQLException {
//使用C3P0Utils工具类的方法getConnection,获取数据库连接对象
Connection conn = C3P0Utils.getConnection();
//System.out.println(conn);//com.mchange.v2.c3p0.impl.NewProxyConnection@13e39c73
//获取执行者对象
Statement stat = conn.createStatement();
//执行sql语句,获取结果
ResultSet rs = stat.executeQuery("select * from users");
//处理结果
while(rs.next()){
System.out.println(rs.getString("username")+"\t"+rs.getString("password"));
}
//释放资源
C3P0Utils.close(rs,stat,conn);
}
}
结果
带XML配置文件的C3P0连接池工具类
使用步骤:
- 在成员位置创建一个静态的ComboPooledDataSource对象
- 把c3p0-config.xml复制到当前模块的src下;C3P0就会自动解析xml,获取数据库连接信息给ComboPooledDataSource对象赋值
- 定义一个静态方法,从ComboPooledDataSource对象中获取数据库连接对象Connection并返回
- 定义一个释放资源的方法
配置文件:注意编码格式为UTF-8
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day04</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工具类:
public class C3P0UtilsXML {
//1.在成员位置创建一个静态的ComboPooledDataSource对象
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
//3.定义一个静态方法,从ComboPooledDataSource对象中获取数据库连接对象Connection并返回
public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
//获取Connection失败,让程序停止,把编译异常转为运行时异常
throw new RuntimeException("获取数据库连接对象失败"+e);
}
}
//4.定义一个释放资源的方法
public static void close(ResultSet rs, Statement stat, Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stat!=null){
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();//把连接归还给连接池
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
测试类:
@Test
public void test02() throws SQLException {
//使用C3P0UtilsXML工具类的方法getConnection,获取数据库连接对象
Connection conn = C3P0UtilsXML.getConnection();
//System.out.println(conn);//com.mchange.v2.c3p0.impl.NewProxyConnection@1ca3b418
//获取预编译的执行者对象
PreparedStatement pst = conn.prepareStatement("SELECT * FROM users");
//执行sql语句,获取结果
ResultSet rs = pst.executeQuery();
//处理结果
while(rs.next()) {
System.out.println(rs.getInt("uid") + "\t" + rs.getString("username") + "\t" + rs.getString("password"));
}
//释放资源
C3P0UtilsXML.close(rs,pst,conn);
}
结果