Java JDBC连接MySQL数据库CRUD操作指南
2026/9/16 8:00:20 网站建设 项目流程

1. Java数据库连接基础解析

Java连接数据库实现增删改查(CRUD)是每个Java开发者必须掌握的核心技能。无论你是刚入门的新手,还是有一定经验的开发者,理解JDBC的工作原理和最佳实践都至关重要。JDBC(Java Database Connectivity)是Java语言中用来规范客户端程序如何访问数据库的应用程序接口,它为数据库操作提供了一套标准方法。

在实际开发中,我们通常使用MySQL作为关系型数据库的代表。MySQL因其开源、高性能和易用性,成为Java后端开发中最常用的数据库之一。要使用Java操作MySQL数据库,首先需要确保已经安装并配置好MySQL服务,同时准备好JDBC驱动。

注意:MySQL 8.0+版本与5.x版本在JDBC连接配置上有一些差异,特别是密码加密方式的变化,这在后续连接配置时需要特别注意。

2. 环境准备与项目搭建

2.1 数据库环境配置

在开始编码前,我们需要完成以下准备工作:

  1. 安装MySQL数据库服务(推荐5.7或8.0版本)
  2. 创建测试数据库和用户表
  3. 下载对应版本的MySQL JDBC驱动

创建测试表的SQL语句示例:

CREATE TABLE IF NOT EXISTS users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, password VARCHAR(50) NOT NULL, email VARCHAR(100), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2.2 Java项目依赖配置

对于Maven项目,需要在pom.xml中添加MySQL驱动依赖:

<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency>

如果是Gradle项目,则在build.gradle中添加:

implementation 'mysql:mysql-connector-java:8.0.28'

3. JDBC核心组件详解

3.1 驱动加载与连接建立

JDBC操作数据库的第一步是加载驱动并建立连接。在Java 6以后,我们不再需要显式调用Class.forName()加载驱动,而是通过DriverManager自动发现并注册驱动。

建立数据库连接的标准代码:

String url = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC"; String username = "root"; String password = "123456"; try (Connection conn = DriverManager.getConnection(url, username, password)) { // 数据库操作代码 } catch (SQLException e) { e.printStackTrace(); }

提示:使用try-with-resources语法可以确保Connection在使用后自动关闭,避免资源泄漏。

3.2 Statement与PreparedStatement

JDBC提供了两种主要的SQL执行接口:

  1. Statement:用于执行静态SQL语句
  2. PreparedStatement:预编译SQL语句,防止SQL注入,性能更好

创建PreparedStatement的示例:

String sql = "INSERT INTO users(username, password, email) VALUES(?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1, "testuser"); pstmt.setString(2, "password123"); pstmt.setString(3, "test@example.com"); pstmt.executeUpdate(); }

3.3 ResultSet处理查询结果

查询操作会返回ResultSet对象,它包含了查询结果的所有数据。处理ResultSet的典型模式:

String query = "SELECT * FROM users WHERE username = ?"; try (PreparedStatement pstmt = conn.prepareStatement(query)) { pstmt.setString(1, "testuser"); try (ResultSet rs = pstmt.executeQuery()) { while (rs.next()) { int id = rs.getInt("id"); String username = rs.getString("username"); String email = rs.getString("email"); // 处理数据... } } }

4. CRUD操作完整实现

4.1 增加数据(Insert)

public int insertUser(User user) throws SQLException { String sql = "INSERT INTO users(username, password, email) VALUES(?, ?, ?)"; try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { pstmt.setString(1, user.getUsername()); pstmt.setString(2, user.getPassword()); pstmt.setString(3, user.getEmail()); int affectedRows = pstmt.executeUpdate(); if (affectedRows == 0) { throw new SQLException("创建用户失败,没有行受影响"); } try (ResultSet generatedKeys = pstmt.getGeneratedKeys()) { if (generatedKeys.next()) { return generatedKeys.getInt(1); } else { throw new SQLException("创建用户失败,未获取到ID"); } } } }

4.2 查询数据(Select)

public User getUserById(int userId) throws SQLException { String sql = "SELECT * FROM users WHERE id = ?"; try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, userId); try (ResultSet rs = pstmt.executeQuery()) { if (rs.next()) { User user = new User(); user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); user.setEmail(rs.getString("email")); user.setCreateTime(rs.getTimestamp("create_time")); user.setUpdateTime(rs.getTimestamp("update_time")); return user; } } } return null; }

