首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 資料庫存取:DAO 模式與 Proxy 模式完整實作範例

     編輯:Java 相關知識

前言

在 Java 企業級開發中,資料存取層(DAO)的設計直接影響系統的可維護性與擴充性。本文展示一個完整的分層架構範例,結合 DAO 介面、實作類別、Proxy 代理類別 以及 Factory 工廠模式,實現資料庫連線管理、CRUD 操作與業務邏輯的清晰分離。

架構概覽

Emp (實體類別)
    ↑
IEmpDAO (DAO 介面)
    ↑
EmpDAOImpl (DAO 具體實作,處理 SQL)
    ↑
EmpDAOProxy (代理類別,管理連線與交易)
    ↑
DAOFactory (工廠類別,提供 DAO 實例)
    ↑
TestDAOInsert (測試主程式)

1. 實體類別:Emp.java

package com.example.dao;

import java.util.Date;

public class Emp {
    private int empno;
    private String ename;
    private String job;
    private Date hiredate;
    private float sal;

    // Getters & Setters 省略...
    // 建議使用 Lombok @Data 簡化
}

2. 資料庫連線管理:DatabaseConnection.java

package com.example.dao;

import java.sql.Connection;
import java.sql.DriverManager;

public class DatabaseConnection {
    private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
    private static final String URL = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC";
    private static final String USER = "root";
    private static final String PASS = "password";

    private Connection conn;

    public DatabaseConnection() throws Exception {
        Class.forName(DRIVER);
        this.conn = DriverManager.getConnection(URL, USER, PASS);
    }

    public Connection getConnection() { return conn; }

    public void close() throws Exception {
        if (conn != null && !conn.isClosed()) conn.close();
    }
}

提示:實務上建議使用連線池(HikariCP、Druid)替代直接建立連線。

3. DAO 介面:IEmpDAO.java

package com.example.dao;

import java.util.List;

public interface IEmpDAO {
    boolean doCreate(Emp emp) throws Exception;
    List<Emp> findAll(String keyword) throws Exception;
    Emp findById(int empno) throws Exception;
}

4. DAO 實作:EmpDAOImpl.java

package com.example.dao;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class EmpDAOImpl implements IEmpDAO {
    private Connection conn;
    private PreparedStatement pstmt;

    public EmpDAOImpl(Connection conn) { this.conn = conn; }

    @Override
    public boolean doCreate(Emp emp) throws Exception {
        String sql = "INSERT INTO emp(empno, ename, job, hiredate, sal) VALUES(?,?,?,?,?)";
        pstmt = conn.prepareStatement(sql);
        pstmt.setInt(1, emp.getEmpno());
        pstmt.setString(2, emp.getEname());
        pstmt.setString(3, emp.getJob());
        pstmt.setDate(4, new java.sql.Date(emp.getHiredate().getTime()));
        pstmt.setFloat(5, emp.getSal());
        boolean flag = pstmt.executeUpdate() > 0;
        pstmt.close();
        return flag;
    }

    @Override
    public List<Emp> findAll(String keyword) throws Exception {
        List<Emp> list = new ArrayList<>();
        String sql = "SELECT empno, ename, job, hiredate, sal FROM emp WHERE ename LIKE ? OR job LIKE ?";
        pstmt = conn.prepareStatement(sql);
        pstmt.setString(1, "%" + keyword + "%");
        pstmt.setString(2, "%" + keyword + "%");
        ResultSet rs = pstmt.executeQuery();
        while (rs.next()) {
            Emp e = new Emp();
            e.setEmpno(rs.getInt("empno"));
            e.setEname(rs.getString("ename"));
            e.setJob(rs.getString("job"));
            e.setHiredate(rs.getDate("hiredate"));
            e.setSal(rs.getFloat("sal"));
            list.add(e);
        }
        pstmt.close();
        return list;
    }

    @Override
    public Emp findById(int empno) throws Exception {
        String sql = "SELECT empno, ename, job, hiredate, sal FROM emp WHERE empno=?";
        pstmt = conn.prepareStatement(sql);
        pstmt.setInt(1, empno);
        ResultSet rs = pstmt.executeQuery();
        Emp e = null;
        if (rs.next()) {
            e = new Emp();
            e.setEmpno(rs.getInt("empno"));
            e.setEname(rs.getString("ename"));
            e.setJob(rs.getString("job"));
            e.setHiredate(rs.getDate("hiredate"));
            e.setSal(rs.getFloat("sal"));
        }
        pstmt.close();
        return e;
    }
}

5. Proxy 代理類別:EmpDAOProxy.java

負責連線建立、交易控制(commit/rollback)、資源關閉,對上層隱藏實作細節。

package com.example.dao;

import java.util.List;

public class EmpDAOProxy implements IEmpDAO {
    private DatabaseConnection dbc;
    private EmpDAOImpl dao;

    public EmpDAOProxy() throws Exception {
        this.dbc = new DatabaseConnection();
        this.dao = new EmpDAOImpl(dbc.getConnection());
    }

    @Override
    public boolean doCreate(Emp emp) throws Exception {
        boolean flag = false;
        try {
            if (dao.findById(emp.getEmpno()) == null) { // 避免重複鍵
                flag = dao.doCreate(emp);
            }
        } finally {
            dbc.close(); // 確保連線關閉
        }
        return flag;
    }

    @Override
    public List<Emp> findAll(String keyword) throws Exception {
        try {
            return dao.findAll(keyword);
        } finally {
            dbc.close();
        }
    }

    @Override
    public Emp findById(int empno) throws Exception {
        try {
            return dao.findById(empno);
        } finally {
            dbc.close();
        }
    }
}

6. 工廠類別:DAOFactory.java

package com.example.dao;

public class DAOFactory {
    public static IEmpDAO getEmpDAOInstance() throws Exception {
        return new EmpDAOProxy();
    }
}

7. 測試主程式:TestDAOInsert.java

package com.example.dao;

public class TestDAOInsert {
    public static void main(String[] args) throws Exception {
        IEmpDAO dao = DAOFactory.getEmpDAOInstance();
        for (int i = 0; i < 5; i++) {
            Emp emp = new Emp();
            emp.setEmpno(1000 + i);
            emp.setEname("測試員工-" + i);
            emp.setJob("工程師-" + i);
            emp.setHiredate(new java.util.Date());
            emp.setSal(5000 + i * 100);
            dao.doCreate(emp);
        }
        System.out.println("資料新增完成");
    }
}

設計模式優勢分析

模式角色優勢
DAOIEmpDAO / EmpDAOImpl抽象資料存取邏輯,支援多種資料來源切換(MySQL、Oracle、MongoDB 等)
ProxyEmpDAOProxy集中管理連線生命週期、交易邊界、例外處理,業務層無需關心 JDBC 細節
FactoryDAOFactory解耦客戶端與具體實作,未來可擴充為 Spring IoC 容器管理

進階改進建議

  1. 連線池整合:將 DatabaseConnection 替換為 HikariCP DataSource,由 Proxy 從池取得/歸還連線。
  2. 泛型 DAO:設計 BaseDAO<T, ID> 減少重複 CRUD 程式碼。
  3. Spring 整合:使用 @Repository、@Transactional、JdbcTemplate 或 MyBatis 簡化開發。
  4. 例外轉換:自訂 DAOException 封裝 SQLException,避免外層依賴 JDBC API。

此分層架構雖屬傳統,但清晰展現了關注點分離的核心思想,是理解現代 ORM 框架(Hibernate、MyBatis)底層運作原理的最佳切入點。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved