JDBC
一、JDBC概述
為什么要使用JDBC?
JDBC:Java DataBase Connectivity,是SUN公司提供的一套操作數據庫的標准規范(技術)。
JDBC與數據庫驅動的關系:接口與實現的關系。
JDBC規范(掌握四個核心對象):
DriverManager類:用於注冊驅動(創建連接對象)。 java.sql.DriverManager;
Connection接口:表示與數據庫創建的連接 。java.sql.Connection;
Statement接口:操作數據庫sql語句的對象,並返回相應結果的對象。 java.sql.Statement;
preparedStatement接口:預編譯對象,是Statement對象的子類。用於解決sql的注入問題。實際用的是這個類。java.sql.PreparedStatement;
ResultSet接口:結果集或一張虛擬表(客戶端存表數據的對象)。 java.sql.ResultSet;
二、開發一個JDBC程序
使用JDBC技術,通過java代碼實現查詢數據庫中的數據並顯示在java的控制台中。
1、先創建數據庫表,並向表中添加測試數據。
create database stu; use stu; create table user ( id int primary key auto_increment, username varchar(40), password varchar(40), ) character set utf8 collate utf8_general_ci; insert into user(username, password) values('zs', '123456'); insert into user(username, password) values('lisi', '123456'); insert into user(username, password) values('wangwu', '123456');
2、創建Java Project項目,添加數據庫驅動(xxx.jar)。
3、實現JDBC操作。
4.源碼示例
package com.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class JDBCUtils { public static Connection connection; private static String url ="jdbc:mysql://localhost:3306/stu?serverTimezone=GMT%2B8"; private static String user ="root"; private static String pwd = "root"; static{ try { Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection(url,user,pwd); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } public static Connection getConnection(){ return connection; } }
package com.pojo; public class User { private int id; private String username; private String password; public User() { } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; } public User(int id, String username, String password) { this.id = id; this.username = username; this.password = password; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
package com.dao; import com.jdbc.JDBCUtils; import com.pojo.User; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; public class UserDao { private Connection conn = JDBCUtils.getConnection(); private PreparedStatement ps; /** * 添加用戶 * @param user 要添加的用戶對象 * @throws SQLException */ public int add(User user) throws SQLException { String sql = "insert into user(username,password) values(?,?)"; ps = conn.prepareStatement(sql); ps.setObject(1,user.getUsername()); ps.setObject(2,user.getPassword()); return ps.executeUpdate(); } /** * 刪除 * @param id * @return * @throws SQLException */ public int delete(int id) throws SQLException { String sql = "delete from user where id=?"; ps = conn.prepareStatement(sql); ps.setObject(1,id); return ps.executeUpdate(); } /** * 修改 * @param user * @return * @throws SQLException */ public int update(User user) throws SQLException { String sql = "update user set username=? , password=? where id=?"; ps = conn.prepareStatement(sql); ps.setObject(1,user.getUsername()); ps.setObject(2,user.getPassword()); ps.setObject(3,user.getId()); return ps.executeUpdate(); } /** * 查詢所有 * @return * @throws SQLException */ public List<User> findAll() throws SQLException { String sql = "select * from user"; ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery(); List<User> list = new ArrayList<User>(); while(rs.next()){ int id = rs.getInt("id"); String username = rs.getString("username"); String password = rs.getString("password"); User u = new User(id,username,password); list.add(u); } return list; } /** * 根據ID查詢 * @param id * @return * @throws SQLException */ public User findByID(int id) throws SQLException { String sql = "select * from user where id=?"; ps = conn.prepareStatement(sql); ps.setInt(1,id); ResultSet rs = ps.executeQuery(); User u = null; while(rs.next()){ int uid = rs.getInt("id"); String username = rs.getString("username"); String password = rs.getString("password"); u = new User(uid,username,password); } return u; } /** * 登錄的方法 */ public User login(User user) throws SQLException { String sql = "select * from user where username=? and password=?"; ps = conn.prepareStatement(sql); ps.setObject(1,user.getUsername()); ps.setObject(2,user.getPassword()); ResultSet rs = ps.executeQuery(); User u = null; while(rs.next()){ int uid = rs.getInt("id"); String username = rs.getString("username"); String password = rs.getString("password"); u = new User(uid,username,password); } return u; } }
package com.service; import com.dao.UserDao; import com.pojo.User; import java.sql.SQLException; import java.util.List; public class UserService { private UserDao userDao = new UserDao(); /** * 添加用戶 * @param user 要添加的用戶對象 * @throws SQLException */ public int add(User user) throws SQLException { return userDao.add(user); } /** * 刪除 * @param id * @return * @throws SQLException */ public int delete(int id) throws SQLException { return userDao.delete(id); } /** * 修改 * @param user * @return * @throws SQLException */ public int update(User user) throws SQLException { return userDao.update(user); } /** * 查詢所有 * @return * @throws SQLException */ public List<User> findAll() throws SQLException { return userDao.findAll(); } /** * 根據ID查詢 * @param id * @return * @throws SQLException */ public User findByID(int id) throws SQLException { return userDao.findByID(id); } /** * 登錄 */ public User login(User user) throws SQLException { return userDao.login(user); } }
package com.jdbc; import com.pojo.User; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; public class Demo { public static void main(String[] args) throws Exception { //1.動態加載驅動 // Class.forName("com.mysql.cj.jdbc.Driver"); // // //2.獲取連接對象 // String url = "jdbc:mysql://localhost:3306/stu?serverTimezone=GMT%2B8"; // String user = "root"; // String pwd = "123456"; // Connection conn = DriverManager.getConnection(url, user, pwd); //3.使用jdbc規范實現CRUD //String sql = "insert into user(username,password) value(?,?)"; // String sql ="select * from user "; // // 創建一個 PreparedStatement 對象來將參數化的 SQL 語句發送到數據庫。 // PreparedStatement ps = conn.prepareStatement(sql); // //// ps.setString(1,"曉峰");//將指定參數設置為給定 Java String 值 //// ps.setString(2,"xf"); // //// ps.setObject(1,"軍帥");//使用給定對象設置指定參數的值。 //// ps.setObject(2,"js"); // ////如果第一個結果是 ResultSet 對象,則返回 true;如果第一個結果是更新計數或者沒有結果,則返回 false //// boolean res = ps.execute(); //// System.out.println(res); // // //(1) SQL 數據操作語言 (DML) 語句的行數 (2) 對於無返回內容的 SQL 語句,返回 0 //// int rel = ps.executeUpdate(); //// System.out.println(rel);//1 // // //ResultSet 對象具有指向其當前數據行的光標。最初,光標被置於第一行之前。next 方法將光標移動到下一行; // //在此 PreparedStatement 對象中執行 SQL 查詢,並返回該查詢生成的 ResultSet 對象。 // ResultSet rs = ps.executeQuery(); //// rs.next(); //// rs.next(); //// rs.next(); //// rs.next(); //// boolean rel = rs.next(); //// System.out.println(rel); // // List<User> list = new ArrayList<User>(); // // while (rs.next()){ // // int id = rs.getInt(1); // String username = rs.getString(2); // String password = rs.getString(3); // //// User u = new User(id,username,password); //// list.add(u); // User u = (User) Class.forName("com.pojo.User").newInstance(); // list.add(u); // // } // // System.out.println(list); } }
三、JDBC常用的類和接口詳解
1、java.sql.Drivermanager類:注冊驅動、建立連接
a、注冊驅動
DriverManager.deregisterDriver(new com.mysql.jdbc.Driver()); // 不建議使用
原因有2個:
> 導致驅動被注冊2次。
> 強烈依賴數據庫的驅動jar包 (若把Build Path給Remove掉,代碼就會報錯,就能看到效果)
解決辦法:
Class.forName("com.mysql.jdbc.Driver"); // 把Build Path給Remove掉,代碼也不會報錯了
用到反射機制的知識:靠類的全路徑來加載,解耦合了,不依賴數據庫的驅動jar包了,以后會把上面的字符串放在配置文件里,更靈活了,便於后期維護。
b、與數據庫建立連接
DriverManager類的靜態方法:
public static Connection getConnection(String url, String user, String password) 試圖建立到給定數據庫 URL 的連接
DriverManager.getConnection("jdbc:mysql://localhost:3306/stu?serverTimezone=GMT%2B8", "root", "root");
URL:是SUN公司與數據庫廠商之間的一種協議。
jdbc:mysql://localhost:3306/stu?serverTimezone=GMT%2B8
協議 子協議 IP 數據庫端口號 數據庫名稱
mysql:jdbc:mysql://localhost:3306/day14 或者
jdbc:mysql:///day14(默認連接本機的數據庫)
oracle:jdbc:oracle:thin:@localhost:1521:sid 瘦客戶端,比如:玩頁游協議
子協議 子子協議 IP 數據庫端口號 數據庫名稱
oracle:jdbc:oracle:thick:@localhost:1521:sid 胖客戶端,比如:LOL、魔獸世界(需要下載軟件)
與數據庫建立連接的方式
第一種:
// public static Connection getConnection(String url, String user, String password)
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/stu?serverTimezone=GMT%2B8", "root", "root");
第二種:
// public static Connection getConnection(String url, Properties info)
Properties info = new Properties(); // 要參考數據庫文檔
info.setProperty("username", "root");
info.setProperty("password", "root");
Connection conn = DriverManager.getConnection("jjdbc:mysql://localhost:3306/stu?serverTimezone=GMT%2B8", info);
第三種:
// public static Connection getConnection(String url)
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/stu?serverTimezone=GMT%2B8?username=root&password=root");
// 模擬表單form提交的兩種方式:GET和POST
實際開發常用第一種。
擴展內容:演示3個案例的話需要寫3個main方法,所以有了快速測試的方法 ==> junit技術
}
-------------------------------------------
2、java.sql.Connection接口:一個連接(與數據庫連接的一座橋梁)
該接口的實現在數據庫驅動jar包中。所有與數據庫交互都是基於連接對象的。
3、java.sql.Statement接口:操作sql語句,並返回相應結果的對象(小貨車)
該接口的實現在數據庫驅動jar包中。用於執行靜態 SQL 語句並返回它所生成結果的對象。
4、java.sql.ResultSet接口:結果集(客戶端存表數據的對象)
a、封裝結果集
提供一個游標,默認游標指向結果集第一行之前(即表頭)。
調用一次boolean next(),游標向下移動一行。
提供一些getXxx()方法。
-------------------------------------------
將結果集中的數據封裝到javaBean類中,javaBean就是一普通java類,該類中只有private類型的成員變量、無參構造方法和getter/setter方法。
java的數據類型與數據庫中的類型的關系:
JAVA DB
byte tityint 1字節
short smallint 2字節
int int 4字節
long bigint 8字節
float float
double double
String char 或者 varchar
Date date
-------------------------------------------
ResultSet結果集封裝數據的方法:
Object getObject(int columnIndex) 根據序號取值,索引從1開始
Object getObject(String ColomnName) 根據列名取值
boolean next() 將光標從當前位置向下移動一行
int getInt(int colIndex) 以int 形式獲取ResultSet結果集當前行指定列號值
int getInt(String colLabel) 以int 形式獲取ResultSet結果集當前行指定列名值
float getFloat(int colIndex) 以float 形式獲取ResultSet結果集當前行指定列號值
float getFloat(String colLabel) 以float形式獲取ResultSet結果集當前行指定列名值
String getString(int colIndex) 以String 形式獲取ResultSet結果集當前行指定列號值
String getString(String colLabel) 以String 形式獲取ResultSet結果集當前行指定列名值
Date getDate(int columnIndex) 以Date 形式獲取ResultSet結果集當前行指定列號值
Date getDate(String columnName) 以Date 形式獲取ResultSet結果集當前行指定列名
void close() 關閉ResultSet 對象
package com.itheima.entitydemo; 2 3 import java.util.Date; 4 5 /* 6 * 一般實體類的類名和數據庫的表名一致。(注意:若數據庫的表名單詞帶s的,則實體類名就去掉s) 7 * 實體類的成員變量名和數據庫表中的列名一致。(這是一個約定,網站開發的時候會提到) 8 */ 9 public class User { 10 private int id; 11 private String name; 12 private String password; 13 private String email; 14 private Date birthday; 15 16 public int getId() { 17 return id; 18 } 19 20 public void setId(int id) { 21 this.id = id; 22 } 23 24 public String getName() { 25 return name; 26 } 27 28 public void setName(String name) { 29 this.name = name; 30 } 31 32 public String getPassword() { 33 return password; 34 } 35 36 public void setPassword(String password) { 37 this.password = password; 38 } 39 40 public String getEmail() { 41 return email; 42 } 43 44 public void setEmail(String email) { 45 this.email = email; 46 } 47 48 public Date getBirthday() { 49 return birthday; 50 } 51 52 public void setBirthday(Date birthday) { 53 this.birthday = birthday; 54 } 55 56 @Override 57 public String toString() { 58 return "User [id=" + id + ", name=" + name + ", password=" + password + ", email=" + email + ", birthday=" 59 + birthday + "]"; 60 } 61 62 }
package com.itheima.entitydemo; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.ResultSet; 6 import java.sql.Statement; 7 import java.util.ArrayList; 8 import java.util.List; 9 10 import org.junit.Test; 11 12 /* 13 * 服務器端叫法:insert、delete、update、select (增刪改查) 14 * 客戶端的叫法:create、read、update、delete (CRUD)(增刪改查) 15 */ 16 public class TestCRUD { 17 18 @Test 19 public void testSelect() throws Exception { 20 // 加載驅動 21 Class.forName("com.mysql.jdbc.Driver"); 22 23 // 獲取連接對象Connection(注意:能用接口的就不用實現類,多態=>對象上傳=>向上轉型=>里氏替換,利於后期代碼擴展) 24 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day06", "root", "root"); 25 26 // 獲取執行sql語句的對象Statement 27 Statement stmt = conn.createStatement(); 28 29 // 執行sql的查詢語句,並返回結果 30 ResultSet rs = stmt.executeQuery("select * from users"); 31 32 // 把取到的數據裝到集合中 33 List<User> list = new ArrayList<User>(); 34 35 // 處理結果 36 User u = null; 37 while (rs.next()) { 38 u = new User(); 39 u.setId(rs.getInt("id")); 40 u.setName(rs.getString("name")); 41 u.setPassword(rs.getString("password")); 42 u.setEmail(rs.getString("email")); 43 u.setBirthday(rs.getDate("birthday")); 44 list.add(u); // 把對象添加到集合中去 45 } 46 47 // 遍歷集合(增強for) 48 for (User user : list) { 49 System.out.println(user); 50 } 51 52 // 關閉資源 53 rs.close(); 54 stmt.close(); 55 conn.close(); 56 } 57 58 @Test 59 public void testInsert() throws Exception { 60 // 加載驅動 61 Class.forName("com.mysql.jdbc.Driver"); 62 63 // 獲取連接對象Connection(注意;能用接口的就不用實現類,多態=>對象上傳=>向上轉型=>里氏替換,利於后期代碼擴展) 64 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day06", "root", "root"); 65 66 // 獲取執行sql語句的對象Statement 67 Statement stmt = conn.createStatement(); 68 69 // 執行sql的插入語句,並返回結果 70 int i = stmt.executeUpdate("INSERT INTO users VALUES (4, 'tom', '123','tom@163.com', '2015-09-28')"); 71 72 // 處理返回的結果 73 if (i > 0) { 74 System.out.println("success"); 75 } 76 77 // 關閉資源 78 stmt.close(); 79 conn.close(); 80 } 81 82 @Test 83 public void testUpdate() throws Exception { 84 // 加載驅動 85 Class.forName("com.mysql.jdbc.Driver"); 86 87 // 獲取連接對象Connection(注意;能用接口的就不用實現類,多態=>對象上傳=>向上轉型=>里氏替換,利於后期代碼擴展) 88 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day06", "root", "root"); 89 90 // 獲取執行sql語句的對象Statement 91 Statement stmt = conn.createStatement(); 92 93 // 執行sql的更新語句,並返回結果 94 int i = stmt.executeUpdate("UPDATE users SET NAME = 'jerry', PASSWORD = '333', email = 'jerry@163.com' WHERE id = 4"); 95 96 // 處理返回的結果 97 if (i > 0) { 98 System.out.println("success"); 99 } 100 101 // 關閉資源 102 stmt.close(); 103 conn.close(); 104 } 105 106 @Test 107 public void testDelete() throws Exception { 108 // 加載驅動 109 Class.forName("com.mysql.jdbc.Driver"); 110 111 // 獲取連接對象Connection(注意;能用接口的就不用實現類,多態=>對象上傳=>向上轉型=>里氏替換,利於后期代碼擴展) 112 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day06", "root", "root"); 113 114 // 獲取執行sql語句的對象Statement 115 Statement stmt = conn.createStatement(); 116 117 // 執行sql的刪除語句,並返回結果 118 int i = stmt.executeUpdate("DELETE FROM users WHERE id = 4"); 119 120 // 處理返回的結果 121 if (i > 0) { 122 System.out.println("success"); 123 } 124 125 // 關閉資源 126 stmt.close(); 127 conn.close(); 128 } 129 130 }
b、可移動游標的方法(現在很少用了,只有next()還常用)
5、釋放資源
資源有限,要正確關閉。
原則:在使用對象之前,先判斷對象是否為空。
package com.itheima.jdbcdemo; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.sql.Statement; 8 9 /* 10 * 使用JDBC技術,通過java代碼實現查詢數據庫中的數據並顯示在java的控制台中。 11 * 12 * 演示正確釋放資源。 13 * 14 * 把某一部分代碼加上try...catch...的快捷鍵:Alt + Shift + Z 15 */ 16 public class Demo4 { 17 18 public static void main(String[] args) { 19 // 獲取連接對象Connection 20 Connection conn = null; 21 // 獲取執行sql語句的對象Statement 22 Statement stmt = null; 23 // 執行sql查詢語句,並返回結果 24 ResultSet rs = null; 25 try { 26 // 加載驅動 27 Class.forName("com.mysql.jdbc.Driver"); 28 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day06", "root", "root"); 29 stmt = conn.createStatement(); 30 rs = stmt.executeQuery("select * from users"); 31 // 處理返回的結果 32 while (rs.next()) { 33 // 根據列名取值,順序自己定,更靈活 34 System.out.println(rs.getObject("id")); 35 System.out.println(rs.getObject("name")); 36 System.out.println(rs.getObject("password")); 37 System.out.println(rs.getObject("email")); 38 System.out.println(rs.getObject("birthday")); 39 } 40 } catch (Exception e) { 41 e.printStackTrace(); 42 } finally { 43 // 關閉資源 44 if (rs != null) { 45 try { 46 rs.close(); 47 } catch (SQLException e) { 48 e.printStackTrace(); 49 } 50 } 51 if (stmt != null) { 52 try { 53 stmt.close(); 54 } catch (SQLException e) { 55 e.printStackTrace(); 56 } 57 } 58 if (conn != null) { 59 try { 60 conn.close(); 61 } catch (SQLException e) { 62 e.printStackTrace(); 63 } 64 } 65 } 66 } 67 }
四、使用JDBC實現CRUD操作
五、實現一個用戶登錄的功能
六、解決SQL注入問題:preparedStatement(預編譯語句)
preparedStatement(接口):預編譯對象,是Statement(接口)對象的子類。
特點:(安全高效,防止惡義SQL語法)
1、性能要高;PreparedStatement 實例包含已編譯的 SQL 語句,所以其執行速度要快於 Statement 對象。
2、會把sql語句先編譯。
3、sql語句中的參數會發生變化,過濾掉用戶輸入的關鍵字。
package com.itheima.service; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import com.itheima.entity.User; import com.itheima.util.DBUtils; public class DoLogin { /** * 根據姓名和密碼查詢用戶信息 * @param name * @param password * @return User */ public User findUser(String name, String password) { Connection conn = null; // Statement stmt = null; PreparedStatement stmt = null; ResultSet rs = null; User u = null; try { conn = DBUtils.getConnection(); // stmt = conn.createStatement(); // String sql = "select * from users where name = '" + name + "' and password = '" + password + "'"; // rs = stmt.executeQuery(sql); String sql = "select * from users where name = ? and password = ?"; stmt = conn.prepareStatement(sql); stmt.setString(1, name); // 給?賦值 stmt.setString(2, password); // 給?賦值 rs = stmt.executeQuery(); if (rs.next()) { u = new User(); u.setId(rs.getInt("id")); u.setName(rs.getString("name")); u.setPassword(rs.getString("password")); u.setEmail(rs.getString("email")); u.setBirthday(rs.getDate("birthday")); } } catch (Exception e) { e.printStackTrace(); } finally { DBUtils.closeAll(rs, stmt, conn); } return u; } }