02-JDBC的使用和分析
JDBC使用和问题分析
1、模拟用户登录功能的实现(1)
package ajdbctest;
import java.sql.*;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/*
实现功能:
1、需求:模拟用户登录功能的实现
2、业务描述:
程序运行,提供一个可以让用户输入用户名和密码的入口
用户输入用户名会让密码之后,提交信息,java程序收集信息
Java程序连接数据库验证用户名和密码
合法:显示登陆成功
不合法:显示登陆失败
3、在实际开发中,表的设计使用专业的建模工具,这里安装使用的建模工具:PowerDesigner
使用PD工具来进行数据库表的设计(具体参见user-login.sql脚本)
4、下面的验证方法是自己写的,是先把表中的所有用户都查询出来,然后进行处理查询结果集遍历与用户输入信息进行if和equals
比较,该方法可能效率没这么高,因为要把表中的所有数据查出来,还有if语句的原因
*/
public class JDBCTest04 {
public static void main(String[] args) {
//初始化用户登录界面方法,让用户输入用户名和密码
Map<String,String> userLoginInfo = initUI();
//连接数据库,验证用户输入信息,即登录方法
boolean userLogin = userLogin(userLoginInfo);
System.out.println(userLogin?"登录成功!":"登陆失败!");
}
/**
* 连接数据库,验证用户信息的方法
* @return 返回true,登陆成功;返回false。登录失败
*/
private static boolean userLogin(Map<String,String> userInfo) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mysqlstudy","root","rong195302");
//获取(创建)数据库操作对象
stmt = conn.createStatement();
//执行sql查询语句,返回的是查询结果集
rs = stmt.executeQuery("select * from t_user");
//处理查询结果集
while (rs.next()){
if (userInfo.get("userName").equals(rs.getString("loginName")) && userInfo.get("userPwd").equals(rs.getString("loginPwd"))){
return true;
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return false;
}
/**
* 初始化用户界面
* @return 一个map数组,用户输入的用户名和密码
*/
private static Map<String, String> initUI() {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String userName = scanner.nextLine();//该方法表示一次读一行
System.out.println("请输入密码:");
String userPwd = scanner.nextLine();
Map<String,String> userInfo = new HashMap<>();
userInfo.put("userName",userName);
userInfo.put("userPwd",userPwd);
return userInfo;
}
}
2、模拟用户登录功能的实现(2)
注意与1的区别
package ajdbctest;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/*
实现功能:
1、需求:模拟用户登录功能的实现
2、业务描述:
程序运行,提供一个可以让用户输入用户名和密码的入口
用户输入用户名会让密码之后,提交信息,java程序收集信息
Java程序连接数据库验证用户名和密码
合法:显示登陆成功
不合法:显示登陆失败
3、在实际开发中,表的设计使用专业的建模工具,这里安装使用的建模工具:PowerDesigner
使用PD工具来进行数据库表的设计(具体参见user-login.sql脚本)
4、下面的验证方法是有SQL注入现象的问题的,是课程中的例子程序
用户名:sd
密码:sd' or '1'='1
登录成功
这种现象被称为SQL注入
设置断点在java语句:rs = stmt.executeQuery(sql); 然后Debug,使得该java语句上面的语句都执行,该语句以及下面不执行
可以看到sql="select * from t_user where loginName='sd' and loginPwd='sd' or '1'='1'";
该sql语句的原本条件有或没有都没关系啦,因为有:or '1'='1',这个永远为真
5、SQL注入的根本原因
用户输入信息中含有sql关键字,并且这些关键字参与了sql语句的编译过程
导致原SQL语句的含义被扭曲,达到SQL注入。
*/
public class JDBCTest05 {
public static void main(String[] args) {
//初始化用户登录界面方法,让用户输入用户名和密码
Map<String,String> userLoginInfo = initUI();
//连接数据库,验证用户输入信息,即登录方法
boolean userLogin = userLogin(userLoginInfo);
System.out.println(userLogin?"登录成功!":"登陆失败!");
}
/**
* 连接数据库,验证用户信息的方法
* @return 返回true,登陆成功;返回false。登录失败
*/
private static boolean userLogin(Map<String,String> userInfo) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mysqlstudy?useSSL=false","root","rong195302");
//获取(创建)数据库操作对象
stmt = conn.createStatement();
//执行sql查询语句,返回的是查询结果集
String sql = "select * from t_user where loginName='"+userInfo.get("userName")+"' and loginPwd='"+userInfo.get("userPwd")+"'";
//以上正好完成了sql语句的拼接,下面代码就是将sql语句发送各DBMS,DBMS进行sql编译。
//正好将用户提供的"非法信息"编译进去。导致了原SQL语句的含义被扭曲
rs = stmt.executeQuery(sql);
//处理查询结果集
while (rs.next()){
return true;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return false;
}
/**
* 初始化用户界面
* @return 一个map数组,用户输入的用户名和密码
*/
private static Map<String, String> initUI() {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String userName = scanner.nextLine();//该方法表示一次读一行
System.out.println("请输入密码:");
String userPwd = scanner.nextLine();
Map<String,String> userInfo = new HashMap<>();
userInfo.put("userName",userName);
userInfo.put("userPwd",userPwd);
return userInfo;
}
}
3、解决SQL注入风险
package ajdbctest;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/*
1、解决SQL注入问题
使用户提供的用户信息不参与sql语句的编译就可以解决了
如何实现:使用java.sql.PreparedStatement
java.sql.PreparedStatement接口继承了java.sql.Statement接口
java.sql.Statement是数据库操作对象
java.sql.PreparedStatement是属于预编译的数据库操作对象
原理:先对给定的sql框架进行预编译,然后在传所需要的条件值
2、对比Statement和PreparedStatement
-前者存在SQL注入问题,后者解决了SQL注入问题
-一般情况下,我们在Windows命令窗口里执行一条SQL语句,第一次执行需要编译,但第二次还是这条不变的SQL语句时不会编译直接执行
-所以前者是编译一次执行一次,后者由于SQL语句框架不变,编译一次执行N次,PreparedStatement效率高
-后者会在编译阶段做安全检查
综上所述:PreparedStatement使用较多,Statement使用较少
3、什么情况下使用Statement?
业务方面要求必须支持SQL注入
*/
public class JDBCTest06 {
public static void main(String[] args) {
//初始化用户登录界面方法,让用户输入用户名和密码
Map<String,String> userLoginInfo = initUI();
//连接数据库,验证用户输入信息,即登录方法
boolean userLogin = userLogin(userLoginInfo);
System.out.println(userLogin?"登录成功!":"登陆失败!");
}
/**
* 连接数据库,验证用户信息的方法
* @return 返回true,登陆成功;返回false。登录失败
*/
private static boolean userLogin(Map<String,String> userInfo) {
Connection conn = null;
//Statement stmt = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mysqlstudy?useSSL=false","root","rong195302");
//获取(创建)预编译的数据库操作对象
//stmt = conn.createStatement();
//SQL语句的框架。其中?表示一个占位符,一个?将来接收一个"值",里面的问号不能用单引号括起来,因为你不知道将来传进的值一定是字符串类型
String sql = "select * from t_user where loginName= ? and loginPwd= ?";
//程序执行到下面语句,发送sql语句框子给DBMS,DBMS进行sql语句的预先编译
ps = conn.prepareStatement(sql);
//给占位符?传值(第一个?下标是1,第二个?下标是2,JDBC中所有的下标都是从1开始)
//传值方法的set后面跟传入值的类型,这里是字符串 所以是setString
ps.setString(1,userInfo.get("userName"));
ps.setString(2,userInfo.get("userPwd"));
//执行sql查询语句,返回的是查询结果集
//rs = stmt.executeQuery(sql);
rs = ps.executeQuery();//sql语句已经放进预编译数据库操作对象里了,所以不在传sql语句参数
//处理查询结果集
while (rs.next()){
return true;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return false;
}
/**
* 初始化用户界面
* @return 一个map数组,用户输入的用户名和密码
*/
private static Map<String, String> initUI() {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String userName = scanner.nextLine();//该方法表示一次读一行
System.out.println("请输入密码:");
String userPwd = scanner.nextLine();
Map<String,String> userInfo = new HashMap<>();
userInfo.put("userName",userName);
userInfo.put("userPwd",userPwd);
return userInfo;
}
}
4、适合使用Statement的情景
package ajdbctest;
import java.sql.*;
import java.util.Scanner;
/*
演示只能使用Statement的功能需求:
输入参数使查询结果升序或者是降序输出,需要将升序降序关键字的字符串拼接到SQL语句中
*/
public class JDBCTest07 {
public static void main(String[] args) {
// //用户输入控制查询结果升序降序的信息
// Scanner scanner = new Scanner(System.in);
// System.out.println("请输入关键字信息:升序asc,降序desc");
// String info = scanner.nextLine();
//
// //执行SQL
// Connection conn = null;
// PreparedStatement ps = null;
// ResultSet rs = null;
//
// try {
//
// Class.forName("com.mysql.jdbc.Driver");
//
// conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mysqlstudy?useSSL=false","root","rong195302");
//
// String sql = "select sal from emp order by sal ?";
// ps = conn.prepareStatement(sql);
// ps.setString(1,info);
//
// rs = ps.executeQuery();
//
// while (rs.next()){
// String sal = rs.getString(1);
// System.out.println(sal);
// }
//
// } catch (ClassNotFoundException e) {
// e.printStackTrace();
// } catch (SQLException throwables) {
// throwables.printStackTrace();
// }finally {
// if (rs != null) {
// try {
// rs.close();
// } catch (SQLException throwables) {
// throwables.printStackTrace();
// }
// }
// if (ps != null) {
// try {
// ps.close();
// } catch (SQLException throwables) {
// throwables.printStackTrace();
// }
// }
// if (conn != null) {
// try {
// conn.close();
// } catch (SQLException throwables) {
// throwables.printStackTrace();
// }
// }
// }
//用户输入控制查询结果升序降序的信息
Scanner scanner = new Scanner(System.in);
System.out.println("请输入关键字信息:升序asc,降序desc");
String info = scanner.nextLine();
//执行SQL
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mysqlstudy?useSSL=false","root","rong195302");
stmt = conn.createStatement();
String sql = "select sal from emp order by sal "+info;
rs = stmt.executeQuery(sql);
while (rs.next()){
String sal = rs.getString(1);
System.out.println(sal);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
5、使用PrepareStatement进行增删操作
package ajdbctest;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/*
使用PreparedStatement进行增删改
*/
public class JDBCTest08 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mysqlstudy?useSSL=false","root","rong195302");
//增
// String sql = "insert into dept(deptno,dname,loc) values (?,?,?)";
// ps = conn.prepareStatement(sql);
// ps.setInt(1,50);
// ps.setString(2,"销售部");
// ps.setString(3,"北京");
//改
// String sql = "update dept set dname = ?,loc = ? where deptno = ?";
// ps = conn.prepareStatement(sql);
// ps.setString(1,"技术部");//要注意下标
// ps.setString(2,"大唐");
// ps.setInt(3,50);
//删
String sql ="delete from dept where deptno = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1,50);
int count = ps.executeUpdate();//注意这里不是Query
System.out.println(count);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
if (ps != null) {
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}