You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于JDBC的MySQL数据持久化Java程序分层架构合理性咨询

架构可行性分析与代码优化建议

你的分层架构(实体层+DAO层+Service层+连接工厂)整体是可行的,符合Java后端开发的经典分层思想,能够实现数据持久化与业务逻辑的初步分离,适合教学场景下的JDBC入门实践。但代码中存在一些细节问题,需要调整优化,具体如下:

一、现有代码的核心问题

  • ConnectionFactory设计缺陷:

    1. 静态单例连接会导致多线程场景下的连接冲突,且当try-with-resources自动关闭连接后,后续调用getConnection()会返回已关闭的连接,引发SQL异常。
    2. 数据库配置(URL、用户名、密码)硬编码在代码中,不利于维护和环境切换。
    3. 使用abstract修饰类无意义,因为类中全是静态方法,无需被继承。
  • 代码一致性问题:

    1. ProductDAO中实现的方法名与DAO接口不一致:接口定义findAll(),但实现类写的是buscarTodos();调用连接时用criaConexao(),但ConnectionFactory的方法名是getConnection(),会直接编译报错。
  • 异常处理不规范:
    DAO层直接用e.printStackTrace()吞掉异常,上层无法感知数据库操作是否失败,也无法进行统一的异常处理。

  • 实体类主键策略不合理:
    Product的id由客户端传入,若数据库主键设为自增,这种设计会导致主键冲突,也不符合自增主键的使用逻辑。

二、针对性优化建议

1. 重构ConnectionFactory

取消静态单例连接,每次请求都创建新连接(或引入连接池如HikariCP提升性能),同时将配置抽离到外部文件:

public class ConnectionFactory {
    private static Properties props = new Properties();

    static {
        try (InputStream is = ConnectionFactory.class.getClassLoader().getResourceAsStream("db.properties")) {
            props.load(is);
        } catch (IOException e) {
            throw new RuntimeException("加载数据库配置失败", e);
        }
    }

    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(
                props.getProperty("db.url"),
                props.getProperty("db.username"),
                props.getProperty("db.password")
        );
    }
}

对应的db.properties文件放在resources目录下:

db.url=jdbc:mysql://localhost/database_name
db.username=user
db.password=password

2. 统一代码命名规范

修正ProductDAO中的方法名,确保与接口一致:

@Override
public List<Product> findAll() { // 替换buscarTodos
    String sql = "SELECT * FROM product";
    List<Product> listProd = new ArrayList<>();
    try(Connection con = ConnectionFactory.getConnection(); // 替换criaConexao
        PreparedStatement stmt = con.prepareStatement(sql)) {
        ResultSet rs = stmt.executeQuery();
        while (rs.next()) {
            Product p = new Product(rs.getInt("id"), rs.getString("name"));
            listProd.add(p);
        }
    } catch (SQLException e) {
        throw new RuntimeException("查询商品失败", e); // 抛出异常而非吞掉
    }
    return listProd;
}

3. 优化异常处理

将DAO层的异常向上抛出(或封装为自定义业务异常),让Service层或上层统一处理:

@Override
public void save(Product p) {
    String sql = "INSERT INTO product values (?,?)";
    try(Connection con = ConnectionFactory.getConnection();
        PreparedStatement stmt = con.prepareStatement(sql)) {
        stmt.setInt(1, p.getId());
        stmt.setString(2, p.getName());
        stmt.executeUpdate();
    } catch (SQLException e) {
        throw new RuntimeException("保存商品失败", e);
    }
}

4. 调整主键生成策略

如果数据库中product表的id是自增主键,修改save方法以返回生成的主键,并让实体类不强制要求传入id:

// 调整Product构造方法,支持无id的创建
public Product(String name) {
    this.name = name;
}

// ProductDAO的save方法修改
@Override
public int save(Product p) {
    String sql = "INSERT INTO product(name) values (?)"; // 去掉id字段
    try(Connection con = ConnectionFactory.getConnection();
        PreparedStatement stmt = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
        stmt.setString(1, p.getName());
        stmt.executeUpdate();
        // 获取生成的主键
        ResultSet rs = stmt.getGeneratedKeys();
        if(rs.next()) {
            return rs.getInt(1);
        }
        throw new RuntimeException("获取生成的主键失败");
    } catch (SQLException e) {
        throw new RuntimeException("保存商品失败", e);
    }
}

5. Service层的扩展方向

当前Service层仅做方法转发,后续可以在这里添加业务逻辑:

  • 参数校验(如判断商品名称是否为空)
  • 事务管理(多DAO操作时统一控制事务)
  • 业务规则处理(如商品库存校验)

内容的提问来源于stack exchange,提问作者user24618785

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 02:56:03