在 Web 开发中,后端与数据库的连接是核心环节之一。无论是简单的博客系统还是复杂的企业级应用,都离不开高效、安全、稳定的数据库连接。本文将系统全面地梳理网站后端连接数据库的核心知识点,从连接原理到最佳实践,涵盖 JDBC、连接池、ORM 框架、事务管理、性能优化等各个方面,帮助开发者建立完整的知识体系。
一、数据库连接基础
1.1 数据库连接原理
应用服务器 → 数据库驱动 → 网络连接 → 数据库服务器
↓ ↓ ↓ ↓
连接请求 → 建立连接 → 传输数据 → 执行SQL → 返回结果
连接过程:
加载数据库驱动
建立网络连接(TCP/IP)
进行身份认证
创建会话
执行 SQL 语句
返回结果
关闭连接(释放资源)
1.2 数据库驱动
常用 JDBC 驱动:
xml
<!-- Maven 依赖 -->
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- PostgreSQL -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.6.0</version>
</dependency>
<!-- Oracle -->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>21.9.0.0</version>
</dependency>
<!-- SQL Server -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>12.4.0.jre11</artifactId>
</dependency>
<!-- H2(内存数据库) -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.2.220</version>
</dependency>
1.3 JDBC 基础连接
java
import java.sql.*;
public class JDBCDemo {
// 数据库连接参数
private static final String URL = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC";
private static final String USERNAME = "root";
private static final String PASSWORD = "123456";
// 1. 基础连接示例
public void basicConnection() {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 加载驱动(可选,JDBC 4.0+ 自动加载)
Class.forName("com.mysql.cj.jdbc.Driver");
// 建立连接
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
System.out.println("数据库连接成功");
// 创建 Statement
stmt = conn.createStatement();
// 执行查询
rs = stmt.executeQuery("SELECT * FROM users");
// 处理结果
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("username");
System.out.println("ID: " + id + ", Name: " + name);
}
} catch (ClassNotFoundException e) {
System.err.println("驱动加载失败: " + e.getMessage());
} catch (SQLException e) {
System.err.println("数据库连接失败: " + e.getMessage());
} finally {
// 关闭资源(顺序:ResultSet → Statement → Connection)
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 2. PreparedStatement(防止 SQL 注入)
public void preparedStatementExample() {
String sql = "SELECT * FROM users WHERE username = ? AND age > ?";
try (Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
// 设置参数
pstmt.setString(1, "张三");
pstmt.setInt(2, 18);
// 执行查询
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
System.out.println(rs.getString("username"));
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
// 3. 批处理操作
public void batchExample() {
String sql = "INSERT INTO users (username, age) VALUES (?, ?)";
try (Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
// 关闭自动提交
conn.setAutoCommit(false);
// 添加批处理
for (int i = 0; i < 1000; i++) {
pstmt.setString(1, "user" + i);
pstmt.setInt(2, 20 + i % 50);
pstmt.addBatch();
}
// 执行批处理
int[] results = pstmt.executeBatch();
// 提交事务
conn.commit();
System.out.println("批量插入完成: " + results.length);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
二、连接池技术
2.1 为什么需要连接池
不使用连接池的问题:
每次请求都需要创建和关闭连接,开销大
高并发时数据库连接数爆炸
资源浪费,性能低下
连接池的优势:
复用连接,减少创建/销毁开销
限制最大连接数,保护数据库
管理连接生命周期
提供连接健康检查
2.2 HikariCP(推荐)
xml
<!-- Maven 依赖 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version>
</dependency>
java
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
public class HikariCPDemo {
// 配置方式1:编程式配置
public DataSource createDataSource() {
HikariConfig config = new HikariConfig();
// 基础配置
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC");
config.setUsername("root");
config.setPassword("123456");
// 连接池大小配置
config.setMaximumPoolSize(10); // 最大连接数
config.setMinimumIdle(5); // 最小空闲连接
config.setIdleTimeout(600000); // 空闲超时(10分钟)
config.setConnectionTimeout(30000); // 连接超时(30秒)
config.setMaxLifetime(1800000); // 连接最大生命周期(30分钟)
// 验证配置
config.setConnectionTestQuery("SELECT 1");
config.setValidationTimeout(5000);
// 其他配置
config.setPoolName("MyHikariPool");
config.setRegisterMbeans(true);
return new HikariDataSource(config);
}
// 配置方式2:配置文件方式
// hikari.properties
/*
dataSourceClassName=com.mysql.cj.jdbc.MysqlDataSource
dataSource.url=jdbc:mysql://localhost:3306/mydb
dataSource.user=root
dataSource.password=123456
maximumPoolSize=10
minimumIdle=5
connectionTimeout=30000
idleTimeout=600000
maxLifetime=1800000
*/
public DataSource createDataSourceFromFile() {
HikariConfig config = new HikariConfig("/hikari.properties");
return new HikariDataSource(config);
}
}
2.3 Druid(阿里)
xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.20</version>
</dependency>
java
import com.alibaba.druid.pool.DruidDataSource;
import javax.sql.DataSource;
import java.util.Properties;
public class DruidDemo {
public DataSource createDataSource() {
DruidDataSource dataSource = new DruidDataSource();
// 基础配置
dataSource.setUrl("jdbc:mysql://localhost:3306/mydb?useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("123456");
// 连接池配置
dataSource.setInitialSize(5); // 初始化连接数
dataSource.setMinIdle(5); // 最小空闲连接
dataSource.setMaxActive(20); // 最大活跃连接
dataSource.setMaxWait(60000); // 获取连接最大等待时间
// 连接保持配置
dataSource.setTimeBetweenEvictionRunsMillis(60000); // 检查空闲连接的间隔
dataSource.setMinEvictableIdleTimeMillis(300000); // 连接最小空闲时间
dataSource.setMaxEvictableIdleTimeMillis(600000); // 连接最大空闲时间
// 验证配置
dataSource.setValidationQuery("SELECT 1");
dataSource.setTestWhileIdle(true);
dataSource.setTestOnBorrow(false);
dataSource.setTestOnReturn(false);
// 监控配置
dataSource.setFilters("stat,wall,log4j"); // 开启监控和防火墙
return dataSource;
}
// 配置文件方式
public DataSource createFromProperties() {
Properties props = new Properties();
props.setProperty("url", "jdbc:mysql://localhost:3306/mydb");
props.setProperty("username", "root");
props.setProperty("password", "123456");
props.setProperty("initialSize", "5");
props.setProperty("maxActive", "20");
props.setProperty("minIdle", "5");
return DruidDataSourceFactory.createDataSource(props);
}
}
2.4 Tomcat JDBC Pool
xml
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
<version>10.1.15</version>
</dependency>
java
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
public class TomcatPoolDemo {
public DataSource createDataSource() {
PoolProperties p = new PoolProperties();
p.setUrl("jdbc:mysql://localhost:3306/mydb");
p.setUsername("root");
p.setPassword("123456");
p.setDriverClassName("com.mysql.cj.jdbc.Driver");
p.setInitialSize(5);
p.setMaxActive(20);
p.setMinIdle(5);
p.setMaxIdle(10);
p.setMaxWait(30000);
p.setValidationQuery("SELECT 1");
p.setTestOnBorrow(true);
p.setTestWhileIdle(true);
p.setTimeBetweenEvictionRunsMillis(60000);
p.setMinEvictableIdleTimeMillis(300000);
return new DataSource(p);
}
}
三、Spring 框架中的数据库连接
3.1 配置数据源
application.yml 配置:
spring:
datasource:
# 基础配置
url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# HikariCP 配置
hikari:
maximum-pool-size: 10
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
connection-test-query: SELECT 1
pool-name: MyHikariPool
# Druid 配置
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 5
max-active: 20
min-idle: 5
max-wait: 60000
filters: stat,wall,slf4j
stat-view-servlet:
enabled: true
url-pattern: /druid/*
login-username: admin
login-password: admin
Java 配置:
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
// 自定义 HikariCP 配置
@Bean
public DataSource customDataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("root");
config.setPassword("123456");
config.setMaximumPoolSize(20);
config.setMinimumIdle(5);
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
config.setMaxLifetime(1800000);
return new HikariDataSource(config);
}
// 多数据源配置
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
}
3.2 JdbcTemplate 使用
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
// 查询单条记录
public User findById(Long id) {
String sql = "SELECT id, username, age FROM users WHERE id = ?";
return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), id);
}
// 查询列表
public List<User> findAll() {
String sql = "SELECT id, username, age FROM users";
return jdbcTemplate.query(sql, (rs, rowNum) -> {
User user = new User();
user.setId(rs.getLong("id"));
user.setUsername(rs.getString("username"));
user.setAge(rs.getInt("age"));
return user;
});
}
// 插入并返回主键
public long insert(User user) {
String sql = "INSERT INTO users (username, age) VALUES (?, ?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setString(1, user.getUsername());
ps.setInt(2, user.getAge());
return ps;
}, keyHolder);
return keyHolder.getKey().longValue();
}
// 批量插入
public int[] batchInsert(List<User> users) {
String sql = "INSERT INTO users (username, age) VALUES (?, ?)";
return jdbcTemplate.batchUpdate(sql, users, 100, (ps, user) -> {
ps.setString(1, user.getUsername());
ps.setInt(2, user.getAge());
});
}
// 命名参数查询
public List<User> findByCondition(String username, Integer age) {
String sql = "SELECT * FROM users WHERE username = :username AND age > :age";
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("username", username);
params.addValue("age", age);
return namedParameterJdbcTemplate.query(sql, params, new BeanPropertyRowMapper<>(User.class));
}
}