jdbc(java database connection)是java內部集成的一套負責與database交互api。用以在java端執行增刪查找數據庫數據的操作。
這一套操作爲:
1.準備好數據庫
先準備操作的數據庫表爲選課表。表結構如下:
2.連接數據庫
- 爲項目引用Jar包
- 註冊Driver到DriverManager裏面
上面的代碼中有加載類Class.forName("com.mysql.jdbc.Driver")的描述。
查看com.mysql.jdbc.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()
}
}
可知,在加載com.mysql.jdbc.Driver的時候,已經生成了一個Driver實例,並將其註冊到DriverManager裏面了。
3.執行insert的sql語句
查詢Category表中數據爲:
可見insert的sql語句起了作用。
上述代碼中:
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/tmall?characterEncoding=UTF-8", "root", "admin");
用以連接tmall數據庫
Statement s = c.createStatement();
String sql = "insert into Category values(85," +"'ruff開發板')";
s.execute(sql);
用以執行SQL語句,向Category表中插入數據。
4.執行select語句,查詢表中數據
其中
while(mResultSet.next()){
System.out.println("Category表中id > 80的產品名稱爲:" + mResultSet.getString(2));
}
取出結果集中,所有的第二列數據。其中ResultSet.next()方法的解釋爲:
Moves the cursor forward one row from its current position. A ResultSet cursor is initially positioned before the first row; the first call to the method next makes the first row the current row; the second call makes the second row the current row, and so on.
到此我們就基本上實現了JDBC的基本功能,可以開始業務開發了。
5.使用PreparedStatement實現數據庫的操作。
public static void main(String args[]){
//new CategoryDao().executePreparedStatement();
//new CategoryDao().executeStatement("空調 or 1= 1");
//new CategoryDao().executePreparedStatement("空調 or 1= 1");
new CategoryDao().executePreparedStatement("空調");
}
private void executePreparedStatement(String userInput){
String sql = "select * from Category where name = ?";
try{
Connection c = getConnection();
PreparedStatement ps = c.prepareStatement(sql);
ps.setString(1, userInput);
ps.execute();
ResultSet rs = ps.getResultSet();
while(rs.next()){
System.out.printf("%d,%s",rs.getInt(1),rs.getString(2));
}
}catch(SQLException ex){
ex.printStackTrace();
}
}
上面的函數執行了從Category數據庫中查找id=80的項目並將name打印出來的功能,執行結果如下:
Console界面將name=空調 的條目打印出來了。
5.使用PreparedStatement與使用Statement的對比。
上述示例代碼中的PreparedStatement.setString(1, userInput);是值往sql第一個參數傳值,然而Statement中sql是通過字符串拼接得到的。下面的代碼對比表示了其區別:
public static void main(String args[]){
//new CategoryDao().executePreparedStatement();
new CategoryDao().executeStatement("75");
//new CategoryDao().executePreparedStatement("空調 or 1= 1");
//new CategoryDao().executePreparedStatement(75);
}
private void executeStatement(String userInput){
String sql = "select * from Category where id = " + userInput ;
try{
Connection c = getConnection();
//PreparedStatement ps = c.prepareStatement(sql);
//ps.setInt(1, 80);
//ps.execute();
Statement s = c.createStatement();
s.execute(sql);
ResultSet rs = s.getResultSet();
while(rs.next()){
System.out.printf("%d,%s",rs.getInt(1),rs.getString(2));
}
}catch(SQLException ex){
ex.printStackTrace();
}
}
得到的結果爲:
由此可見Statement的sql語句是採用字符串拼接得到的。
因此PreparedStatement可以在兩方面比Statement好:
(1)書寫很麻煩 。尤其是當sql字段涉及到char, varchar類型的數據時,轉義字符(\”)寫的人腦袋都要繞暈了。
(2)很重要的一點,PreparedStatement可以防止sql注入。
public static void main(String args[]){
//new CategoryDao().executePreparedStatement();
new CategoryDao().executeStatement("75 or 1 = 1");
//new CategoryDao().executePreparedStatement("空調 or 1= 1");
//new CategoryDao().executePreparedStatement(75);
}
private void executeStatement(String userInput){
String sql = "select * from Category where id = " + userInput ;
try{
Connection c = getConnection();
//PreparedStatement ps = c.prepareStatement(sql);
//ps.setInt(1, 80);
//ps.execute();
Statement s = c.createStatement();
s.execute(sql);
ResultSet rs = s.getResultSet();
while(rs.next()){
System.out.printf("%d,%s",rs.getInt(1),rs.getString(2));
}
}catch(SQLException ex){
ex.printStackTrace();
}
}
注意這行:
new CategoryDao().executeStatement("75 or 1 = 1");
執行結果爲:
結果得知,打印出了Category表中所有的信息,這就是sql注入導致的信息泄露了。而在PreparedStatement中就不會產生這個結果,因爲 “75 or 1 = 1”這個參數在Category表中是查不出來東西的。
(3)還有一點重要的區別是執行效率的區別
使用 PreparedStatement 最重要的一點好處是它擁有更佳的性能優勢,SQL語句會預編譯在數據庫系統中。執行計劃同樣會被緩存起來,它允許數據庫做參數化查詢。使用預處理語句比普通的查詢更快,因爲它做的工作更少(數據庫對SQL語句的分析,編譯,優化已經在第一次查詢前完成了)。爲了減少數據庫的負載,生產環境中德JDBC代碼你應該總是使用PreparedStatement 。值得注意的一點是:爲了獲得性能上的優勢,應該使用參數化sql查詢而不是字符串追加的方式。