簡單的DbUtils工具類【精細】

目錄

單條通用增刪改方法

? ? ? ? 1.創建maven項目,并加載依賴

?2.創建數據庫連接工具類(Dbutils類)

3.創建一個執行器(SqlExecutor類)

4.通用(增,刪,改)方法

1.創建方法

2.創建userInfo實體類

3.創建測試類,測試增,刪,改三個操作

4.最后執行結果

批量(增,刪,改)方法

1.在sqlExecutor類中添加一個executeBatch(String,Object[][])方法

2. 測試類中執行 批量(增,刪,改)操作

3.最后運行結果

通用查詢

策略模式:

1.定義一個類型轉換接口 ResultSetHandler

【一】將查詢結果轉換為Array數組

1.定義ArrayHandler轉換類,實現ResultSetHandler接口

2.定義RowProcessor行處理器

3. 在SqlExecutor類中定義通用查詢方法

【二】將查詢結果轉換為Map集合

1.定義MapHandler類,實現ResultSetHandler

2.在RowProcessor類中實現具體轉換map集合的方法

【二.五】測試轉換的兩個類型(Array和Map)

1.分別創建兩個策略

2.最后轉換結果

【三】將結果集轉換成Bean對象

1.創建一個Cloumn注解【映射數據庫名稱】

2. 在實體類中加上自定義Column注解

3.創建一個BeanHandler策略類實現ResultSetHandler接口

4.在行RowProcessor行處理器工具類添加實現轉換bean對象的方法

getPropertyDescriptors(Class);

newInstance(Class clazz);

checkColumn(String,PropertyDescriptor, Class);

setBean(String,ResultSet,PropertyDescriptor, T);

5.最后在測試類中進行測試結果

6. 最后執行結果


單條通用增刪改方法

? ? ? ? 1.創建maven項目,并加載依賴

對應maven的依賴為:

        <!--mysql驅動--><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.2.0</version></dependency><!--單元測試--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency><!--lombok依賴--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.32</version><scope>provided</scope></dependency>

?2.創建數據庫連接工具類(Dbutils類)

作用:? ?用于獲取Connection連接對象,連接數據庫

package com.xr.utils;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;/*** 數據庫連接工具類* 作用: 用于獲取Connection連接對象* @author xr* @Date 2024/5/8 13:10*/public class Dbutils {/*** 數據庫驅動* @Date 2024/5/8 13:12*/private static final String DRIVER = "com.mysql.cj.jdbc.Driver";/*** 數據據連接地址* jdbc:mysql://localhost:3306/數據庫名稱?serverTimezone=GMT%2B8"* @Date 2024/5/8 13:12*/private static final String URL = "jdbc:mysql://localhost:3306/dbutils?serverTimezone=GMT%2B8";/*** 數據庫用戶名* @Date 2024/5/8 13:12*/private static final String USER = "root";/*** 數據庫密碼* @Date 2024/5/8 13:12*/private static final String PASSWORD = "1234";/*** 獲取數據庫連接* @Date 2024/5/8 13:12*/public static Connection getConnection() {// 定義數據庫連接對象Connection conn = null;try {// 注冊JDBC驅動Class.forName(DRIVER);// 獲取數據庫連接conn = DriverManager.getConnection(URL, USER, PASSWORD);} catch (ClassNotFoundException | SQLException e) {throw new RuntimeException("數據數據庫連接失敗!!!!",e);}// 返回連接對象return conn;}
}

3.創建一個執行器(SqlExecutor類)

作用:?

???????? 封裝增,刪,查,改操作

package com.xr.utils;import java.sql.Connection;/*** sql 執行器,操作執行sql,通用增刪改操作* 作用: 封裝增刪改查操作* @author xr* @Date 2024/5/8 13:19*/
public class SqlExecutor {/*** 定義連接對象* @Date 2024/5/8 13:20*/private final Connection conn;/*** 構造方法,初始化連接對象* @Date 2024/5/8 13:20*/public SqlExecutor(Connection conn) {this.conn = conn;}}


4.通用(增,刪,改)方法

因為我想使用一個方法就可以完成(增,刪,改)操作

1. 例如:

? ? ? ? // 增加數據

? ? ? ? insert into user_info(u_name,u_password,u_age,deposit,brith)

????????value(?,?,?,?,?);

? ? ? ? // 刪除數據

? ? ? ??delete from user_info where u_name = ?

? ? ? ? // 修改數據

? ? ? ? update user set u_name = ??where id = ?

2. 發現:

????????我們的有不同的sql語句,設置不同的值

3. 解決方案:?

? ? ? ? 使用PreparedStatement中的預編譯sql語句,使用占位數 ? 解決值未知問題

4.思路:?

? ? ? ? ?1. 定義方法 excuteUpdate(sql,Object... params){};

? ? ? ? ?2. sql: sql語句? , params : 為多個參數,返回一個任意類型數組

? ? ? ? ?3. 將sql語句進行預編譯操作,設置傳遞過來的參數

? ? ? ? ?4. 為什么要將預編譯操作抽離出來一個方法,因為可以使方法結果更清晰

? ? ? ? ?5. 執行sql語句,返回受影響行數

? ? ? ? ?6.關閉資源

1.創建方法

????????executeUpdate(String,Object...) : 通用增,刪,改方法

????????setPreparedStatement(PreparedStatement,Object...) : sql語句預編譯,并設置參數

? ? ? ? close(Statement st) :關閉Statement對象資源

? ? ? ? close(ResultSet rs) : 關閉ResultSet對象資源

? ? ? ? close() :關閉Connection連接對象資源

    /*** 通用執行增刪改操作方法* sql: sql語句* params: sql語句中占位符對應的參數* @Date 2024/5/8 13:23*/    
public int executeUpdate(String sql,Object... params) throws SQLException {// 判斷連接對象是否為空if (conn == null) {throw new RuntimeException("連接對象為空!!!");}// 判斷sql語句是否為空if(sql == null || sql.isEmpty()) {throw new RuntimeException("sql語句為空!!!");}// 定義PreparedStatement對象,作用對sql語句進行預編譯PreparedStatement ps = null;try {// 將sql進行預編譯,方法中拋出SQL異常ps = conn.prepareStatement(sql);// 設置sql 中 ?號 占位符對應的參數// 比如 sql = "insert into user(name,age) values(?,?)"setPreparedStatementParams(ps,params);// 執行sql語句,返回影響行數return ps.executeUpdate();} catch (SQLException e) {// 異常重拋,重拋成運行時異常throw new RuntimeException("執行sql語句失敗!!!",e);} finally {// 關閉資源close(ps);close();}}/*** 設置sql預編譯參數* @Date 2024/5/8 9:04*/private void setPreparedStatement(PreparedStatement preparedStatement,Object... params)throws SQLException {// insert into user(id,name,age) values(?,?,?)// update user set name=?,age=? where id=?// 循環的邊界為傳遞過來的params參數長度for (int i = 0; i < params.length; i++) {// 設置占位符?對應的值,占位符下標從1開始preparedStatement.setObject(i+1,params[i]);}}/*** 關閉PreparedStatement對象資源* @Date 2024/5/8 13:54*/private void close(PreparedStatement ps) {if (ps != null) {try {// 關閉PreparedStatement對象資源ps.close();} catch (SQLException e) {throw new RuntimeException("關閉PreparedStatement對象失敗!!!",e);}}}/*** 關閉連接對象資源* @Date 2024/5/8 13:54*/private void close() {if (conn != null ) {try {// 關閉連接對象資源conn.close();} catch (SQLException e) {throw new RuntimeException("關閉conn對象資源失敗!!!",e);}}}/*** 關閉ResultSet對象資源* @param rs 結果集對象*/private void close(ResultSet rs) {if (rs != null) {try {// 關閉ResultSet對象資源rs.close();} catch (SQLException e) {throw new RuntimeException("關閉ResultSet對象失敗!!!",e);}}}

2.創建userInfo實體類

package com.xr.entity;import com.xr.util.interfaceUtils.Column;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.math.BigDecimal;
import java.time.LocalDateTime;/*** userInfo實體* @Data注解: 擁有get,set方法* @AllArgsConstructor: 擁有全參數構造方法* @NoArgsConstructor: 擁有無參構造方法* @author xr* @Date 2024/5/8 20:51*/@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {/*** id*/private Integer id; /*** 用戶名*/private String username;/*** 密碼*/private String password;/*** 年齡*/private Integer age;/*** 存款*/private BigDecimal deposit;/*** 生日*/private LocalDateTime birth;
}

3.創建測試類,測試增,刪,改三個操作

package com.xr;import com.xr.utils.Dbutils;
import com.xr.utils.SqlExecutor;import java.math.BigDecimal;
import java.sql.SQLException;
import java.time.LocalDateTime;/*** 測試類* @author xr* @Date 2024/5/10 10:54*/public class TestSql {public static void main(String[] args) throws SQLException {//*----------------------添加數據-----------------------*/// 創建SqlExecutor對象控制器,并傳入連接對象SqlExecutor sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定義sql語句,[插入數據]String sql = "insert into user_info(u_name,u_password,u_age,deposit,birth) values(?,?,?,?,?)";// 增加數據操作int i = sqlExecutor.executeUpdate(sql, "張三", "123456", 18, new BigDecimal(1000), LocalDateTime.now());//輸出結果System.out.println("插入數據:" + i +"行");/*----------------------修改數據-----------------------*/sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定義sql語句,[修改數據]sql = "update user_info set u_password = ? where u_name = ?";// 修改數據操作i = sqlExecutor.executeUpdate(sql, "22222","張三");//輸出結果System.out.println("修改數據:" + i +"行");/*----------------------刪除數據-----------------------*/sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定義sql語句,[刪除數據]sql = "delete from user_info where u_name = ?";// 刪除數據操作i = sqlExecutor.executeUpdate(sql, "張三");//輸出結果System.out.println("刪除數據:" + i +"行");}
}

4.最后執行結果


批量(增,刪,改)方法

前面定義的方法是 :

? ? ? ? 1.單條增加數據

? ? ? ? 2.單條修改數據

? ? ? ? 3.單條刪除數據

現在我想批量做這些操作(多條),就是將多個sql語句參數裝數組里面,定義一個Object[][]二維數組,存儲參數

思路:

? ? ? ? 1.將sql語句進行預編譯

????????2.將sql語句中的占位符對應的參數設置到sql語句中

????????3.將sql語句添加到批量緩存中

????????4.批量提交到數據庫中

1.在sqlExecutor類中添加一個executeBatch(String,Object[][])方法

String : sql語句

Object[][] : sql語句的參數

  /*** 批量執行sql語句(增,刪,改)* 實現思路:* 1.將sql語句進行預編譯* 2.將sql語句中的占位符對應的參數設置到sql語句中* 3.將sql語句添加到批量緩存中* 4.批量提交到數據庫中* @param sql sql語句* @param params 參數數組* @return 受影響行數*/public int[] executeBatch(String sql,Object[][] params) {if (conn == null) {throw new RuntimeException("連接對象為空!!!");}if (sql == null || sql.isEmpty()) {throw new RuntimeException("sql語句為空!!!");}// 定義PreparedStatement對象PreparedStatement ps = null;try {// 將sql語句進行預編譯ps = conn.prepareStatement(sql);// 遍歷params數組,將數組中的參數設置到sql語句中// 第一個循環遍歷params數組,第二個循環遍歷params數組中的數組for(Object[] param : params) {// 設置sql語句中占位符對應的參數setPreparedStatementParams(ps,param);//將ps的操作緩存到內存中,最后在批量提交到數據庫中  添加到批量緩存中ps.addBatch();}// 批量提交sql語句return ps.executeBatch();} catch (SQLException e) {throw new RuntimeException("預編譯sql語句失敗!!!",e);} finally {close(ps);close();}}

2. 測試類中執行 批量(增,刪,改)操作

package com.xr;import com.xr.utils.Dbutils;
import com.xr.utils.SqlExecutor;import java.math.BigDecimal;
import java.sql.SQLException;
import java.time.LocalDateTime;/*** 測試類* @author xr* @Date 2024/5/10 10:54*/public class TestSql {public static void main(String[] args) throws SQLException {//*----------------------批量添加數據-----------------------*/// 創建SqlExecutor對象控制器,并傳入連接對象SqlExecutor sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定義sql語句,[批量插入數據]String sql = "insert into user_info(u_name,u_password,u_age,deposit,birth) values(?,?,?,?,?)";// 創建二維數組Object[][] params = {{"李四", "123456", 18, new BigDecimal(1000), LocalDateTime.now()},{"王五", "123456", 18, new BigDecimal(1000), LocalDateTime.now()},{"張三", "123456", 18, new BigDecimal(1000), LocalDateTime.now()}};// 批量增加數據操作int[] i = sqlExecutor.executeBatch(sql,params );//輸出結果System.out.println("批量添加數據:" + i.length +"行");/*----------------------批量修改數據-----------------------*/sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定義sql語句,[批量修改數據]sql = "update user_info set u_password = ? where u_name = ?";// 創建二維數組params = new Object[][]{{"11111","李四"},{"22222","王五"},{"33333","張三"}};// 批量修改數據操作i = sqlExecutor.executeBatch(sql, params);//輸出結果System.out.println("批量修改數據:" + i.length +"行");/*----------------------批量刪除數據-----------------------*/sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定義sql語句,[批量刪除數據]sql = "delete from user_info where u_name = ?";//定義二位數組params = new Object[][]{{"李四"},{"王五"},{"張三"}};// 批量刪除數據操作i = sqlExecutor.executeBatch(sql, params);//輸出結果System.out.println("批量刪除數據:" + i.length +"行");}
}

3.最后運行結果


通用查詢

前面完成了增刪改操作,就差一個查詢了

當從數據庫查詢出來的結果后,java代碼如何拿到呢???

? ? ? ? 1.可以通過Array數組接收

? ? ? ? 2.可以通過map集合接收

? ? ? ? 3.可以將查詢出來的結果映射成java對象

? ? ? ? 4.如果只查一列的值,直接返回一個任意基本數據類型接收

這里有4中選項可以選擇,將查詢出的結果使用多種方式接收他,如果按照常理的方案寫的話,需要寫多個if,else很麻煩,所以可以擇優簡便選擇使用策略模式


?

策略模式:

策略模式:

? ? ? ? 我的理解:?當一個東西可以有很多種選擇時,就可以考慮使用策略來簡化代碼了

? ? ? ? 官方解釋:

????????????????策略模式是一種設計模式,它允許在運行時選擇算法的行為。在策略模式中,可以定義一系列算法,并將每個算法封裝在單獨的類中。這些算法具有相同的接口,使得它們可以互相替換,而客戶端代碼則可以根據需要選擇合適的算法。

? ? ? ? 比如:

????????????????我商城購買了一件東西,付款的時候可有有多種支付方式,如:支付寶支付,微信支付,銀聯支付,這時也是有多種選擇

1.定義一個類型轉換接口 ResultSetHandler

作用:

????????是封裝結果集不同處理方法,將查詢的結果轉換成不同的形式

ResultSet :結果集,我們通過結果集來轉化為其他類型

package com.xr.util;import java.sql.ResultSet;
import java.sql.SQLException;/*** 抽象的結果集處理器* @author xr* @Date 2024/5/8 9:58*/public interface ResultSetHandler<T> {/*** 結果集的處理方法* @param rs 結果集對象* @return 處理后的對象,因為返回的對象是未知類型,定義成泛型* @throws SQLException sql異常*/T handle(ResultSet rs) throws SQLException;
}

【一】將查詢結果轉換為Array數組

實現思路:?

? ? ? ? 1. 定義一個ArrayHandler類,實現ResultSetHandler接口

? ? ? ? 2. 定義一個RowProcessor工具類,具體的實現轉換方法寫在該類

????????2. 重寫Handle方法,調用RowProcessor工具類中的具體轉換實現

為什么不直接到ArrayHandler類中直接寫轉換的實現,而是又定義一個RowProcessor工具類來實現呢?

????????1. 因為要避免將具體的實現邏輯暴露給調用者,因為調用者并不需要知道如何將結果集轉換為數組,而是應該只關心如何將結果集轉換為數組,所以將具體的實現邏輯寫在RowProcessor中,也遵循單一職責原則

? ? ? ? 2.看RowProcessor這個類名,他是一個行處理工具類,所以現在將查詢出來的結果轉換成其他類型都只是轉換1條數據,如果多條數據需要轉換的話,我們也可以調用行處理器中的實現,簡便代碼

1.定義ArrayHandler轉換類,實現ResultSetHandler接口

作用:

? ? ? ? 將查詢出來的數據(單條)轉換成數組

package com.xr.result;import com.xr.utils.ResultSetHandler;
import com.xr.utils.RowProcessor;import java.sql.ResultSet;
import java.sql.SQLException;/*** 數組轉換類* @author xr* @Date 2024/5/10 19:31*/public class ArrayHandler implements ResultSetHandler<Object[]> {/*** 將結果集轉換為數組* @param rs 結果集* @return 返回轉換后的數組* @throws SQLException 拋出sql異常*/@Overridepublic Object[] handle(ResultSet rs) throws SQLException {// 如果結果集有數據,則調用工具類中的方法將結果集轉換為數組,否則返回nullreturn rs.next() ? RowProcessor.toArray(rs):null;}
}

2.定義RowProcessor行處理器

作用:

? ? ? ? 將轉換多個類型具體操作全部寫在本類,實施單一職責

在類中定義toArray()方法

? ? ? 作用:?將查詢出的數據結果集轉換成數組

package com.xr.utils;import java.sql.ResultSet;
import java.sql.SQLException;/*** 行處理器工具類* @author xr* @Date 2024/5/10 19:34*/public class RowProcessor {/*** 將結果集轉換為數組* @param rs 數據集對象* @return 轉換后的數組* @throws SQLException 拋出sql異常*/public static Object[] toArray(ResultSet rs) throws SQLException {// 定義數組,使用結果集的元數據中的列數作為數組長度Object[] result = new Object[rs.getMetaData().getColumnCount()];// 遍歷結果集for(int i = 0; i< result.length; i++) {// 遍歷數組,將結果集的每一列的值賦值給數組,通過結果集的下標獲取具體值result[i] = rs.getObject(i+1);}// 返回數組return result;}
}
3. 在SqlExecutor類中定義通用查詢方法

executeQuery(String,ResultSetHandler,Object....)

? ? ? ? 作用:

????????????????通用返回結果類,并將查詢的結果轉換成指定的類型

String :sql語句

ResultSetHandler:指定轉換類型的實現類

Object...: 參數數組

  /*** 執行查詢sql語句,返回指定類型* 實現思路:* 1.將sql語句進行預編譯* 2.將sql語句中的占位符對應的參數設置到sql語句中* 3.執行sql語句,返回結果集* 4.調用處理器,將查詢的結果轉換成指定類型* 5.返回轉換之后的類型* @param sql sql語句* @param handler 結果集處理器, 將結果集轉換為指定類型* @param params 參數數組* @return 返回轉換之后的類型*/public <T> T executeQuery(String sql,ResultSetHandler<T> handler,Object... params) {if (conn == null) {throw new RuntimeException("連接對象為空!!!");}if (sql == null || sql.isEmpty()) {throw new RuntimeException("sql語句為空!!!");}if (handler == null) {throw new RuntimeException("ResultSetHandler為空!!!");}// 定義PreparedStatement對象PreparedStatement ps = null;// 聲明結果集對象ResultSet rs = null;// 定義轉換后的結果T result = null;try {// 將sql語句進行預編譯ps = conn.prepareStatement(sql);// 設置sql語句中占位符對應的參數setPreparedStatementParams(ps,params);//執行sqlrs = ps.executeQuery();// 調用處理器,將查詢的結果轉換成指定類型result = handler.handle(rs);// 返回轉換后的類型return result;}catch (SQLException e) {throw new RuntimeException("預編譯sql語句失敗!!!",e);} finally {// 關閉ResultSet結果集資源close(rs);// 關閉PreparedStatement對象資源close(ps);// 關閉Connection連接對象資源close();}}


【二】將查詢結果轉換為Map集合

前面已經實現第一個策略,將查詢的結果轉換成Array數組

現在將查詢結果轉換成Map集合,同樣也是將這個策略實現ResultSetHandler接口

實現思路:

? ? ? ? 1.定義一個類MapHandler,并實現ResultsetHandler接口,重寫handle方法

? ? ? ? 2.在行轉換器RowProcess類中寫出轉換成map類型方法的具體實現

? ? ? ? 3.查詢的結果字段作為map集合的key,具體值作為map集合的value

1.定義MapHandler類,實現ResultSetHandler
package com.xr.result;import com.xr.utils.ResultSetHandler;
import com.xr.utils.RowProcessor;import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;/*** 將查詢的結果ResultSet結果集轉換為Map集合* @author xr* @Date 2024/5/11 8:42*/public class MapHandler implements ResultSetHandler<Map<String,Object>> {/*** 將查詢的結果ResultSet結果集轉換為Map集合* @param rs 結果集* @return 返回轉換后的Map集合* @throws SQLException sql異常*/@Overridepublic Map<String, Object> handle(ResultSet rs) throws SQLException {// 如果結果集有數據,則返回Map集合,否則返回nullreturn rs.next() ? RowProcessor.toMap(rs) : null;}
}
2.在RowProcessor類中實現具體轉換map集合的方法
    /*** 將查詢結果轉換成map* 思路:* 1. 獲取結果集的元數據* 2. 獲取結果集的列數* 3. 遍歷結果集,將結果集的每一列的值賦值給map,通過結果集的下標獲取具體值* 4. map集合中的key為列名,value為列的值* 5. 返回map* @param rs 數據集對象* @return 返回轉換后的map集合* @throws SQLException sql異常*/public static Map<String,Object> toMap(ResultSet rs) throws SQLException {// 定義map數組Map<String,Object> result = new HashMap<>();//循環遍歷結果結合for(int i = 0; i<rs.getMetaData().getColumnCount(); i++) {// 獲取列名String columnName = rs.getMetaData().getColumnName(i+1);// 獲取列值Object object = rs.getObject(i + 1);// 將列名和列值添加到map集合中result.put(columnName,object);}// 返回map數組return result;}

【二.五】測試轉換的兩個類型(Array和Map)

1.分別創建兩個策略
package com.xr;import com.xr.result.ArrayHandler;
import com.xr.result.MapHandler;
import com.xr.utils.Dbutils;
import com.xr.utils.ResultSetHandler;
import com.xr.utils.SqlExecutor;import java.sql.SQLException;
import java.util.Arrays;
import java.util.Map;/*** 測試類* @author xr* @Date 2024/5/10 10:54*/public class TestSql {public static void main(String[] args) throws SQLException {/*----------------------轉換Array數組-----------------------*/// 創建SqlExecutor對象控制器,并傳入連接對象SqlExecutor sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定義sql語句,[查詢單條數據]String sql = "select * from user_info where u_name = ?";/* 將查詢的結果轉換成Array數組*///1.創建Array策略ResultSetHandler<Object[]> arrayHandler = new ArrayHandler();//2.調用sqlExecutor工具類的executeQuery方法,傳入sql語句和策略和具體參數Object[] objects = sqlExecutor.executeQuery(sql, arrayHandler, "xr");// 查看轉換結果Arrays.stream(objects).forEach(System.out::println);System.out.println("-------------------------------轉換數組完成------------------------------------");/*----------------------轉換Array數組-----------------------*/// 創建SqlExecutor對象控制器,并傳入連接對象sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定義sql語句,[查詢單條數據]sql = "select * from user_info where u_name = ?";/* 將查詢的結果轉換成map集合*/// 1. 創建Map策略ResultSetHandler<Map<String,Object>> result = new MapHandler();//2.調用sqlExecutor工具類的executeQuery方法,傳入sql語句和策略和具體參數Map<String, Object> map = sqlExecutor.executeQuery(sql, result, "xr");// 循環輸出結果map.forEach((k,v)->{System.out.println("字段名:"+k+"---->字段值:"+v);});}
}
2.最后轉換結果


【三】將結果集轉換成Bean對象

將查詢的結果轉換成bean對象時解決難點:

? ? ? ? 1.對象屬性名稱與數據庫字段名稱不一樣

? ? ? ? 2.如何將查詢出來的值設置到對象屬性中

? ? ? ? 3.時間轉換問題

解決方案:

? ? ? ? 1.添加自定義注解,解決屬性名稱與數據庫字段名稱不一致

? ? ? ? 2.通過反射的內省機制來設置對象屬性值

? ? ? ? 3.因為有多種時間類型,mysql版本8之前只有Date時間類型,而mysql8之后多了LocalDateTime時間類型

1.創建一個Cloumn注解【映射數據庫名稱】

作用:

? ? ? ? 將數據庫庫字段名稱映射在實體類中的屬性上

package com.xr;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 映射數據庫名稱* @author xr* @Date 2024/5/11 9:37* @Retention 注解在運行時有效* @Target 注解作用范圍,使用范圍在屬性上*/@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {/*** 數據庫字段名*/String name();
}
2. 在實體類中加上自定義Column注解
package com.xr.entity;import com.xr.Column;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.math.BigDecimal;
import java.time.LocalDateTime;/*** userInfo實體* @author xr* @Date 2024/5/8 20:51*/@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {/*** id*/@Column(name="u_id")private Integer id;/*** 用戶名*/@Column(name="u_name")private String username;/*** 密碼*/@Column(name="u_password")private String password;/*** 年齡*/@Column(name="u_age")private Integer age;/*** 存款* 后面這兩個字段與數據庫名稱一樣,所以不需要添加@Column注解*/private BigDecimal deposit;/*** 生日*/private LocalDateTime birth;
}
3.創建一個BeanHandler策略類實現ResultSetHandler接口

為什么要定義class對象?

? ? ? ? 1.當我們使用實體類中的屬性名與數據庫字段名進行匹配時,需要用到反射獲取注解和屬性

? ? ? ? 2.當我們給實體類中的屬性設置值時,需要通過反射的內省機制來設置

? ? ? ? 3.一切使用到的反射操作,都需要先通過獲取Class對象才能使用

package com.xr.result;import com.xr.utils.ResultSetHandler;
import com.xr.utils.RowProcessor;import java.sql.ResultSet;
import java.sql.SQLException;/*** 將結果集轉換成bean對象* @author xr* @Date 2024/5/11 9:15*/public class BeanHandler<T> implements ResultSetHandler<T> {/*** 定義class對象*/private Class<T> clazz;/*** 構造方法傳遞類對象* @param clazz 類對象*/public BeanHandler(Class<T> clazz){this.clazz = clazz;}@Overridepublic T handle(ResultSet rs) throws SQLException {// 如果有數據,則返回bean對象,否則返回nullreturn rs.next()? RowProcessor.toBean(rs,clazz) :null;}
}
4.在行RowProcessor行處理器工具類添加實現轉換bean對象的方法
/*** 將查詢結果轉換成Bean對象* 思路:* 1.我要將查詢出來的數據轉化為對象* 2.數據源rs,擁有數據庫所有字段名稱,及對應的數據值* 3.clazz,通過clazz對象可以獲取里面的所有屬性* 4.rs里面的字段名稱作為clazz對象的屬性* 5.將rs里面的數據值賦值給clazz對象的屬性的屬性值* 6.由于rs里面的字段名稱和clazz對象的屬性名稱可能不一樣* 7.定義了注解@Cloumn進行映射對應的數據庫名稱* 8.如果屬性有@Cloumn注解,則獲取對應的屬性名稱,判斷是否和數據庫字段一樣,一樣則賦值* 9.如果屬性沒有@Cloumn注解,則獲取屬性名稱,因為沒有@Cloumn注解,證明屬性名稱和數據庫字段一樣* 10.設置值的時候肯能有時間類型轉換問題* 11.為什么會有時間問題呢,因為mysql的驅動5和mysql的驅動8,時間類型不一樣* 12.設置完成將返回對應的對象* @param rs 結果集* @param clazz 對象對應的類* @return 返回轉換后的bean對象* @throws SQLException sql異常*/public static <T> T toBean(ResultSet rs,Class<T> clazz) throws SQLException {// 獲取類中的所有屬性,通過內省機制獲取PropertyDescriptor[] pds = getPropertyDescriptors(clazz);// 獲取對象實例,由于getConstructor,newInstance需要拋出異常,// 則將他們提取出來,定義另一個方法拋出異常T bean = newInstance(clazz);for (int i = 0 ; i < rs.getMetaData().getColumnCount(); i++) {/* 獲取列名,通過類名判斷是否與屬性名一樣 */String columnLabel = rs.getMetaData().getColumnLabel(i+1);// 循環遍歷屬性for (PropertyDescriptor pd : pds) {// 判斷屬性名稱和數據庫字段是否一樣if(checkColumn(columnLabel,pd,clazz)) {// 屬性名稱和數據庫字段一樣,則設置對應的值/** columnLabel:數據庫字段名稱* rs:結果集,擁有所有數據庫字段名稱及對應的數據值* pd:屬性描述器,屬性名稱* bean:對象實例* */setBean(columnLabel,rs,pd,bean);break;}}}return bean;}

1.toBean(Result,Class)方法,rs結果集對象,Class為類對象;

2.這個方法里面又調用了其他4個方法

? ? ? ? 1.getPropertyDescriptors(Class<?> class)? ? ? ? ?獲取所有屬性

? ? ? ? 2.newInstance(Class);? ? ? ??獲取對象實例

? ? ? ? 3.checkColumn(Stirng,PropertyDescriptor, Class<?> )? ?判斷數據庫字段名稱是否與屬性名稱一致

? ? ? ? 4.setBean(String,ResultSet,PropertyDescriptor,T)? ? ? ? ? 設置對象的屬性值

3.為什么要定義這4方法呢????

? ? ? ? 1. 因為這些方法都需要拋出異常,如果全部寫在toBean方法內,使得代碼的閱讀性糟糕

getPropertyDescriptors(Class<?>);

參數:

? ? ? ? Class<?> : 實體類的Class對象

作用:

? ? ? ? 獲取實體類中所有屬性;

在RowProcessor行處理器類中添加該方法

    /*** 獲取類中的所有屬性【內省機制獲取】* @param clazz 類* @return 返回屬性數組*/private static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) {try {// 創建BeanInfo對象BeanInfo beanInfo = Introspector.getBeanInfo(clazz, Object.class);// 獲取所有屬性,返回屬性數組return beanInfo.getPropertyDescriptors();} catch (IntrospectionException e) {throw new RuntimeException("獲取屬性描述器失敗",e);}}

newInstance(Class<T> clazz);

參數:

? ? ? ? Class:實體類的Class對象

作用:

? ? ? ? 獲取實體類對象的實例

在RowProcessor行處理器類中添加該方法

    /*** 獲取對象實例* @param clazz 類的class對象* @return 返回對象實例*/private static <T> T newInstance(Class<T> clazz) {try {return clazz.getConstructor().newInstance();} catch (Exception e) {throw new RuntimeException("創建對象失敗!!!",e);}}

checkColumn(String,PropertyDescriptor, Class<?>);

參數:

? ? ? ? ? ? ? ? ? ? String :? ? ? ? ?數據庫字段名稱

PropertyDescriptor:? ? ? ?屬性

? ? ? ? ? ? ? ?Class<?>:? ? ? ? ?實體類的Class對象

作用 :

????????判斷屬性名稱和數據庫字段是否一樣

在RowProcessor行處理器類中添加該方法

    /*** 判斷是否與屬性名一樣*/private static boolean checkColumn(String columnName, PropertyDescriptor pd, Class<?> clazz) {// 獲取屬性名稱String beanName = pd.getName();try {// 通過屬性名稱獲取對象的屬性Field fieldName = clazz.getDeclaredField(beanName);// 判斷屬性是否有注解// 如果有注解,代表屬性名稱與數據庫字段名稱不一樣,需要進行對比// 如果沒有,則代表屬性名稱與數據庫名稱一樣if (fieldName.isAnnotationPresent(Column.class)) {// 獲取屬性上的注解值beanName = fieldName.getAnnotation(Column.class).name();}return beanName.equalsIgnoreCase(columnName);} catch (NoSuchFieldException e) {throw new RuntimeException("判斷屬性名稱失敗!",e);}}
setBean(String,ResultSet,PropertyDescriptor, T);

參數:

? ? ? ? ? ? ? ? ? ? String :? ? ? ? ?數據庫字段名稱

? ? ? ? ? ? ? ?ResultSet:? ? ? ? ?結果集

PropertyDescriptor:? ? ? ?屬性

? ? ? ? ? ? ? ? ? ? ? ? ? ?T?:? ? ? ?對象

作用:

????????設置實體類屬性對應的值

在RowProcessor行處理器類中添加該方法

  /**** @param columnLabel 列名* @param rs 數據集* @param pd 屬性* @param instance 實例對象* @param <T> 返回轉換后的對象*/private static <T> void setBean(String columnLabel,ResultSet rs,PropertyDescriptor pd, T instance) {try {// 通過名稱獲取數據庫查詢的具體值Object value = rs.getObject(columnLabel);// 如果是基本類型,并且獲取的值為空,直接返回if(pd.getPropertyType().isPrimitive() && value == null) {return;}// 通過傳遞過來來的字段獲取set方法,并調用通過invoke方法調用set方法pd.getWriteMethod().invoke(instance,value);} catch (SQLException | IllegalAccessException | InvocationTargetException e) {throw new RuntimeException("獲取具體值失敗,設置具體值失敗!!!",e);}}
5.最后在測試類中進行測試結果
package com.xr;import com.xr.entity.UserInfo;
import com.xr.result.BeanHandler;
import com.xr.utils.Dbutils;
import com.xr.utils.SqlExecutor;import java.sql.SQLException;/*** 測試類* @author xr* @Date 2024/5/10 10:54*/public class TestSql {public static void main(String[] args) throws SQLException {/*----------------------轉換Bean對象-----------------------*/// 創建SqlExecutor對象控制器,并傳入連接對象SqlExecutor sqlExecutor = new SqlExecutor(Dbutils.getConnection());// 定義sql語句,[查詢單條數據]String sql = "select * from user_info where u_name = ?";// 創建Bean策略BeanHandler<UserInfo> userInfoBeanHandler = new BeanHandler<>(UserInfo.class);//調用sqlExecutor工具類的executeQuery方法,傳入sql語句和策略和具體參數UserInfo xr = sqlExecutor.executeQuery(sql, userInfoBeanHandler, "xr");// 輸出結果System.out.println(xr);System.out.println("-------------------------------轉換bean對象完成------------------------------------");}
}
6. 最后執行結果


------------------------------------------------------------待更新------------------------------------------------------------

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/10777.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/10777.shtml
英文地址,請注明出處:http://en.pswp.cn/web/10777.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

探索數據結構:樹與二叉樹

?? 歡迎大家來到貝蒂大講堂?? &#x1f388;&#x1f388;養成好習慣&#xff0c;先贊后看哦~&#x1f388;&#x1f388; 所屬專欄&#xff1a;數據結構與算法 貝蒂的主頁&#xff1a;Betty’s blog 1. 樹 1.1. 樹的定義 樹是一種非線性的數據結構&#xff0c;它是由n&a…

ORA-609頻繁出現在alert.log,如何解決?

ORA-609就alertlog中比較常見的一個報錯&#xff0c;雖然并沒有太大的影響&#xff0c;但是頻繁的出現在alert log也是很讓人厭煩的事情&#xff0c;本文介紹如何排查解決ORA-609問題。 1.ORA-609官方定義 could not attach to incoming connection Cause Oracle process cou…

【SRC實戰】前端脫敏信息泄露

挖個洞先 https://mp.weixin.qq.com/s/xnCQQCAneT21vYH8Q3OCpw “ 以下漏洞均為實驗靶場&#xff0c;如有雷同&#xff0c;純屬巧合 ” 01 — 漏洞證明 一、前端脫敏&#xff0c;請求包泄露明文 “ 前端脫敏處理&#xff0c;請求包是否存在泄露&#xff1f; ” 1、獲取驗…

|Python新手小白中級教程|第二十八章:面向對象編程(類定義語法私有屬性類的繼承與多態)(4)

文章目錄 前言一、類定義語法二、私有方法和私有屬性1.私有屬性2.私有方法 三、類“繼承”1.初識繼承2.使用super函數調用父類中構造的東西 四、類“多態”1.多態基礎2.子類不同形態3.使用isinstance函數與多態結合判斷類型 總結 前言 大家好&#xff0c;我是BoBo仔吖&#xf…

6818Linux內核開發移植

Linux內核開發移植 Linux內核版本變遷及其獲得 Linux是最受歡迎的自由電腦操作系統內核&#xff0c; 是一個用C語言寫成&#xff0c; 并且符合POSIX標準的類Unix操作系統 Linux是由芬蘭黑客Linus Torvalds開發的&#xff0c; 目的是嘗試在英特爾x86架構上提供自由免費的類Un…

Task Office for Mac v9.0激活版:任務管理新境界

還在為繁瑣的任務管理而煩惱嗎&#xff1f;Task Office for Mac為您帶來全新的任務管理體驗。簡潔明了的界面設計&#xff0c;讓您輕松上手&#xff1b;強大的任務管理和項目管理功能&#xff0c;讓您輕松掌握任務進度&#xff1b;多用戶協作功能&#xff0c;讓團隊協作更加高效…

ubuntu24.04安裝ros

ubuntu24.04安裝ros 踩坑 踩坑 目前安裝人數比較少&#xff0c;沒有較為詳細的博客&#xff0c;參考官網的鏈接 http://docs.ros.org/en/rolling/Installation/Ubuntu-Install-Debians.html 同時在如下的一步中會找不到網址報錯&#xff0c;此時可以參考https://blog.51cto.c…

Excel辦公技巧之下拉菜單

在日常辦工中&#xff0c;經常需在單元格中輸入特定的值&#xff0c;此時我們可以使用下拉菜單解決&#xff0c;輸入錯誤和錯誤值&#xff0c;可以一勞永逸的解決固定數據輸入問題。 使用Excel下拉菜單時&#xff0c;它在數據輸入和驗證方面發揮著重要作用通過點擊單元格的下拉…

學習筆記-Vue3中Hook函數

什么是Hook函數 Hook翻譯過來是鉤子的意思&#xff0c;其本質上是一組可復用的函數。簡單理解來說&#xff0c;你能夠在不同的組件中&#xff0c;實現相同的代碼邏輯&#xff0c;以達到代碼復用、提高維護性的效果。那為何叫’鉤子’呢&#xff0c;我的理解是&#xff1a; 它可…

商業數據分析--時間序列圖及趨勢分析

繪制時間序列圖,并指出存在什么樣的狀態如上兩圖: 可見狀態:從時間序列圖可以看出,這些數據存在明顯的季節性波動,每年的第4季度值都最高,而第2季度值最低。同時也存在一些下降的趨勢。 通過引進虛擬變量,建立多元線性回歸模型。答: 通過引入虛擬變量,我們可以建立如下的…

Oracle數據庫之多表查詢、層次查詢(五)

目錄 前言 Oracle 的連接條件的類型 多表查詢 1. 使用JOIN關鍵字 2. 使用WHERE子句進行多表查詢 3. 子查詢 4. EXISTS關鍵字 5. 集合運算 6. 注意事項&#xff1a; 層次查詢 前言 Oracle 的連接條件的類型 等值連接不等值連接外連接自連接 多表查詢 在Oracle數據…

商場學習之微服務

前言 寒假前在新電腦上配置了java環境&#xff0c;maven倉庫&#xff0c;node,js&#xff0c;navicat&#xff0c;MySQL&#xff0c;linux&#xff0c;vmware等環境&#xff0c;創建了6個mysql數據庫&#xff0c;77張表。 如此多的表&#xff0c;字段&#xff0c;去手寫基礎…

Web入門——三欄布局頁面

前置知識 內外邊距 內邊距(padding)&#xff1a; padding是元素邊框與其內容之間的空間。也就是說&#xff0c;如果你給一個元素設置了內邊距&#xff0c;這個空間會作為元素內容與元素邊框之間的緩沖區域。設置內邊距會使元素本身變大。例如padding:10px就創建了10像素的空間…

Qt之QMqtt 發送圖片數據

簡述 MQTT(消息隊列遙測傳輸)是ISO標準下基于發布/訂閱范式的消息協議;它工作在TCP/IP協議族上,是為硬件性能低下的遠程設備以及網絡狀況糟糕的情況下而設計的發布/訂閱型消息協議,為此,它需要一個消息中間件; MQTT是一個基于客戶端-服務器的消息發布/訂閱傳輸協議;MQT…

Ubuntu設置中午輸入法

本篇博客將指導您如何在Ubuntu系統中設置中文輸入法&#xff0c;讓您能夠輕松地進行中文輸入。 準備工作 在開始之前&#xff0c;請確保您的系統已經更新到最新版本。這可以通過以下命令來完成&#xff1a; sudo apt update && sudo apt upgrade這將幫助確保所有的軟…

Docker in Docker(DinD)原理與實戰

&#x1f407;明明跟你說過&#xff1a;個人主頁 &#x1f3c5;個人專欄&#xff1a;《Docker幻想曲&#xff1a;從零開始&#xff0c;征服容器宇宙》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目錄 一、引言 1、Docker簡介 2、Docker …

使用 AI Assistant for Observability 和組織的運行手冊增強 SRE 故障排除

作者&#xff1a;Almudena Sanz Oliv, Katrin Freihofner, Tom Grabowski 通過本指南&#xff0c;你的 SRE 團隊可以實現增強的警報修復和事件管理。 可觀測性 AI 助手可幫助用戶使用自然語言界面探索和分析可觀測性數據&#xff0c;利用自動函數調用來請求、分析和可視化數據…

Harmony 添加library依賴庫步驟

在Harmony添加library依賴庫步驟如下&#xff1a; 1、在library中定義名字 在library中的oh-package.json5中定義library對外的名字是什么&#xff1a;格式是 “name”:“ohos/名字” {"name": "ohos/library_name" //名字 }2、在項目目錄build-profi…

windows系統安裝Ubuntu子系統

安裝前先在 控制面板 中打開 程序與功能選項 &#xff0c;點擊 啟用或關閉Windows功能&#xff1a; 勾選 適用于 Linux的Windows子系統 和 虛擬機平臺 、 Hyper-v 。 重啟電腦后再 Microsoft Store Windows應用商店 中下載合適的Ubuntu版本。 運行Ubuntu程序&#xff0c;如出現…

【實戰】算法思路總結

面試過程中&#xff0c;總是被拷打&#xff0c;信心都要沒了。但是也慢慢摸索出一些思路&#xff0c;希望對大家有幫助。 &#xff08;需要多用一下ACM模式&#xff0c;力扣模式提供好了模板&#xff0c;自己在IDEA里面寫的話&#xff0c;還是會有些陌生&#xff09; 0、基本…