DAO概念
DAO:Data Access Object,數據訪問對象。
Java是面向對象語言,數據在Java中通常以對象的形式存在。一張表對應一個實體類,一張表的操作對應一個DAO對象!
在Java操作數據庫時,我們會將對同一張表的增刪改查操作統一維護起來,維護的這個類就是DAO層。
DAO層只關注對數據庫的操作,供業務層Service調用,將職責劃分清楚!
還是有user為例
面向接口開發,這邊先提供dao接口
package com.qayrup.dao;import com.qayrup.pojo.User;/*** @author qayrup* @version 1.0* @date-time 2025/9/12-10:20* @belongs-project qayrup-study* @belongs-package com.qayrup.dao* @description*/public interface UserDao {User queryUserByNameAndPassword(String name, String password);int insertUser(User user);
}
實現這個接口
package com.qayrup.dao.impl;import com.qayrup.dao.UserDao;
import com.qayrup.pojo.User;
import com.qayrup.utils.JDBCTools;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;/*** @author qayrup* @version 1.0* @date-time 2025/9/12-10:21* @belongs-project qayrup-study* @belongs-package com.qayrup.dao.impl* @description*/public class UserDaoImpl implements UserDao {@Override/*** 根據用戶名和密碼查詢用戶信息* @param name 用戶名* @param password 密碼* @return 查詢到的User對象,如果未找到則返回空的User對象* @throws Exception 數據庫操作異常*/public User queryUserByNameAndPassword(String name, String password) throws Exception {// 從JDBCTools工具類中獲取數據庫連接Connection connection = JDBCTools.getConnection();// 定義SQL查詢語句,使用占位符防止SQL注入String sql = "select * from user where name = ? and password = ?";// 創建PreparedStatement對象,用于執行預編譯SQL語句PreparedStatement preparedStatement = connection.prepareStatement(sql);// 設置第一個占位符的值為傳入的用戶名preparedStatement.setString(1, name);// 設置第二個占位符的值為傳入的密碼preparedStatement.setString(2, password);// 執行查詢操作,獲取結果集ResultSet resultSet = preparedStatement.executeQuery();// 創建一個新的User對象用于存儲查詢結果User user = new User();// 遍歷結果集,通常只會有一條記錄匹配while (resultSet.next()){// 從結果集中獲取id字段值并設置到user對象user.setId(resultSet.getLong("id"));// 從結果集中獲取name字段值并設置到user對象user.setName(resultSet.getString("name"));// 從結果集中獲取password字段值并設置到user對象user.setPassword(resultSet.getString("password"));// 從結果集中獲取type字段值并設置到user對象user.setType(resultSet.getInt("type"));// 從結果集中獲取status字段值并設置到user對象user.setStatus(resultSet.getInt("status"));// 從結果集中獲取created_at字段值并設置到user對象user.setCreated_at(resultSet.getDate("created_at"));// 從結果集中獲取created_by字段值并設置到user對象user.setCreated_by(resultSet.getLong("created_by"));// 從結果集中獲取updated_at字段值并設置到user對象user.setUpdated_at(resultSet.getDate("updated_at"));// 從結果集中獲取updated_by字段值并設置到user對象user.setUpdated_by(resultSet.getLong("updated_by"));}// 關閉PreparedStatement資源preparedStatement.close();// 返回封裝了查詢結果的User對象return user;}/*** 插入新的用戶信息到數據庫* @param user 要插入的User對象* @return 受影響的行數,成功插入返回1* @throws Exception 數據庫操作異常*/@Overridepublic int insertUser(User user) throws Exception {// 從JDBCTools工具類中獲取數據庫連接Connection connection = JDBCTools.getConnection();// 定義SQL插入語句,使用占位符防止SQL注入String sql = "insert into user(name,password,type,status,created_at,created_by,updated_at,updated_by) values(?,?,?,?,?,?,?,?)";// 創建PreparedStatement對象,用于執行預編譯SQL語句PreparedStatement preparedStatement = connection.prepareStatement(sql);// 設置第一個占位符的值為用戶姓名preparedStatement.setString(1, user.getName());// 設置第二個占位符的值為用戶密碼preparedStatement.setString(2, user.getPassword());// 設置第三個占位符的值為用戶類型preparedStatement.setInt(3, user.getType());// 設置第四個占位符的值為用戶狀態preparedStatement.setInt(4, user.getStatus());// 設置第五個占位符的值為創建時間,將java.util.Date轉換為java.sql.DatepreparedStatement.setDate(5, new java.sql.Date(user.getCreated_at().getTime()));// 設置第六個占位符的值為創建者IDpreparedStatement.setLong(6, user.getCreated_by());// 設置第七個占位符的值為更新時間,將java.util.Date轉換為java.sql.DatepreparedStatement.setDate(7, new java.sql.Date(user.getUpdated_at().getTime()));// 設置第八個占位符的值為更新者IDpreparedStatement.setLong(8, user.getUpdated_by());// 執行插入操作,返回受影響的行數// 返回插入操作的結果return preparedStatement.executeUpdate();}
}
外部調用
@Testpublic void daoSelect() throws Exception {UserDao userDao = new UserDaoImpl();User user = userDao.queryUserByNameAndPassword("admin", "admin");System.out.println("user = " + user);}
這樣有個壞處,那就是每有一張表,就得寫一個這種查詢,
所以這邊將這種基礎查詢封裝
package com.qayrup.dao;import com.qayrup.utils.JDBCTools;import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;/*** @author qayrup* @version 1.0* @date-time 2025/9/12-09:58* @belongs-project qayrup-study* @belongs-package com.qayrup.dao* @description*/public class BaseDao {protected int update(String sql, Object... args) throws Exception {Connection connection = JDBCTools.getConnection();PreparedStatement preparedStatement = connection.prepareStatement(sql);if(args != null && args.length>0){for(int i=0; i<args.length; i++) {preparedStatement.setObject(i+1,args[i]);//?的編號從1開始,不是從0開始,數組的下標是從0開始}}int count = preparedStatement.executeUpdate();preparedStatement.close();if (!connection.getAutoCommit()) return count;//回收JDBCTools.release();return count;}/*通用的查詢多個Javabean對象的方法,例如:多個員工對象,多個部門對象等這里的clazz接收的是T類型的Class對象,如果查詢員工信息,clazz代表Employee.class,如果查詢部門信息,clazz代表Department.class,返回List<T> list*/protected <T> ArrayList<T> query(Class<T> clazz, String sql, Object... args) throws Exception {// 創建PreparedStatement對象,對sql預編譯Connection connection = JDBCTools.getConnection();PreparedStatement ps = connection.prepareStatement(sql);//設置?的值if(args != null && args.length>0){for(int i=0; i<args.length; i++) {ps.setObject(i+1, args[i]);//?的編號從1開始,不是從0開始,數組的下標是從0開始}}ArrayList<T> list = new ArrayList<>();ResultSet res = ps.executeQuery();/*獲取結果集的元數據對象。元數據對象中有該結果集一共有幾列、列名稱是什么等信息*/ResultSetMetaData metaData = res.getMetaData();int columnCount = metaData.getColumnCount();//獲取結果集列數//遍歷結果集ResultSet,把查詢結果中的一條一條記錄,變成一個一個T 對象,放到list中。while(res.next()){//循環一次代表有一行,代表有一個T對象T t = clazz.newInstance();//要求這個類型必須有公共的無參構造//把這條記錄的每一個單元格的值取出來,設置到t對象對應的屬性中。for(int i=1; i<=columnCount; i++){//for循環一次,代表取某一行的1個單元格的值Object value = res.getObject(i);//這個值應該是t對象的某個屬性值//獲取該屬性對應的Field對象//String columnName = metaData.getColumnName(i);//獲取第i列的字段名//這里再取別名可能沒辦法對應上String columnName = metaData.getColumnLabel(i);//獲取第i列的字段名或字段的別名//通過反射將屬性的值設置到t對象中Field field = clazz.getDeclaredField(columnName);//開啟暴力反射field.setAccessible(true);//這么做可以操作private的屬性//將值設置給t對象field.set(t,value);}list.add(t);}res.close();ps.close();//這里檢查下是否開啟事務,開啟不關閉連接,業務方法關閉!//沒有開啟事務的話,直接回收關閉即可!if (connection.getAutoCommit()) {//回收JDBCTools.release();}return list;}/*** 通用的查詢單個JavaBean對象的方法* @param clazz 指定要查詢的JavaBean類型* @param sql 查詢SQL語句* @param args SQL中的參數值* @return 返回查詢到的第一個對象,如果沒有查詢到則返回null* @throws Exception 拋出異常*/protected <T> T queryBean(Class<T> clazz,String sql, Object... args) throws Exception {// 調用query方法獲取查詢結果列表ArrayList<T> list = query(clazz, sql,args);// 如果查詢結果為空或者沒有數據,則返回nullif(list == null || list.size() == 0){return null;}// 返回查詢結果中的第一個對象return list.get(0);}
}
讓實現類繼承這個封裝類
結果