PreparedStatement
接口继承了Statement
,也就是说: PreparedStatement
接口是 Statement
接口的子接口PreparedStatement
代替Statement
.也就是说,在任何时候都不要使用Statement
1、代码
package class01;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class Demo01 {
public static void main(String[] args){
Connection conn = null;
Statement sta = null;
String url = "jdbc:mysql://localhost:3306/db_test";
String user = "root";
String pwd = "lemon";
try {
conn = DriverManager.getConnection(url,user,pwd);
sta = conn.createStatement();
sta.executeUpdate("create table userlogin (id int primary key auto_increment,name varchar(20),password varchar(20))");
System.out.println("创建成功");
} catch (SQLException e) {
e.printStackTrace();
}finally {
if (sta != null){
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
2、插入数据
insert into userlogin values (null,'lemon','lemon'),(null,'lily','lily')
package class01;
import java.sql.*;
import java.util.Scanner;
public class Demo01{
public static void main(String[] args){
//在控制台输入用户名和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String name = sc.nextLine();
System.out.println("请输入密码:");
String pwd = sc.nextLine();
login(name,pwd);
}
//登录方法
public static void login(String name,String pwd){
Connection conn = null;
Statement sta = null;
ResultSet rs = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_test","root","lemon");
sta = conn.createStatement();
//查询数据库,如果有记录则表示登录成功,否则登录失败
String sql = "select * from userlogin where name='" + name + "' and password='" + pwd + "'";
System.out.println(sql);
rs = sta.executeQuery(sql);
if (rs.next()){
System.out.println("登录成功,欢迎" + name);
}else{
System.out.println("登录失败,请重新登录!!!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
if (rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (sta != null){
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
在这段代码中,当输入 a' or '1' = '1
也会显示登录成功!
问题分析:
select * from userlogin where name='newbady' and password='a' or '1' = '1'
,name='newbady' and password='a'
为假, '1' = '1'
为真select * from userlogin where true
我们让用户输入的密码和 SQL 语句进行字符串拼接。用户输入的内容作为了 SQL 语句语法的一部分,改变了原有 SQL 真正的意义,以上问题称为 SQL 注入。要解决 SQL 注入就不能让用户输入的密码和我们的 SQL 语句进行简单的字符串拼接。
1、概述
PreparedStatement
对象是一个预编译的SQL语句PreparedStatement
在JDK中的 java.sql 包下PreparedStatement
是 Statement
接口的子接口,继承于父接口中所有的方法2、执行原理
3、Connection 创建 PreparedStatement 对象
4、PreparedStatement 接口中的方法
5、使用 PreparedStatement 的步骤
"SELECT * FROM user WHERE name=? AND password=?";
6、PrepareStatement 中设置参数的方法
7、使用 PreparedStatement 接口修改 (三、使用 Statement 模拟登录)案例中的问题,防止 SQL 注入
package class01;
import java.sql.*;
import java.util.Scanner;
public class Demo01{
public static void main(String[] args){
//在控制台输入用户名和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String name = sc.nextLine();
System.out.println("请输入密码:");
String pwd = sc.nextLine();
login(name,pwd);
}
//登录方法
public static void login(String name,String pwd){
Connection conn = null;
Statement sta = null;
ResultSet rs = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_test","root","lemon");
sta = conn.createStatement();
//查询数据库,如果有记录则表示登录成功,否则登录失败
//String sql = "select * from userlogin where name='" + name + "' and password='" + pwd + "'";
String sql = "select * from userlogin where name = ? and password = ?";
//System.out.println(sql);
PreparedStatement ps = conn.prepareStatement(sql);//得到语句对象
//设置参数
ps.setString(1,name);
ps.setString(2,pwd);
rs = ps.executeQuery();
//rs = sta.executeQuery(sql);
if (rs.next()){
System.out.println("登录成功,欢迎" + name);
}else{
System.out.println("登录失败,请重新登录!!!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
if (rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (sta != null){
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
以 db_test 数据库中的 student 表为例
1、写数据库操作工具类 JdbcUtils.java
package student.jdbcutils;
import java.sql.*;
public class JdbcUtils {
//把用户名、密码、URL、驱动类 这几个字符串定义为常量
private static final String USER = "root";
private static final String PWD = "lemon";
private static final String URL = "jdbc:mysql://localhost:3306/db_test";
private static final String DRIVER = "com.mysql.jdbc.Driver";
//注册驱动
static {//静态代码块,只执行一次
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//得到数据库的连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PWD);
}
//关闭所有打开的资源
public static void close(Connection conn, Statement stm) {
if (stm != null) {
try {
stm.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//关闭所有打开的资源
public static void close(Connection conn, Statement stm, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stm != null) {
try {
stm.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
2、Dao 模式具体类 StudentDao.java 接口
package student.dao;
import student.domain.Student;
import java.util.List;
public interface StudentDao {
//写增删改查
public int add(Student s);
public int delete(Integer id);
public int update(Student s);
public List<Student> find();//查所有
public Student findById(Integer id);//查单个
}
3、写 StudentDao 接口的实现类 StudentDaoImpl.java,用来实现接口
package student.dao;
import student.domain.Student;
import student.jdbcutils.JdbcUtils;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class StudentDaoImpl implements StudentDao{
@Override
public int add(Student s) {
int i = 0;
Connection conn = null;
PreparedStatement ps = null;
try {
//conn = DriverManager.getConnection(url,user,password);
conn = JdbcUtils.getConnection();//引用创建好的工具类,在 jdbcutils 包下
ps = conn.prepareStatement("insert into student values (null,?,?,?,?)");
ps.setString(1,s.getName());
ps.setString(2,s.getSex());
//ps.setDate(3,s.getBirthday());//这样写不对
ps.setDate(3,new Date(s.getBirthday().getTime()));//得到一个时间戳,通过日期函数转换成日期
ps.setInt(4,s.getNum());
i = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//释放资源,引用创建好的工具类,在 jdbcutils 包下
JdbcUtils.close(conn,ps);
}
return i;
}
@Override
public int delete(Integer id) {
int i = 0;
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JdbcUtils.getConnection();//引用创建好的工具类,在 jdbcutils 包下
ps = conn.prepareStatement("delete from student where id = ?");
ps.setInt(1,id);
i = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//释放资源
JdbcUtils.close(conn,ps);
}
return i;
}
@Override
public int update(Student s) {
int i = 0;
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JdbcUtils.getConnection();
ps = conn.prepareStatement("update student set name = ?,sex = ?,birthday = ?,num = ? where id = ?");
ps.setString(1,s.getName());
ps.setString(2,s.getSex());
ps.setDate(3,new Date(s.getBirthday().getTime()));
ps.setInt(4,s.getNum());
ps.setInt(5,s.getId());
i = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(conn,ps);
}
return i;
}
@Override
public List<Student> find() {
List<Student> list = new ArrayList<>();
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
ps = conn.prepareStatement("SELECT * FROM student");
rs = ps.executeQuery();
while (rs.next()) {
Student s = new Student();
s.setId(rs.getInt("id"));
s.setName(rs.getString("name"));
s.setSex(rs.getString("sex"));
s.setBirthday(rs.getDate("birthday"));
s.setNum(rs.getInt("num"));
list.add(s);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.close(conn, ps, rs);
}
return list;
}
@Override
public Student findById(Integer id) {
Student s = null;
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
ps = conn.prepareStatement("SELECT * FROM student where id = ?");
ps.setInt(1, id);
rs = ps.executeQuery();
while (rs.next()) {
s = new Student();
s.setId(rs.getInt("id"));
s.setName(rs.getString("name"));
s.setSex(rs.getString("sex"));
s.setBirthday(rs.getDate("birthday"));
s.setNum(rs.getInt("num"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.close(conn, ps, rs);
}
return s;
}
}
4、封装 Student.java 实体类,与数据表对应
package student.domain;
import java.util.Date;
public class Student {
private Integer id;
private String name;
private String sex;
private Date birthday;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
private Integer num;
}
5、测试类 StudentDaoTest.java ,输出数据表的数据
package student.test;
import student.dao.StudentDao;
import student.dao.StudentDaoImpl;
import student.domain.Student;
import java.util.List;
public class StudentDaoTest {
public static void main(String[] args){
StudentDao dao = new StudentDaoImpl();
//查询
List<Student> list = dao.find();
for (Student student : list) { //增强 for 循环,快捷 iter
System.out.println(student.getId() + "\t" +
student.getName() + "\t" +
student.getSex() + "\t" +
student.getBirthday() + "\t" +
student.getNum());
}
}
}
package student.test;
import student.dao.StudentDao;
import student.dao.StudentDaoImpl;
public class StudentDaoTest {
public static void main(String[] args) {
StudentDao dao = new StudentDaoImpl();
//在表中删除一条数据
dao.delete(5);
}
}
以上代码太过复杂,查看以Spring中的JdbcTemplete类简化后的 Dao 模式请点击