方立勋_30天掌握JavaWeb_JDBC、SQL防注入(一)
JDBC簡介
數(shù)據(jù)庫驅(qū)動
SUN公司為了簡化、統(tǒng)一對數(shù)據(jù)庫的操作,定義了一套Java操作數(shù)據(jù)庫的規(guī)范,稱之為JDBC。
JDBC全稱為:Java Data Base Connectivity(java數(shù)據(jù)庫連接),它主要由接口組成。
組成JDBC的2個包:
- java.sql
- javax.sql
開發(fā)JDBC應(yīng)用需要以上2個包的支持外,還需要導(dǎo)入相應(yīng)JDBC的數(shù)據(jù)庫實現(xiàn)(即數(shù)據(jù)庫驅(qū)動)。
第一個JDBC程序
編寫一個程序,這個程序從user表中讀取數(shù)據(jù),并打印在命令行窗口中。
一、搭建實驗環(huán)境 :
1. 在mysql中創(chuàng)建一個庫,并創(chuàng)建user表和插入表的數(shù)據(jù)。
2. 新建一個Java工程,并導(dǎo)入數(shù)據(jù)驅(qū)動。
二、編寫程序,在程序中加載數(shù)據(jù)庫驅(qū)動
DriverManager. registerDriver(Driver driver)
三、建立連接(Connection)
Connection conn = DriverManager.getConnection(url,user,pass);
四、創(chuàng)建用于向數(shù)據(jù)庫發(fā)送SQL的Statement對象,并發(fā)送sql
Statement st = conn.createStatement();
ResultSet rs = st.excuteQuery(sql);
五、從代表結(jié)果集的ResultSet中取出數(shù)據(jù),打印到命令行窗口
六、斷開與數(shù)據(jù)庫的連接,并釋放相關(guān)資源
1.先在數(shù)據(jù)庫中新建數(shù)據(jù)庫和表:
create database day14 character set utf8 collate utf8_general_ci;use day14;create table user(id int primary key auto_increment,name varchar(40),password varchar(40),email varchar(60),birthday date )character set utf8 collate utf8_general_ci;insert into user(name,password,email,birthday) values('zs','123456','zs@sina.com','1980-12-04'); insert into user(name,password,email,birthday) values('lisi','123456','lisi@sina.com','1981-12-04'); insert into user(name,password,email,birthday) values('wangwu','123456','wangwu@sina.com','1979-12-04');2.Java代碼去訪問數(shù)據(jù)庫表:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement;public class Demo1 {public static void main(String[] args) throws Exception{/**create database jdbc character set utf8 collate utf8_general_ci;use jdbc;create table user(id int primary key auto_increment,name varchar(40),password varchar(40),email varchar(60),birthday date)character set utf8 collate utf8_general_ci;insert into user(name,password,email,birthday) values('zs','123456','zs@sina.com','1980-12-04');insert into user(name,password,email,birthday) values('lisi','123456','lisi@sina.com','1981-12-04');insert into user(name,password,email,birthday) values('wangwu','123456','wangwu@sina.com','1979-12-04'); */String url="jdbc:mysql://localhost:3306/day14";String user ="root";String password="";// 1. 加載驅(qū)動DriverManager.registerDriver(new com.mysql.jdbc.Driver()); // 2.獲取連接Connection con = DriverManager.getConnection(url, user, password); //導(dǎo)入java.sql的包,非mysql包// 3.獲取向數(shù)據(jù)庫發(fā)sql語句的statement對象Statement st = con.createStatement();//導(dǎo)入java.sql的包,非mysql包// 4.向數(shù)據(jù)庫發(fā)送sql,獲取數(shù)據(jù)庫返回的結(jié)果集ResultSet rs = st.executeQuery("select * from users");// 5.從結(jié)果集中獲取數(shù)據(jù)while(rs.next()){System.out.println(rs.getObject("id"));System.out.println(rs.getObject("name"));System.out.println(rs.getObject("password"));System.out.println(rs.getObject("email"));System.out.println(rs.getObject("birthday"));}rs.close();st.close();con.close();} }這樣的方式其實不好,查看Driver類源代碼:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {// ~ Static fields/initializers// ---------------------------------------------//// Register ourselves with the DriverManager//static {try {java.sql.DriverManager.registerDriver(new Driver());} catch (SQLException E) {throw new RuntimeException("Can't register driver!");}}// ~ Constructors// -----------------------------------------------------------/*** Construct a new driver and register it with DriverManager* * @throws SQLException* if a database error occurs.*/public Driver() throws SQLException {// Required for Class.forName().newInstance()} }會發(fā)現(xiàn)這里也進(jìn)行注冊了一次,所以加載mysql驅(qū)動會進(jìn)行兩次驅(qū)動加載。
這里可以采用另一種方式加載驅(qū)動:
Class.forName(“com.mysql.jdbc.Driver”);
MySQL官方手冊說的也是這種方式。
程序詳解
DriverManager
jdbc程序中的DriverManager用于加載驅(qū)動,并創(chuàng)建與數(shù)據(jù)庫的鏈接,這個API的常用方法:
DriverManager.registerDriver(new Driver())
DriverManager.getConnection(url, user, password),
注意:在實際開發(fā)中并不推薦采用registerDriver方法注冊驅(qū)動。原因有二:
- 查看Driver的源代碼可以看到,如果采用此種方式,會導(dǎo)致驅(qū)動程序注冊兩次,也就是在內(nèi)存中會有兩個Driver對象。
- 程序依賴mysql的api,脫離mysql的jar包,程序?qū)o法編譯,將來程序切換底層數(shù)據(jù)庫將會非常麻煩。
推薦方式:Class.forName(“com.mysql.jdbc.Driver”);
采用此種方式不會導(dǎo)致驅(qū)動對象在內(nèi)存中重復(fù)出現(xiàn),并且采用此種方式,程序僅僅只需要一個字符串,不需要依賴具體的驅(qū)動,使程序的靈活性更高。
同樣,在開發(fā)中也不建議采用具體的驅(qū)動類型指向getConnection方法返回的connection對象。
數(shù)據(jù)庫URL
URL用于標(biāo)識數(shù)據(jù)庫的位置,程序員通過URL地址告訴JDBC程序連接哪個數(shù)據(jù)庫,URL的寫法為:
jdbc:mysql:[]//localhost:3306/test ?參數(shù)名:參數(shù)值
常用數(shù)據(jù)庫URL地址的寫法:
- Oracle寫法:jdbc:oracle:thin:@localhost:1521:sid
- SqlServer—jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sid
- MySql—jdbc:mysql://localhost:3306/sid
Mysql的url地址的簡寫形式: jdbc:mysql:///sid
常用屬性:useUnicode=true&characterEncoding=UTF-8
Connection
Jdbc程序中的Connection,它用于代表數(shù)據(jù)庫的鏈接,Collection是數(shù)據(jù)庫編程中最重要的一個對象,客戶端與數(shù)據(jù)庫所有交互都是通過
- connection對象完成的,這個對象的常用方法:
- createStatement():創(chuàng)建向數(shù)據(jù)庫發(fā)送sql的statement對象。
- prepareStatement(sql) :創(chuàng)建向數(shù)據(jù)庫發(fā)送預(yù)編譯sql的PrepareSatement對象。
- prepareCall(sql):創(chuàng)建執(zhí)行存儲過程的callableStatement對象。
- setAutoCommit(boolean autoCommit):設(shè)置事務(wù)是否自動提交。
- commit() :在鏈接上提交事務(wù)。
- rollback() :在此鏈接上回滾事務(wù)。
Statement
Jdbc程序中的Statement對象用于向數(shù)據(jù)庫發(fā)送SQL語句, Statement對象常用方法:
- executeQuery(String sql) :用于向數(shù)據(jù)發(fā)送查詢語句。
- executeUpdate(String sql):用于向數(shù)據(jù)庫發(fā)送insert、update或delete語句
- execute(String sql):用于向數(shù)據(jù)庫發(fā)送任意sql語句
- addBatch(String sql) :把多條sql語句放到一個批處理中。
- executeBatch():向數(shù)據(jù)庫發(fā)送一批sql語句執(zhí)行。
ResultSet
jdbc程序中的ResultSet用于代表Sql語句的執(zhí)行結(jié)果。Resultset封裝執(zhí)行結(jié)果時,采用的類似于表格的方式。ResultSet 對象維護(hù)了一個指向表格數(shù)據(jù)行的游標(biāo),初始的時候,游標(biāo)在第一行之前,調(diào)用ResultSet.next() 方法,可以使游標(biāo)指向具體的數(shù)據(jù)行,進(jìn)行調(diào)用方法獲取該行的數(shù)據(jù)。
ResultSet既然用于封裝執(zhí)行結(jié)果的,所以該對象提供的都是用于獲取數(shù)據(jù)的get方法:
獲取任意類型的數(shù)據(jù)
getObject(int index)
getObject(string columnName)
獲取指定類型的數(shù)據(jù),例如:
getString(int index)
getString(String columnName)
提問:數(shù)據(jù)庫中列的類型是varchar,獲取該列的數(shù)據(jù)調(diào)用什么方法?Int類型呢?bigInt類型呢?Boolean類型?
A:
常用數(shù)據(jù)類型轉(zhuǎn)換表
ResultSet還提供了對結(jié)果集進(jìn)行滾動的方法:
- next():移動到下一行
- Previous():移動到前一行
- absolute(int row):移動到指定行
- beforeFirst():移動resultSet的最前面。
- afterLast() :移動到resultSet的最后面
釋放資源
Jdbc程序運行完后,切記要釋放程序在運行過程中,創(chuàng)建的那些與數(shù)據(jù)庫進(jìn)行交互的對象,這些對象通常是ResultSet, Statement和Connection對象。
特別是Connection對象,它是非常稀有的資源,用完后必須馬上釋放,如果Connection不能及時、正確的關(guān)閉,極易導(dǎo)致系統(tǒng)宕機(jī)。Connection的使用原則是盡量晚創(chuàng)建,盡量早的釋放。
為確保資源釋放代碼能運行,資源釋放代碼也一定要放在finally語句中。
使用JDBC對數(shù)據(jù)庫進(jìn)行CRUD
Jdbc中的statement對象用于向數(shù)據(jù)庫發(fā)送SQL語句,想完成對數(shù)據(jù)庫的增刪改查,只需要通過這個對象向數(shù)據(jù)庫發(fā)送增刪改查語句即可。
Statement對象的executeUpdate方法,用于向數(shù)據(jù)庫發(fā)送增、刪、改的sql語句,executeUpdate執(zhí)行完后,將會返回一個整數(shù)(即增刪改語句導(dǎo)致了數(shù)據(jù)庫幾行數(shù)據(jù)發(fā)生了變化)。
Statement.executeQuery方法用于向數(shù)據(jù)庫發(fā)送查詢語句,executeQuery方法返回代表查詢結(jié)果的ResultSet對象。
CRUD操作-create
使用executeUpdate(String sql)方法完成數(shù)據(jù)添加操作,示例操作:
Statement st = conn.createStatement();String sql = "insert into user(….) values(…..) "; int num = st.executeUpdate(sql);if(num>0){System.out.println("插入成功!!!");}CRUD操作-updata
使用executeUpdate(String sql)方法完成數(shù)據(jù)修改操作,示例操作:
Statement st = conn.createStatement();String sql = “update user set name=‘’ where name=‘’"; int num = st.executeUpdate(sql);if(num>0){System.out.println(“修改成功!!!");}CRUD操作-delete
使用executeUpdate(String sql)方法完成數(shù)據(jù)刪除操作,示例操作:
Statement st = conn.createStatement();String sql = “delete from user where id=1; int num = st.executeUpdate(sql);if(num>0){System.out.println(“刪除成功!!!");}CRUD操作-read
使用executeQuery(String sql)方法完成數(shù)據(jù)查詢操作,示例操作:
Statement st = conn.createStatement();String sql = “select * from user where id=1; ResultSet rs = st.executeUpdate(sql);while(rs.next()){//根據(jù)獲取列的數(shù)據(jù)類型,分別調(diào)用rs的相應(yīng)方法//映射到j(luò)ava對象中}sql防注入
SQL 注入是用戶利用某些系統(tǒng)沒有對輸入數(shù)據(jù)進(jìn)行充分的檢查,從而進(jìn)行惡意破壞的行為。
1、statement存在sql注入攻擊問題,例如登陸用戶名采用’ or 1=1 or name=‘
2、對于防范 SQL 注入,可以采用PreparedStatement取代Statement。
PreparedStatement
PreperedStatement是Statement的孩子,它的實例對象可以通過調(diào)用Connection.preparedStatement()方法獲得,相對于Statement對象而言:
- PreperedStatement可以避免SQL注入的問題。
- Statement會使數(shù)據(jù)庫頻繁編譯SQL,可能造成數(shù)據(jù)庫緩沖區(qū)溢出。PreparedStatement 可對SQL進(jìn)行預(yù)編譯,從而提高數(shù)據(jù)庫的執(zhí)行效率。
- 并且PreperedStatement對于sql中的參數(shù),允許使用占位符的形式進(jìn)行替換,簡化sql語句的編寫。
總結(jié)
以上是生活随笔為你收集整理的方立勋_30天掌握JavaWeb_JDBC、SQL防注入(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 方立勋_30天掌握JavaWeb_数据库
- 下一篇: 方立勋_30天掌握JavaWeb_jdb