基于JDBC的MySQL数据持久化Java程序分层架构合理性咨询
架构可行性分析与代码优化建议
你的分层架构(实体层+DAO层+Service层+连接工厂)整体是可行的,符合Java后端开发的经典分层思想,能够实现数据持久化与业务逻辑的初步分离,适合教学场景下的JDBC入门实践。但代码中存在一些细节问题,需要调整优化,具体如下:
一、现有代码的核心问题
ConnectionFactory设计缺陷:
- 静态单例连接会导致多线程场景下的连接冲突,且当
try-with-resources自动关闭连接后,后续调用getConnection()会返回已关闭的连接,引发SQL异常。 - 数据库配置(URL、用户名、密码)硬编码在代码中,不利于维护和环境切换。
- 使用
abstract修饰类无意义,因为类中全是静态方法,无需被继承。
- 静态单例连接会导致多线程场景下的连接冲突,且当
代码一致性问题:
- ProductDAO中实现的方法名与DAO接口不一致:接口定义
findAll(),但实现类写的是buscarTodos();调用连接时用criaConexao(),但ConnectionFactory的方法名是getConnection(),会直接编译报错。
- ProductDAO中实现的方法名与DAO接口不一致:接口定义
异常处理不规范:
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
相关产品推荐
相关产品推荐

