MySQL資料庫學習筆記(十一)----DAO設計模式實現資料庫的增刪改查(進一步封裝JDBC工具類)
系列文章並非本人原創。
在這裡我想討論的一個問題是:在PersonDaoImpl這個實現類中,我們可以看到
public void add(Person p) throws SQLException { 26 Connection conn = null; 27 PreparedStatement ps = null;
@Override 48 public void update(Person p) throws SQLException { 49 Connection conn = null; 50 PreparedStatement ps = null;
這幾個增刪改查的方法中,Connection conn,PrepareStatement ps這倆個變數都寫成了方法中的區域性的變數,而沒有在這個類中宣告為全域性的變數,為什麼要這樣做呢?其實這樣做是有原因的。因為我們在每次執行完增刪改查的任何一個方法後,我們都會去關閉相關的流。如果是這樣的話,那麼就會影響我們的接下來的其它的操作,因此我們需要將這些變數都寫成方法中的區域性的變數。只有這樣的話,我們才能更好地完成所有的操作。這是要注意的。
【正文】
一、DAO模式簡介
DAO即Data Access Object,資料訪問介面。資料訪問:故名思義就是與資料庫打交道。夾在業務邏輯與資料庫資源中間。
DAO模式實際上是兩個模式的組合,即Data Accessor (資料訪問者)模式和 Active Domain Object(領域物件)模式。Data Accessor 模式實現了資料訪問和業務邏輯的分離;Active Domain Object 模式實現了業務資料的物件化封裝。
需要注意的是,DAO設計模式是Java EE中的設計模式,而非Java SE中的23種設計模式。
二、實現DAO模式
一個典型的DAO實現有下列幾個元件:
- 一個DAO介面;
- 一個實現DAO介面的具體類;
- 資料傳遞物件(DTO):有些時候叫做值物件(VO)或領域模型(domain)
這種實現模式就是一個套路,記熟就好了。不過在這之前,如果有不明白的地方,還是要回顧一下之前幾篇博文中的知識:PreparedStatement介面重構增刪改查、封裝JDBC工具類。好了,下面直接上程式碼。
三、程式碼實現
我們一下面的這張資料表為例:
新建Java工程檔案DaoTest01,最終的工程檔案結構如下:
- DBUtils:初步封裝的JDBC工具類;
- db-config.properties:屬性檔案,方便修改配置資訊;
- Person類就是領域模型,表示是對它(資料庫表)進行增刪改查。
- PersonDao介面:專門對Person類進行操作(例如增刪改查)的介面。注:這裡不直接寫操作類,是因為介面利於維護,可以在這裡寫上公共的程式碼。一個領域模型對應一個Dao介面。
- PeronDaoImpl類:實現上面的PeronDao介面
步驟如下:
注:第(1)、(2)步操作和上一篇博文是一模一樣的,這裡只是為了保證本篇文章的完整性,所以重新寫一下。
(1)先新建一個DBUtils工具類:(package com.util.db)
1 package com.util.db; 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 import java.util.ResourceBundle; 9 10 /** 11 * 資料庫操作工具類 12 * @author lamp 13 * 14 */ 15 public class DBUtils { 16 17 //資料庫連線地址 18 public static String URL; 19 //使用者名稱 20 public static String USERNAME; 21 //密碼 22 public static String PASSWORD; 23 //mysql的驅動類 24 public static String DRIVER; 25 26 private static ResourceBundle rb = ResourceBundle.getBundle("com.util.db.db-config"); 27 28 private DBUtils(){} 29 30 //使用靜態塊載入驅動程式 31 static{ 32 URL = rb.getString("jdbc.url"); 33 USERNAME = rb.getString("jdbc.username"); 34 PASSWORD = rb.getString("jdbc.password"); 35 DRIVER = rb.getString("jdbc.driver"); 36 try { 37 Class.forName(DRIVER); 38 } catch (ClassNotFoundException e) { 39 e.printStackTrace(); 40 } 41 } 42 //定義一個獲取資料庫連線的方法 43 public static Connection getConnection(){ 44 Connection conn = null; 45 try { 46 conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); 47 } catch (SQLException e) { 48 e.printStackTrace(); 49 System.out.println("獲取連線失敗"); 50 } 51 return conn; 52 } 53 54 /** 55 * 關閉資料庫連線 56 * @param rs 57 * @param stat 58 * @param conn 59 */ 60 public static void close(ResultSet rs,Statement stat,Connection conn){ 61 try { 62 if(rs!=null)rs.close(); 63 if(stat!=null)stat.close(); 64 if(conn!=null)conn.close(); 65 } catch (SQLException e) { 66 e.printStackTrace(); 67 } 68 } 69 70 }
注意:26行中,注意獲取屬性檔案的包名是否正確。稍後會定義這個屬性檔案。
28行:既然是工具類,一般不要例項化,此時可以採用單例設計模式,或者將構造方法私有化。
26行:很明顯可以看到,我們是將連線資料庫的URL、使用者名稱,密碼等資訊編寫在一個屬性檔案(jdbc.properties)中,稍後再來定義這個屬性檔案。
31行:為避免重複程式碼,使用靜態程式碼塊:只會在類載入的時候執行一次。
42行:定義一個獲取資料庫連線的方法
60行:關閉資料庫連線
(2)接下來新建一個屬性檔案,new-->file,命名為:db-config.properties,程式碼如下:
jdbc.url=jdbc:mysql://localhost:3306/jdbcdb jdbc.username=root jdbc.password=smyh jdbc.driver=com.mysql.jdbc.Driver
以後如果需要修改配置資訊,只需要在這裡改就行了。注意在上面的DBUtils類中是怎麼來呼叫這個配置資訊的。
緊接著新建檔案,定義好Person類:(package com.vae.domain)
1 package com.vae.domain; 2 3 public class Person { 4 private int id; 5 private String name; 6 private int age; 7 private String description; 8 public int getId() { 9 return id; 10 } 11 public void setId(int id) { 12 this.id = id; 13 } 14 public String getName() { 15 return name; 16 } 17 public void setName(String name) { 18 this.name = name; 19 } 20 public int getAge() { 21 return age; 22 } 23 public void setAge(int age) { 24 this.age = age; 25 } 26 public String getDescription() { 27 return description; 28 } 29 public void setDescription(String description) { 30 this.description = description; 31 } 32 public Person(int id, String name, int age, String description) { 33 super(); 34 this.id = id; 35 this.name = name; 36 this.age = age; 37 this.description = description; 38 } 39 public Person(String name, int age, String description) { 40 super(); 41 this.name = name; 42 this.age = age; 43 this.description = description; 44 } 45 public Person() { 46 super(); 47 // TODO Auto-generated constructor stub 48 } 49 @Override 50 public String toString() { 51 return "Person [id=" + id + ", name=" + name + ", age=" + age 52 + ", description=" + description + "]"; 53 } 54 55 56 }
這個Person類就是領域模型,表示是對它進行增刪改查。
(3)定義PersonDao介面:專門對Person類進行操作(例如增刪改查)的介面(package com.vae.dao)
package com.vae.dao; import java.sql.SQLException; import java.util.List; import com.vae.domain.Person; public interface PersonDao { //新增方法 public void add(Person p)throws SQLException; //更新方法 public void update(Person p)throws SQLException; //刪除方法 public void delete(int id)throws SQLException; //查詢方法 public Person findById(int id)throws SQLException; //查詢所有 public List<Person> findAll()throws SQLException; }
(4)定義PeronDaoImpl實現類 ,實現上面的PeronDao介面(package com.vae.dao.impl)
1 package com.vae.dao.impl; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.util.ArrayList; 8 import java.util.List; 9 10 import com.util.db.DBUtils; 11 import com.vae.dao.PersonDao; 12 import com.vae.domain.Person; 13 14 /** 15 * PersonDao的具體實現類 16 * @author lamp 17 * 18 */ 19 public class PersonDaoImpl implements PersonDao{ 20 21 /** 22 * 實現新增方法 23 */ 24 @Override 25 public void add(Person p) throws SQLException { 26 Connection conn = null; 27 PreparedStatement ps = null; 28 String sql = "insert into person(name,age,description)values(?,?,?)"; 29 try{ 30 conn = DBUtils.getConnection(); 31 ps = conn.prepareStatement(sql); 32 ps.setString(1, p.getName()); 33 ps.setInt(2, p.getAge()); 34 ps.setString(3, p.getDescription()); 35 ps.executeUpdate(); 36 }catch(SQLException e){ 37 e.printStackTrace(); 38 throw new SQLException("新增資料失敗"); 39 }finally{ 40 DBUtils.close(null, ps, conn); 41 } 42 } 43 44 /** 45 * 更新方法 46 */ 47 @Override 48 public void update(Person p) throws SQLException { 49 Connection conn = null; 50 PreparedStatement ps = null; 51 String sql = "update person set name=?,age=?,description=? where id=?"; 52 try{ 53 conn = DBUtils.getConnection(); 54 ps = conn.prepareStatement(sql); 55 ps.setString(1, p.getName()); 56 ps.setInt(2, p.getAge()); 57 ps.setString(3, p.getDescription()); 58 ps.setInt(4, p.getId()); 59 ps.executeUpdate(); 60 }catch(SQLException e){ 61 e.printStackTrace(); 62 throw new SQLException("更新資料失敗"); 63 }finally{ 64 DBUtils.close(null, ps, conn); 65 } 66 } 67 68 /** 69 * 刪除方法 70 */ 71 @Override 72 public void delete(int id) throws SQLException { 73 Connection conn = null; 74 PreparedStatement ps = null; 75 String sql = "delete from person where id=?"; 76 try{ 77 conn = DBUtils.getConnection(); 78 ps = conn.prepareStatement(sql); 79 ps.setInt(1,id); 80 ps.executeUpdate(); 81 }catch(SQLException e){ 82 e.printStackTrace(); 83 throw new SQLException(" 刪除資料失敗"); 84 }finally{ 85 DBUtils.close(null, ps, conn); 86 } 87 } 88 89 /** 90 * 根據ID查詢一個物件 91 */ 92 @Override 93 public Person findById(int id) throws SQLException { 94 Connection conn = null; 95 PreparedStatement ps = null; 96 ResultSet rs = null; 97 Person p = null; 98 String sql = "select name,age,description from person where id=?"; 99 try{ 100 conn = DBUtils.getConnection(); 101 ps = conn.prepareStatement(sql); 102 ps.setInt(1, id); 103 rs = ps.executeQuery(); 104 if(rs.next()){ 105 p = new Person(); 106 p.setId(id); 107 p.setName(rs.getString(1)); 108 p.setAge(rs.getInt(2)); 109 p.setDescription(rs.getString(3)); 110 } 111 }catch(SQLException e){ 112 e.printStackTrace(); 113 throw new SQLException("根據ID查詢資料失敗"); 114 }finally{ 115 DBUtils.close(rs, ps, conn); 116 } 117 return p; 118 } 119 120 /** 121 * 查詢所有資料 122 */ 123 @Override 124 public List<Person> findAll() throws SQLException { 125 Connection conn = null; 126 PreparedStatement ps = null; 127 ResultSet rs = null; 128 Person p = null; 129 List<Person> persons = new ArrayList<Person>(); 130 String sql = "select id,name,age,description from person"; 131 try{ 132 conn = DBUtils.getConnection(); 133 ps = conn.prepareStatement(sql); 134 rs = ps.executeQuery(); 135 while(rs.next()){ 136 p = new Person(); 137 p.setId(rs.getInt(1)); 138 p.setName(rs.getString(2)); 139 p.setAge(rs.getInt(3)); 140 p.setDescription(rs.getString(4)); 141 persons.add(p); 142 } 143 }catch(SQLException e){ 144 e.printStackTrace(); 145 throw new SQLException("查詢所有資料失敗"); 146 }finally{ 147 DBUtils.close(rs, ps, conn); 148 } 149 return persons; 150 } 151 152 }
我們在各自的增刪改查裡都丟擲了異常,如果出現異常,就會丟擲相應的錯誤資訊。
總結:這樣的話,我們就封裝好了JDBC的工具類。以後如果要用的話,可以直接在主方法裡呼叫這個類就行了。