通过 PreparedStatement 来执行 SQL 语句
一.PreparedStatement初识
-
之前我们都是通过 Statement 来执行的 SQL 语句
-
可以通过调用 Connection 对象的 preparedStatement() 方法获取PreparedStatement 对象
-
PreparedStatement 接口是 Statement 的子接口, 它表示一条预编译过的 SQL 语句
-
PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示, 调用PreparedStatement 对象的 setXxx() 方法来设置这些参数. setXxx() 方法有两个参数, 第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始), 第二个是设置的 SQL 语句中的参数的值
1.为什么要用PreparedStatement
-
看之前用Statement的程序, SQL语句需要我们去拼接, 很麻烦, 而PreparedStatement 就不需要我们拼接.
-
使用 PreparedStatement 代码的可读性和可维护性好.
-
PreparedStatement 能最大可能提高性能:
– DBServer会对预编译语句提供性能优化. 因为预编译语句有可能被重复调用, 所以语句在被DBServer的编译器编译后的执行代码被缓存下来, 那么下次调用时只要是相同的预编译语句就不需要编译, 只要将参数直接传入编译过的语句执行代码中就会得到执行.
– 在statement语句中, 即使是相同操作但因为数据内容不一样, 所以整个语句本身不能匹配, 没有缓存语句的意义. 事实是没有数据库会对普通语句编译后的执行代码缓存. 这样每执行一次都要对传入的语句编译一次.
– (语法检查, 语义检查, 翻译成二进制命令, 缓存)
- PreparedStatement 可以有效防止 SQL 注入
2.SQL 注入
-
SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查, 而在用户输入数据中注入非法的 SQL 语句段或命令, 从而利用系统的 SQL 引擎完成恶意行为的做法
-
对于 Java 而言, 要防范 SQL 注入, 只要用 PreparedStatement 取代 Statement 就可以了
例, 在数据库的UserInfo表中查询, 是否有某个用户名及对应密码. 用户名为: a’
OR PASSWORD = ;密码为: OR ‘1’=‘1’
用 Statement 方式, 拼接的 SQL 语句如下:
1 2
| SELECT UserName,Password FROM UserInfo WHERE UserName = 'a' OR PASSWORD = ' AND Password = ' OR '1'='1';
|
而 PreparedStatement 方式, 生成的 SQL 语句如下:
1 2 3
| SELECT UserName,Password FROM UserInfo WHERE UserName = "a' OR PASSWORD = " AND Password = " OR '1'='1'";
|
二.使用PreparedStatement
1.executeUpdate()
在 JDBCTools类中, 重载之前用Statement写的update(String sql)方法, 重载函数为update(String sql,Object … args).
这里的sql是带有占位符的SQL语句, 后面利用可变个数形参的方法传递参数. 若用数组的话, 在传递参数的时候还要将那些参数封装为数组.
这里的SQL语句是 insert delete update 语句, 不能是select语句. 之后用 PreparedStatement 的 executeUpdate() 方法执行生成的 SQL 语句.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class JDBCTools {
public static void update(String sql,Object ... args) { Connection conn = null; PreparedStatement ps = null; try { conn = JDBCTools.getConnection(); ps = conn.prepareStatement(sql); for(int i = 0;i < args.length;i ++) { ps.setObject(i+1, args[i]); } ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); }finally { JDBCTools.release(null,ps, conn); } } }
|
2.executeQuery()
使用 PreparedStatement 的executeQuery()方法, 执行带有 select 的SQL语句.
当然, 你也可以和执行 JDBCTools 里的update()方法一样, 将查询数据库的代码抽取成一个工具方法, 之后会讲如何写一个通用的查询方法, 放在 JDBCTools 类里面. 我就直接测试了.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| import java.io.FilterInputStream; import java.io.IOException; import java.sql.Connection; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Scanner; import org.junit.Test;
public class TestJDBCTools { @Test public void testAddStudent() { Student stu = getStudentFromConsole(); addStudent(stu); } private Student getStudentFromConsole() { Student stu = new Student(); Scanner scanner = new Scanner(System.in); System.out.print("请输入学生ID:"); stu.setId(scanner.nextInt()); System.out.print("请输入学生Name:"); stu.setName(scanner.next()); System.out.print("请输入学生Email:"); stu.setEmail(scanner.next()); System.out.print("请输入学生Birth:"); stu.setDate(Date.valueOf(scanner.next())); System.out.println(stu.getDate()); scanner.close(); return stu; } public void addStudent(Student stu) { String sql = "INSERT INTO Student VALUES(?,?,?,?)"; JDBCTools.update(sql,stu.getId(),stu.getName(),stu.getEmail(),stu.getDate()); } @Test public void testSearchStudent() { int type = getSearchType(); Student stu = searchStudent(type); showMsg(stu); } private void showMsg(Student stu) { if(stu == null) { System.out.println("查无此人!"); }else { System.out.println(stu); } } private int getSearchType() { System.out.print("请输入查询类型:1.id查询 2.姓名查询"); Scanner scanner = new Scanner(new FilterInputStream(System.in){ @Override public void close() throws IOException { } }); int type = scanner.nextInt(); scanner.close(); if(type != 1 && type != 2) { System.out.println("输入的查询类型有误!"); throw new RuntimeException(); } return type; } public Student searchStudent(int type) { String sql = null; Student stu = null; Scanner scanner = new Scanner(System.in); if(type == 1) { System.out.print("请输入要查询的id:"); int id = scanner.nextInt(); sql = "SELECT * FROM Student WHERE id = ?"; stu = getStudent(sql,id); }else { System.out.print("请输入要查询的Name:"); String name = scanner.next(); sql = "SELECT * FROM Student WHERE name = '?'"; stu = getStudent(sql,name); } scanner.close(); return stu; } private Student getStudent(String sql,Object ... args) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; Student stu = null; try { conn = JDBCTools.getConnection(); ps = conn.prepareStatement(sql); for(int i = 0;i < args.length;i ++) { ps.setObject(i+1, args[i]); } rs = ps.executeQuery(); if(rs.next()) { stu = new Student( rs.getInt(1), rs.getString(2).trim(), rs.getString(3).trim(), rs.getDate(4)); } } catch (Exception e) { e.printStackTrace(); } finally { JDBCTools.release(rs, ps, conn); } return stu; } }
|