預編譯語句PreparedStatement 是java.sql中的一個接口,它是Statement的子接口。經過Statement對象執行SQL語句時,須要將SQL語句發送給DBMS,由 DBMS首先進行編譯後再執行。預編譯語句和Statement不一樣,在建立PreparedStatement 對象時就指定了SQL語句,該語句當即發送給DBMS進行編譯。當該編譯語句被執行時,DBMS直接運行編譯後的SQL語句,而不須要像其餘SQL語句那樣首先將其編譯,在執行。java
在用法上不同:mysql
① Statement是普通的語句對象,PreparedStatement是預編譯的語句對象;sql
Statement建立對象時不傳入SQL,PreparedStatement建立對象是須要傳入SQL;apache
Statement在執行時需傳入SQL,PreparedStatement執行時不需傳入SQL。併發
② Statement是拼接字符串效果,PreparedStatement不用拼接字符串;框架
③ PreparedStatement執行效率高於Statement;dom
④ PreparedStatement不是拼接字符串能夠防止SQL注入問題;ide
具體實現代碼以下:高併發
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 }
這裏採用簡單模擬的方式,比較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 }
鏈接池:簡單地說,就是用來裝鏈接對象的容器。
背景:在高併發訪問的網頁上,每次請求都會建立一個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 }