4.3 更新数据(Update)

public boolean updateUser(User user) throws SQLException { String sql = "UPDATE users SET username = ?, password = ?, email = ? WHERE id = ?"; try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1, user.getUsername()); pstmt.setString(2, user.getPassword()); pstmt.setString(3, user.getEmail()); pstmt.setInt(4, user.getId()); return pstmt.executeUpdate() > 0; } }

4.4 删除数据(Delete)

public boolean deleteUser(int userId) throws SQLException { String sql = "DELETE FROM users WHERE id = ?"; try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, userId); return pstmt.executeUpdate() > 0; } }

5. 高级特性与性能优化

5.1 批量操作

当需要执行大量相似操作时,批量处理可以显著提高性能:

public int[] batchInsert(List<User> users) throws SQLException { String sql = "INSERT INTO users(username, password, email) VALUES(?, ?, ?)"; try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { for (User user : users) { pstmt.setString(1, user.getUsername()); pstmt.setString(2, user.getPassword()); pstmt.setString(3, user.getEmail()); pstmt.addBatch(); } return pstmt.executeBatch(); } }

5.2 事务管理

确保一组操作要么全部成功,要么全部失败:

public boolean transferMoney(int fromId, int toId, BigDecimal amount) throws SQLException { Connection conn = null; try { conn = getConnection(); conn.setAutoCommit(false); // 开始事务 // 扣除转出账户金额 String deductSql = "UPDATE accounts SET balance = balance - ? WHERE id = ? AND balance >= ?"; try (PreparedStatement pstmt = conn.prepareStatement(deductSql)) { pstmt.setBigDecimal(1, amount); pstmt.setInt(2, fromId); pstmt.setBigDecimal(3, amount); if (pstmt.executeUpdate() != 1) { conn.rollback(); return false; } } // 增加转入账户金额 String addSql = "UPDATE accounts SET balance = balance + ? WHERE id = ?"; try (PreparedStatement pstmt = conn.prepareStatement(addSql)) { pstmt.setBigDecimal(1, amount); pstmt.setInt(2, toId); if (pstmt.executeUpdate() != 1) { conn.rollback(); return false; } } conn.commit(); // 提交事务 return true; } catch (SQLException e) { if (conn != null) { conn.rollback(); } throw e; } finally { if (conn != null) { conn.setAutoCommit(true); conn.close(); } } }

5.3 连接池配置

生产环境推荐使用连接池管理数据库连接。以HikariCP为例:

public class DataSourceConfig { private static HikariDataSource dataSource; static { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/test_db"); config.setUsername("root"); config.setPassword("123456"); config.setMaximumPoolSize(20); config.setMinimumIdle(5); config.setConnectionTimeout(30000); config.setIdleTimeout(600000); config.setMaxLifetime(1800000); dataSource = new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }

6. 常见问题与解决方案

6.1 连接超时问题

问题现象:获取数据库连接时抛出ConnectionTimeoutException。

解决方案

  1. 检查数据库服务是否正常运行
  2. 验证连接字符串、用户名和密码是否正确
  3. 增加连接超时时间配置
  4. 检查网络连接是否通畅

6.2 SQL注入防护

风险代码

String sql = "SELECT * FROM users WHERE username = '" + username + "'"; Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql);

安全写法

String sql = "SELECT * FROM users WHERE username = ?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, username); ResultSet rs = pstmt.executeQuery();

6.3 字符编码问题

问题现象:中文数据存入数据库后变成乱码。

解决方案

  1. 确保数据库、表和字段使用utf8mb4字符集
  2. JDBC连接字符串中添加字符集参数:
    jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=UTF-8
  3. 检查应用程序的字符编码设置

6.4 时区问题

问题现象:时间类型数据与预期不符。

解决方案

  1. 在JDBC连接字符串中指定时区:
    jdbc:mysql://localhost:3306/test_db?serverTimezone=Asia/Shanghai
  2. 确保数据库服务器和应用程序服务器使用相同的时区
  3. 在Java代码中明确处理时区转换

7. 最佳实践与性能优化

7.1 资源释放

确保及时释放数据库资源,避免内存泄漏:

public List<User> getAllUsers() throws SQLException { List<User> users = new ArrayList<>(); Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { conn = getConnection(); pstmt = conn.prepareStatement("SELECT * FROM users"); rs = pstmt.executeQuery(); while (rs.next()) { User user = new User(); // 设置用户属性... users.add(user); } } finally { if (rs != null) try { rs.close(); } catch (SQLException e) { /* 忽略 */ } if (pstmt != null) try { pstmt.close(); } catch (SQLException e) { /* 忽略 */ } if (conn != null) try { conn.close(); } catch (SQLException e) { /* 忽略 */ } } return users; }

7.2 使用Try-With-Resources简化代码

Java 7+支持try-with-resources语法,可以自动关闭资源:

public List<User> getAllUsers() throws SQLException { List<User> users = new ArrayList<>(); String sql = "SELECT * FROM users"; try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql); ResultSet rs = pstmt.executeQuery()) { while (rs.next()) { User user = new User(); // 设置用户属性... users.add(user); } } return users; }

7.3 使用RowMapper简化结果集处理

定义通用的RowMapper接口:

@FunctionalInterface public interface RowMapper<T> { T mapRow(ResultSet rs) throws SQLException; }

实现通用的查询方法:

public <T> List<T> query(String sql, RowMapper<T> rowMapper, Object... params) throws SQLException { List<T> results = new ArrayList<>(); try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { for (int i = 0; i < params.length; i++) { pstmt.setObject(i + 1, params[i]); } try (ResultSet rs = pstmt.executeQuery()) { while (rs.next()) { results.add(rowMapper.mapRow(rs)); } } } return results; }

使用示例:

List<User> users = query("SELECT * FROM users WHERE username LIKE ?", rs -> { User user = new User(); user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); return user; }, "%test%");

7.4 使用JdbcTemplate简化JDBC操作

Spring框架提供的JdbcTemplate进一步简化了JDBC操作:

@Repository public class UserRepository { private final JdbcTemplate jdbcTemplate; @Autowired public UserRepository(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public User findById(int id) { String sql = "SELECT * FROM users WHERE id = ?"; return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> { User user = new User(); user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); return user; }, id); } public int insert(User user) { String sql = "INSERT INTO users(username, password) VALUES(?, ?)"; return jdbcTemplate.update(sql, user.getUsername(), user.getPassword()); } }

8. 实际项目中的扩展应用

8.1 分页查询实现

public Page<User> findUsersByPage(int pageNum, int pageSize) throws SQLException { Page<User> page = new Page<>(); page.setPageNum(pageNum); page.setPageSize(pageSize); // 查询总数 String countSql = "SELECT COUNT(*) FROM users"; try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(countSql); ResultSet rs = pstmt.executeQuery()) { if (rs.next()) { page.setTotal(rs.getInt(1)); } } // 查询分页数据 String dataSql = "SELECT * FROM users LIMIT ? OFFSET ?"; int offset = (pageNum - 1) * pageSize; try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(dataSql)) { pstmt.setInt(1, pageSize); pstmt.setInt(2, offset); try (ResultSet rs = pstmt.executeQuery()) { List<User> users = new ArrayList<>(); while (rs.next()) { User user = new User(); // 设置用户属性... users.add(user); } page.setData(users); } } return page; }

8.2 存储过程调用

public void callStoredProcedure(int userId) throws SQLException { String sql = "{call update_user_status(?, ?)}"; try (Connection conn = getConnection(); CallableStatement cstmt = conn.prepareCall(sql)) { cstmt.setInt(1, userId); cstmt.registerOutParameter(2, Types.INTEGER); cstmt.execute(); int result = cstmt.getInt(2); if (result != 0) { throw new SQLException("存储过程执行失败,错误码:" + result); } } }

8.3 数据库元数据获取

public void printDatabaseMetadata() throws SQLException { try (Connection conn = getConnection()) { DatabaseMetaData metaData = conn.getMetaData(); System.out.println("数据库产品名称: " + metaData.getDatabaseProductName()); System.out.println("数据库产品版本: " + metaData.getDatabaseProductVersion()); System.out.println("JDBC驱动名称: " + metaData.getDriverName()); System.out.println("JDBC驱动版本: " + metaData.getDriverVersion()); // 获取所有表信息 try (ResultSet tables = metaData.getTables(null, null, "%", new String[]{"TABLE"})) { while (tables.next()) { System.out.println("表名: " + tables.getString("TABLE_NAME")); } } } }

9. 测试与调试技巧

9.1 单元测试配置

使用JUnit测试数据库操作:

public class UserDaoTest { private UserDao userDao; private Connection testConn; @Before public void setUp() throws SQLException { // 初始化测试数据库连接 String url = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"; testConn = DriverManager.getConnection(url, "sa", ""); // 创建测试表 try (Statement stmt = testConn.createStatement()) { stmt.execute("CREATE TABLE users(id INT PRIMARY KEY, username VARCHAR(50))"); } userDao = new UserDao(() -> testConn); } @Test public void testInsertAndFindUser() throws SQLException { User user = new User(); user.setId(1); user.setUsername("testuser"); userDao.insert(user); User found = userDao.findById(1); assertNotNull(found); assertEquals("testuser", found.getUsername()); } @After public void tearDown() throws SQLException { if (testConn != null) { testConn.close(); } } }

9.2 SQL日志记录

配置日志框架记录执行的SQL语句:

# log4j.properties log4j.logger.java.sql=DEBUG log4j.logger.javax.sql=DEBUG

或使用p6spy等工具拦截SQL:

<!-- pom.xml --> <dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>3.9.1</version> </dependency>

配置spy.properties:

driverlist=com.mysql.jdbc.Driver appender=com.p6spy.engine.spy.appender.Slf4JLogger

10. 从JDBC到ORM框架

虽然直接使用JDBC可以完成所有数据库操作,但在实际项目中,我们通常会使用ORM框架如Hibernate或MyBatis来简化开发。理解JDBC原理对于使用这些框架至关重要。

10.1 MyBatis基础配置

<!-- mybatis-config.xml --> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test_db"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/example/mapper/UserMapper.xml"/> </mappers> </configuration>

10.2 MyBatis Mapper示例

<!-- UserMapper.xml --> <mapper namespace="com.example.mapper.UserMapper"> <select id="findById" resultType="com.example.model.User"> SELECT * FROM users WHERE id = #{id} </select> <insert id="insert" useGeneratedKeys="true" keyProperty="id"> INSERT INTO users(username, password) VALUES(#{username}, #{password}) </insert> </mapper>

10.3 Spring Data JPA示例

@Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(nullable = false, length = 50) private String username; @Column(nullable = false, length = 100) private String password; // getters and setters } public interface UserRepository extends JpaRepository<User, Integer> { User findByUsername(String username); }

在实际开发中,根据项目规模和团队习惯选择合适的数据库访问方式。小型项目可以直接使用JDBC或JdbcTemplate,中型项目可以考虑MyBatis,大型复杂项目则可能更适合使用JPA或Hibernate。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询