1. PreparedStatement预编译的语句对象
预编译语句PreparedStatement 是java.sql中的一个接口,它是Statement的子接口。通过Statement对象执行SQL语句时,需要将SQL语句发送给DBMS,由 DBMS首先进行编译后再执行。预编译语句和Statement不同,在创建PreparedStatement 对象时就指定了SQL语句,该语句立即发送给DBMS进行编译。当该编译语句被执行时,DBMS直接运行编译后的SQL语句,而不需要像其他SQL语句那样首先将其编译,在执行。
2.PreparedStatement和Statement的区别
在用法上不一样:
① Statement是普通的语句对象,PreparedStatement是预编译的语句对象;
Statement创建对象时不传入SQL,PreparedStatement创建对象是需要传入SQL;
Statement在执行时需传入SQL,PreparedStatement执行时不需传入SQL。
② Statement是拼接字符串效果,PreparedStatement不用拼接字符串;
③ PreparedStatement执行效率高于Statement;
④ PreparedStatement不是拼接字符串可以防止SQL注入问题;
具体实现代码如下:
1 package cn.yif.dao.impl; 2 3 import cn.yif.domain.Student; 4 import cn.yif.dao.IStudentDao; 5 import cn.yif.domain.User; 6 import cn.yif.utils.DBCPUtil; 7 import cn.yif.utils.JDBCUtil; 8 9 import java.sql.*; 10 import java.util.ArrayList; 11 import java.util.List; 12 13 /** 14 * @author Administrator 15 * @create 2019-09-13-20:33 16 */ 17 public class StudentDaoImpl implements IStudentDao { 18 @Override 19 public void insert(Student student) { 20 Connection conn = null; 21 PreparedStatement preStatement = null; 22 try { 23 //①加载(注册)驱动,这部分硬编码可以写也可以不写,jdbc4.0版本自带了 24 //Class.forName("com.mysql.jdbc.Driver"); 25 //②获取连接 26 //conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest01", "root", "admin"); 27 conn = JDBCUtil.getInstance().getConnection(); 28 //③预编译的语句对象:提前编译好SQL语句 29 String insertSql = "insert into jdbcdemo02 (name, age) values (?, ?)"; 30 preStatement = conn.prepareStatement(insertSql); 31 preStatement.setString(1, student.getName()); 32 preStatement.setInt(2, student.getAge()); 33 //④执行SQL语句:执行时无需代入SQL 34 preStatement.execute(); 35 }catch (Exception ex){ 36 ex.printStackTrace(); 37 }finally { 38 //⑤释放资源 39 JDBCUtil.getInstance().close(null, preStatement, conn); 40 } 41 } 42 43 @Override 44 public void update(Student student) { 45 Connection conn = null; 46 PreparedStatement preStatement = null; 47 try { 48 //①加载(注册)驱动 49 //Class.forName("com.mysql.jdbc.Driver"); 50 //②获取连接 51 //conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest01", "root", "admin"); 52 conn = JDBCUtil.getInstance().getConnection(); 53 //③预编译的语句对象:提前编译好SQL语句 54 String updateSql = "update jdbcdemo02 set name = ?, age = ? where id = ?"; 55 preStatement = conn.prepareStatement(updateSql); 56 preStatement.setString(1, student.getName()); 57 preStatement.setInt(2, student.getAge()); 58 preStatement.setInt(3, student.getId()); 59 //④执行SQL语句:执行时无需代入SQL 60 preStatement.execute(); 61 }catch (Exception ex){ 62 ex.printStackTrace(); 63 }finally { 64 //⑤释放资源 65 JDBCUtil.getInstance().close(null, preStatement, conn); 66 } 67 } 68 69 @Override 70 public void delete(Integer id) { 71 Connection conn = null; 72 PreparedStatement preStatement = null; 73 try { 74 //①加载(注册)驱动 75 //Class.forName("com.mysql.jdbc.Driver"); 76 //②获取连接 77 //conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest01", "root", "admin"); 78 conn = JDBCUtil.getInstance().getConnection(); 79 //③预编译的语句对象:提前编译好SQL语句 80 String deleteSql = "delete from jdbcdemo02 where id = ?"; 81 preStatement = conn.prepareStatement(deleteSql); 82 preStatement.setInt(1, id); 83 //④执行SQL语句:执行时无需代入SQL 84 preStatement.execute(); 85 }catch (Exception ex){ 86 ex.printStackTrace(); 87 }finally { 88 //⑤释放资源 89 JDBCUtil.getInstance().close(null, preStatement, conn); 90 } 91 } 92 93 @Override 94 public Student queryOne(Integer id) { 95 Connection conn = null; 96 PreparedStatement preStatement = null; 97 ResultSet resultSet = null; 98 Student student = null; 99 try { 100 //①加载(注册)驱动 101 //Class.forName("com.mysql.jdbc.Driver"); 102 //②获取连接 103 //conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest01", "root", "admin"); 104 conn = JDBCUtil.getInstance().getConnection(); 105 //③预编译的语句对象:提前编译好SQL语句 106 String queryOneSql = "select * from jdbcdemo02 where id = ?"; 107 preStatement = conn.prepareStatement(queryOneSql); 108 preStatement.setInt(1, id); 109 //④执行SQL语句:执行时无需代入SQL 110 resultSet = preStatement.executeQuery(); 111 if(resultSet.next()) 112 { 113 student = new Student(); 114 student.setId(resultSet.getInt("id")); 115 student.setName(resultSet.getString("name")); 116 student.setAge(resultSet.getInt("age")); 117 } 118 }catch (Exception ex){ 119 120 }finally { 121 //⑤释放资源 122 JDBCUtil.getInstance().close(resultSet, preStatement, conn); 123 } 124 return student; 125 } 126 127 @Override 128 public List<Student> queryAll() { 129 Connection conn = null; 130 PreparedStatement preStatement = null; 131 ResultSet resultSet = null; 132 Student student = null; 133 List<Student> list = new ArrayList<Student>(); 134 try { 135 //①加载(注册)驱动 136 //Class.forName("com.mysql.jdbc.Driver"); 137 //②获取连接 138 //conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest01", "root", "admin"); 139 conn = JDBCUtil.getInstance().getConnection(); 140 //③获取语句对象:用来执行SQL语句 141 String queryAllSql = "select * from jdbcdemo02"; 142 preStatement = conn.prepareStatement(queryAllSql); 143 //④执行SQL语句 144 resultSet = preStatement.executeQuery(queryAllSql); 145 while (resultSet.next()) 146 { 147 student = new Student(); 148 student.setId(resultSet.getInt("id")); 149 student.setName(resultSet.getString("name")); 150 student.setAge(resultSet.getInt("age")); 151 list.add(student); 152 } 153 }catch (Exception ex){ 154 ex.printStackTrace(); 155 }finally { 156 //⑤释放资源 157 JDBCUtil.getInstance().close(resultSet, preStatement, conn); 158 } 159 return list; 160 }
3.登录方式实现
这里采用简单模拟的方式,比较Statement与PreparedStatement的不同,以及SQL注入问题带来的影响:
登录实现代码:
1 @Override 2 public User Login1_PreparedSt(String userName, String passWord) { 3 Connection conn = null; 4 PreparedStatement preStatement = null; 5 ResultSet resultSet = null; 6 User user = null; 7 try { 8 conn = JDBCUtil.getInstance().getConnection(); 9 String queryOneSql = "select * from jdbcdemo02_user where username = ? and password = ?"; 10 preStatement = conn.prepareStatement(queryOneSql); 11 preStatement.setString(1, userName); 12 preStatement.setString(2, passWord); 13 resultSet = preStatement.executeQuery(); 14 if(resultSet.next()) 15 { 16 user = new User(); 17 user.setId(resultSet.getInt("id")); 18 user.setUsername(resultSet.getString("username")); 19 user.setPassword(resultSet.getString("password")); 20 user.setAge(resultSet.getInt("age")); 21 user.setSex(resultSet.getInt("sex")); 22 } 23 }catch (Exception ex){ 24 25 }finally { 26 //⑤释放资源 27 JDBCUtil.getInstance().close(resultSet, preStatement, conn); 28 } 29 return user; 30 } 31 32 @Override 33 public User Login2_PreparedSt(String userName) { 34 Connection conn = null; 35 PreparedStatement preStatement = null; 36 ResultSet resultSet = null; 37 User user = null; 38 try { 39 conn = JDBCUtil.getInstance().getConnection(); 40 String queryOneSql = "select * from jdbcdemo02_user where username = ?"; 41 preStatement = conn.prepareStatement(queryOneSql); 42 preStatement.setString(1, userName); 43 resultSet = preStatement.executeQuery(); 44 if(resultSet.next()) 45 { 46 user = new User(); 47 user.setId(resultSet.getInt("id")); 48 user.setUsername(resultSet.getString("username")); 49 user.setPassword(resultSet.getString("password")); 50 user.setAge(resultSet.getInt("age")); 51 user.setSex(resultSet.getInt("sex")); 52 } 53 }catch (Exception ex){ 54 55 }finally { 56 //⑤释放资源 57 JDBCUtil.getInstance().close(resultSet, preStatement, conn); 58 } 59 return user; 60 } 61 62 @Override 63 public User Login1_St(String userName, String passWord) { 64 Connection conn = null; 65 Statement statement = null; 66 ResultSet resultSet = null; 67 User user = null; 68 try { 69 conn = JDBCUtil.getInstance().getConnection(); 70 statement = conn.createStatement(); 71 String queryOneSql = "select * from jdbcdemo02_user where username = '"+userName+"' and password = '"+passWord+"'"; 72 resultSet = statement.executeQuery(queryOneSql); 73 if(resultSet.next()) 74 { 75 user = new User(); 76 user.setId(resultSet.getInt("id")); 77 user.setUsername(resultSet.getString("username")); 78 user.setPassword(resultSet.getString("password")); 79 user.setAge(resultSet.getInt("age")); 80 user.setSex(resultSet.getInt("sex")); 81 } 82 }catch (Exception ex){ 83 84 }finally { 85 //⑤释放资源 86 JDBCUtil.getInstance().close(resultSet, statement, conn); 87 } 88 return user; 89 }
后台使用SQL注入测试代码:
1 @org.junit.Test 2 public void Login1_PreparedSt() { 3 User test1 = new User(); 4 //正常情况:未有SQL注入 5 test1.setUsername("李华"); 6 test1.setPassword("123456"); 7 test1.setSex(2); 8 //异常情况:有SQL注入 9 // test1.setUsername(" 'or 1=1 or' "); 10 // test1.setPassword("456"); 11 // test1.setSex(2); 12 IStudentDao userDao = new StudentDaoImpl(); 13 User user = userDao.Login1_PreparedSt(test1.getUsername(), test1.getPassword()); 14 if(user != null){ 15 System.out.println(String.format("恭喜%s登录成功!", user.getUsername())); 16 } 17 else { 18 System.out.println("用户名或密码错误,登录失败!"); 19 } 20 } 21 22 @org.junit.Test 23 public void Login2_PreparedSt() { 24 User test1 = new User(); 25 test1.setUsername("李华"); 26 test1.setPassword("123456"); 27 test1.setSex(2); 28 IStudentDao userDao = new StudentDaoImpl(); 29 User user = userDao.Login2_PreparedSt(test1.getUsername()); 30 if(user != null){ 31 if(user.getPassword().equals(test1.getPassword())){ 32 System.out.println(String.format("恭喜%s登录成功!", user.getUsername())); 33 } 34 else { 35 System.out.println("密码错误,登录失败!"); 36 } 37 } 38 else { 39 System.out.println("用户名错误,登录失败!"); 40 } 41 } 42 43 @org.junit.Test 44 public void Login1_St() { 45 User test1 = new User(); 46 //正常情况:未有SQL注入 47 //test1.setUsername("李华"); 48 //test1.setPassword("123456"); 49 //test1.setSex(2); 50 //异常情况:有SQL注入 51 test1.setUsername(" 'or 1=1 or' "); 52 //这里就是一条拼接的SQL -- select * from jdbcdemo02_user where username = ''or 1=1 or'' and password = 12344 53 test1.setPassword("145"); 54 test1.setSex(2); 55 IStudentDao userDao = new StudentDaoImpl(); 56 User user = userDao.Login1_St(test1.getUsername(), test1.getPassword()); 57 if(user != null){ 58 System.out.println(String.format("恭喜%s登录成功!", user.getUsername())); 59 } 60 else { 61 System.out.println("用户名或密码错误,登录失败!"); 62 } 63 }
4.连接池
4.1.连接池介绍
连接池:简单地说,就是用来装连接对象的容器。
背景:在高并发访问的网页上,每次请求都会创建一个connection,因此会非常浪费资源(内存),当同时1000人访问的时候,那就会占用很多资源,因此浪费很多时间并且导致容器操作系统崩溃。
而连接池里面取connection则只需要从连接池里面拿到,不需要用户名和密码,用完之后,还回到连接池。
在Java中,在Java中,连接池使用javax.sql.DataSource接口来表示连接池. 这里的DataSource就是连接池。连接池就是DataSource。DataSource是接口,和JDBC一样,是Sun公司开发的一套接口,需要各大厂商去实现:导入各大厂商对应的jar包;
常用的DataSource的实现有下面两种方式:
DBCP: Spring推荐的(Spring框架已经集成DBCP)
C3P0: Hibernate推荐的(早期)(Hibernate框架已经集成C3P0)
DBCP连接池配置文件
dbcp.properties
#连接字符串
url=jdbc:mysql://localhost:3306/jdbcdemo
#用户名
username=root
#密码
password=admin
#驱动的类路径
driverClassName=com.mysql.jdbc.Driver
#连接池启动时的初始值
initialSize=1
#连接池的最大值
maxActive=50
#连接池的最大空闲数
maxIdle=20
具体代码实现如下:
1 @Override 2 public Student queryOneByDBCP(Integer id) { 3 Connection conn = null; 4 PreparedStatement preStatement = null; 5 ResultSet resultSet = null; 6 Student student = null; 7 try { 8 //①加载(注册)驱动 9 //Class.forName("com.mysql.jdbc.Driver"); 10 //②获取连接 11 //conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest01", "root", "admin"); 12 conn = DBCPUtil.getInstance().getConnection(); 13 //③预编译的语句对象:提前编译好SQL语句 14 String queryOneSql = "select * from jdbcdemo02 where id = ?"; 15 preStatement = conn.prepareStatement(queryOneSql); 16 preStatement.setInt(1, id); 17 //④执行SQL语句:执行时无需代入SQL 18 resultSet = preStatement.executeQuery(); 19 if(resultSet.next()) 20 { 21 student = new Student(); 22 student.setId(resultSet.getInt("id")); 23 student.setName(resultSet.getString("name")); 24 student.setAge(resultSet.getInt("age")); 25 } 26 }catch (Exception ex){ 27 28 }finally { 29 //⑤释放资源 30 DBCPUtil.getInstance().close(resultSet, preStatement, conn); 31 } 32 return student; 33 }
DBCPUtil.java文件:
1 package cn.yif.utils; 2 3 import org.apache.commons.dbcp.BasicDataSource; 4 import org.apache.commons.dbcp.BasicDataSourceFactory; 5 6 import javax.sql.DataSource; 7 import java.io.IOException; 8 import java.sql.*; 9 import java.util.Properties; 10 11 public class DBCPUtil { 12 //使用静态static类构造单例模式 13 private DBCPUtil(){ } 14 //在静态代码块中创建instance的实例,这里使用static静态变量来保存 15 private static DBCPUtil instance = null; 16 //每次都要加载驱动,这里定义一个Properties,把资源文件里面的内容读到Properties里面 17 private static Properties properties = null; 18 //定义一个连接池对象 19 //private static BasicDataSource ds = null; 20 private static DataSource ds = null; 21 //JDBCUtil类加载的时候,就加载注册驱动 22 static { 23 properties = new Properties(); 24 try { 25 //当前线程类加载器加载获取properties文件 26 properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties")); 27 //方式一:创建一个BasicDataSource对象来读取配置文件 28 // ds = new BasicDataSource(); 29 // ds.setDriverClassName(properties.getProperty("driverClassName")); 30 // ds.setUrl(properties.getProperty("url")); 31 // ds.setUsername(properties.getProperty("username")); 32 // ds.setPassword(properties.getProperty("password")); 33 //方式二:使用工厂DataSourceFactory来加载配置文件 34 try { 35 ds = BasicDataSourceFactory.createDataSource(properties); 36 } catch (Exception e) { 37 e.printStackTrace(); 38 } 39 } catch (IOException e) { 40 e.printStackTrace(); 41 } 42 instance = new DBCPUtil(); 43 } 44 45 public static DBCPUtil getInstance(){ 46 return instance; 47 } 48 49 //抽取获取连接Connection的方法 50 public Connection getConnection() throws SQLException { 51 //这里不再使用DriverManager,而应该使用BasicDataSource,从连接池中拿到这个连接 52 return ds.getConnection(); 53 } 54 55 //抽取JDBC关闭资源的close方法 56 public void close(ResultSet resultSet, Statement statement, Connection conn){ 57 try { 58 if(resultSet != null){ 59 resultSet.close(); 60 } 61 } catch (SQLException e) { 62 e.printStackTrace(); 63 } 64 try { 65 if(statement != null){ 66 statement.close(); 67 } 68 } catch (SQLException e) { 69 e.printStackTrace(); 70 } 71 try { 72 if(conn != null){ 73 conn.close(); 74 } 75 } catch (SQLException e) { 76 e.printStackTrace(); 77 } 78 } 79 }