1.Dbs封裝
每一個數據庫都對應著一個dao 每個dao勢必存在公共部分 我們需要將公共部分抽取出來 封裝成一個工具類 保留個性化代碼即可
- 我們的工具類一般命名為xxxs 比如Strings 就是字符串相關的工具類 而工具類 我們將其放置于util包中
- 我們以是否有<T>區分泛型方法和非泛型方法
- 建議先創建語句、傳遞參數之后 在執行語句 即不要著急將ResultSet放置在try-with-resources中
- 你可以通過live template快速生成一些代碼模板 比如itar可以幫助我們快速生成遍歷數組元素的模板 我們也可以自定義動態模板
- dao中需要傳遞一段個性化代碼(即ResultSet->bean)給dbs 要求dbs存在一個接口(用于將數據庫中每一行ResultSet映射為bean)用于接收代碼 同時可以設置一個row 用于顯示當前所在的行號(數據庫中的行號一般從0開始計數 列號一般從1開始計數)
- 既然存在dbs 那么以前的三個常量URL、USERNAME、PASSWORD就可以作為公共部分存放在dbs中
- 代碼實現
- Dbs.java
package com.axihh.util;import com.axihh.Constants;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 Dbs {// 常量public static final String URL = "jdbc:mysql://localhost:3306/crm?serverTimezone=UTC";public static final String USERNAME = "root";public static final String PASSWORD = "root";// 定義一個方法 用于存放數據庫保存的公共代碼public static boolean update(String sql, Object ...args) {try {// 注冊驅動Class.forName("com.mysql.cj.jdbc.Driver");try(Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);PreparedStatement pstmt = conn.prepareStatement(sql)) {// 設置參數for (int i = 0; i < args.length; i++) {pstmt.setObject(i + 1, args[i]);}// 執行sql語句return pstmt.executeUpdate() > 0;}}catch(Exception e) {e.printStackTrace();// 執行到此 說明沒有任何行受到影響return false;}}// 定義一個方法 用于存放用戶信息集合獲取的公共代碼public static <T> List<T> list(String sql, RowMapper<T> rm, Object ...args) {try {// 注冊驅動Class.forName("com.mysql.cj.jdbc.Driver");try(Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);PreparedStatement pstmt = conn.prepareStatement(sql)) {// 建議不要將resultset寫入try-with-resources中 因為我們要保持順序的先后性 先創建語句 在執行語句 保持這樣的順序// 設置參數for (int i = 0; i < args.length; i++) {pstmt.setObject(i + 1, args[i]);}// 執行sql語句ResultSet rs = pstmt.executeQuery();List<T> array = new ArrayList<>();// resultset映射beanfor(int row = 0; rs.next(); row++) {array.add(rm.map(rs, row));}return array;}}catch(Exception e) {e.printStackTrace();// 如果出現異常 證明返回值為空return null;}}// 定義一個接口 用于接收resultset映射bean的代碼 具體就是數據庫中的每一行對應的resultset映射為beanpublic interface RowMapper<T> {public T map(ResultSet rs, int row) throws Exception;} }
- CustomerDao.java
package com.axihh.dao;import com.axihh.bean.Customer; import com.axihh.util.Dbs;import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import com.axihh.util.Dbs.RowMapper; import java.util.List;public class CustomerDao {public boolean save(Customer customer){String sql = "INSERT INTO customer(name, age, height) VALUES (?, ?, ?)";return Dbs.update(sql, customer.getName(), customer.getAge(), customer.getHeight());}public List<Customer> list() {String sql = "SELECT id, name, age, height FROM customer";return Dbs.list(sql, (rs, row) -> {Customer customer = new Customer();customer.setId(rs.getInt("id"));customer.setName(rs.getString("name"));customer.setAge(rs.getInt("age"));customer.setHeight(rs.getDouble("height"));System.out.println(row + "_" + customer);return customer;});} }
2.缺陷
雖然 我們將dao的公共代碼抽取到dbs中以期簡化了dao 但是仍然存在著一些問題 常量(比如PASSWORD)動態修改時 需要打開源碼修改 再重新編譯打包 顯然很麻煩
- 解決方案:利用配置文件幫助我們將一些需要動態修改的值放入配置文件中 以代替在寫死的Java代碼
1.配置文件
- 常見的配置文件
- properties:比較單一 適合量小、簡單的配置
- key、value的分隔符是=、:
- 建議分隔符左右不要留空格
- #、!開頭是單行注釋
- 可以用\來連接多行內容(內容太多一行中無法顯示)
- xml:比較靈活 適合量大、復雜的配置
- properties:比較單一 適合量小、簡單的配置
2.properties解決方案
- 首先新建一個properties文件 具體就是在src下右鍵選擇resource bundle
- 編寫properties內容 以鍵值對的形式定義三個動態修改的變量(不包含任何的修飾符和字符串雙引號 僅僅只是鍵和值)
- 讀取properties內容 根據鍵找值的方式獲取相關值寫入Dbs中對應的變量 由于變量支持動態修改 所以不在通過final修飾 并且變量不再是常量 所以無需大寫
- 我們將讀取properties 寫入三個動態修改的變量代碼內置于靜態初始化塊中 表明程序運行過程中僅需進行一次賦值
- 調用class獲取當前類的字節碼文件 然后通過getClassLoader獲取加載該字節碼文件的類加載器 然后通過getResourceAsStream獲取資源文件 并且通過字節輸入流讀取該資源文件
- 定義Properties對象 將資源文件加載到該對象中
- 通過鍵找值的方式 完成對Dbs文件中三個動態修改變量的賦值操作
- 注意 其中InputStream屬于資源 需要關閉以釋放 通過try-with-resources將其自動關閉
package com.axihh.util;import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;public class Dbs {// 常量public static String url;public static String username;public static String password;static {try(// 通過當前類獲取字節碼文件 然后通過字節碼文件獲取類加載器 然后通過類加載器獲取資源文件 并且通過字節輸入流讀取該資源文件InputStream is = Dbs.class.getClassLoader().getResourceAsStream("db.properties")) {// 創建properties對象 然后將字節輸入流讀取的內容加載進該對象中Properties properties = new Properties();properties.load(is);// 然后通過鍵找值的方式獲取指定變量對應的值url = properties.getProperty("url");username = properties.getProperty("username");password = properties.getProperty("password");} catch (IOException e) {e.printStackTrace();}}// 定義一個方法 用于存放數據庫保存的公共代碼public static boolean update(String sql, Object ...args) {try {// 注冊驅動Class.forName("com.mysql.cj.jdbc.Driver");try(Connection conn = DriverManager.getConnection(url, username, password);PreparedStatement pstmt = conn.prepareStatement(sql)) {// 設置參數for (int i = 0; i < args.length; i++) {pstmt.setObject(i + 1, args[i]);}// 執行sql語句return pstmt.executeUpdate() > 0;}}catch(Exception e) {e.printStackTrace();// 執行到此 說明沒有任何行受到影響return false;}}// 定義一個方法 用于存放用戶信息集合獲取的公共代碼public static <T> List<T> list(String sql, RowMapper<T> rm, Object ...args) {try {// 注冊驅動Class.forName("com.mysql.cj.jdbc.Driver");try(Connection conn = DriverManager.getConnection(url, username, password);PreparedStatement pstmt = conn.prepareStatement(sql)) {// 建議不要將resultset寫入try-with-resources中 因為我們要保持順序的先后性 先創建語句 在執行語句 保持這樣的順序// 設置參數for (int i = 0; i < args.length; i++) {pstmt.setObject(i + 1, args[i]);}// 執行sql語句ResultSet rs = pstmt.executeQuery();List<T> array = new ArrayList<>();// resultset映射beanfor(int row = 0; rs.next(); row++) {array.add(rm.map(rs, row));}return array;}}catch(Exception e) {e.printStackTrace();// 如果出現異常 證明返回值為空return null;}}// 定義一個接口 用于接收resultset映射bean的代碼 具體就是數據庫中的每一行對應的resultset映射為beanpublic interface RowMapper<T> {public T map(ResultSet rs, int row) throws Exception;}
}
- 細節
我們將properties文件創建在src中 目的在于經過編譯打包這一系列操作之后 該資源文件被內置于classes目錄中 經過查詢 可知該目錄中正好是存放src下字節碼文件的位置 類加載器將字節碼文件加載到JVM中 前提肯定是知道classes目錄的位置 那么我們就可以利用這一點通過類加載器找到classes中的properties文件