获取 Connection 对象的几种方式详解
获取 Connection 对象的几种方式详解
1. JDBC 原生方式
1.1 使用 DriverManager(最基础方式)
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;public class DriverManagerExample {public static Connection getConnectionBasic() throws SQLException {// 1. 注册驱动(JDBC 4.0+ 可以省略,但显式注册更安全)try {Class.forName("com.mysql.cj.jdbc.Driver");} catch (ClassNotFoundException e) {throw new SQLException("数据库驱动未找到", e);}// 2. 获取连接String url = "jdbc:mysql://localhost:3306/mydatabase";String username = "root";String password = "password";return DriverManager.getConnection(url, username, password);}public static Connection getConnectionWithParams() throws SQLException {String url = "jdbc:mysql://localhost:3306/mydatabase?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8";String username = "root";String password = "password";// 可以添加连接属性java.util.Properties props = new java.util.Properties();props.setProperty("user", username);props.setProperty("password", password);props.setProperty("useUnicode", "true");props.setProperty("characterEncoding", "UTF-8");return DriverManager.getConnection(url, props);}
}
1.2 使用示例
public class JdbcExample {public void basicUsage() {Connection conn = null;try {// 获取连接conn = DriverManagerExample.getConnectionBasic();// 执行数据库操作Statement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery("SELECT * FROM users");while (rs.next()) {System.out.println(rs.getString("username"));}} catch (SQLException e) {e.printStackTrace();} finally {// 关闭连接if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}}// 使用 try-with-resources(推荐)public void modernUsage() {try (Connection conn = DriverManagerExample.getConnectionBasic();Statement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {while (rs.next()) {System.out.println(rs.getString("username"));}} catch (SQLException e) {e.printStackTrace();}// 自动关闭资源}
}
2. 连接池方式
2.1 HikariCP(性能最优)
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;public class HikariCPExample {private static HikariDataSource dataSource;static {// 配置 HikariCPHikariConfig config = new HikariConfig();config.setJdbcUrl("jdbc:mysql://localhost:3306/mydatabase");config.setUsername("root");config.setPassword("password");config.setDriverClassName("com.mysql.cj.jdbc.Driver");// 连接池配置config.setMaximumPoolSize(20);config.setMinimumIdle(5);config.setConnectionTimeout(30000); // 30秒config.setIdleTimeout(600000); // 10分钟config.setMaxLifetime(1800000); // 30分钟config.setAutoCommit(true);// 可选优化配置config.addDataSourceProperty("cachePrepStmts", "true");config.addDataSourceProperty("prepStmtCacheSize", "250");config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");dataSource = new HikariDataSource(config);}public static Connection getConnection() throws SQLException {return dataSource.getConnection();}public static DataSource getDataSource() {return dataSource;}public static void closeDataSource() {if (dataSource != null && !dataSource.isClosed()) {dataSource.close();}}
}
2.2 Apache DBCP2
import org.apache.commons.dbcp2.BasicDataSource;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;public class DBCP2Example {private static BasicDataSource dataSource;static {dataSource = new BasicDataSource();dataSource.setUrl("jdbc:mysql://localhost:3306/mydatabase");dataSource.setUsername("root");dataSource.setPassword("password");dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");// 连接池配置dataSource.setInitialSize(5); // 初始连接数dataSource.setMaxTotal(20); // 最大连接数dataSource.setMaxIdle(10); // 最大空闲连接dataSource.setMinIdle(5); // 最小空闲连接dataSource.setMaxWaitMillis(30000); // 获取连接超时时间// 连接验证配置dataSource.setValidationQuery("SELECT 1");dataSource.setTestOnBorrow(true);dataSource.setTestWhileIdle(true);dataSource.setTimeBetweenEvictionRunsMillis(60000); // 60秒检测一次}public static Connection getConnection() throws SQLException {return dataSource.getConnection();}
}// 使用配置文件的版本
public class DBCP2WithProperties {public static DataSource createDataSourceFromProperties() {Properties props = new Properties();try (InputStream input = DBCP2WithProperties.class.getClassLoader().getResourceAsStream("dbcp.properties")) {props.load(input);} catch (IOException e) {throw new RuntimeException("无法加载数据库配置", e);}BasicDataSource dataSource = new BasicDataSource();dataSource.setDriverClassName(props.getProperty("driverClassName"));dataSource.setUrl(props.getProperty("url"));dataSource.setUsername(props.getProperty("username"));dataSource.setPassword(props.getProperty("password"));// 其他配置...return dataSource;}
}
2.3 C3P0
import com.mchange.v2.c3p0.ComboPooledDataSource;import javax.sql.DataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;public class C3P0Example {private static ComboPooledDataSource dataSource;static {try {dataSource = new ComboPooledDataSource();dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydatabase");dataSource.setUser("root");dataSource.setPassword("password");// 连接池配置dataSource.setInitialPoolSize(5);dataSource.setMinPoolSize(5);dataSource.setMaxPoolSize(20);dataSource.setAcquireIncrement(5);dataSource.setMaxIdleTime(1800); // 30分钟// 连接测试dataSource.setTestConnectionOnCheckin(true);dataSource.setTestConnectionOnCheckout(false);dataSource.setPreferredTestQuery("SELECT 1");} catch (PropertyVetoException e) {throw new RuntimeException("C3P0配置失败", e);}}public static Connection getConnection() throws SQLException {return dataSource.getConnection();}
}
3. MyBatis 集成方式
3.1 MyBatis 内置连接池
<!-- mybatis-config.xml -->
<configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mydatabase"/><property name="username" value="root"/><property name="password" value="password"/><!-- MyBatis 内置连接池配置 --><property name="poolMaximumActiveConnections" value="20"/><property name="poolMaximumIdleConnections" value="10"/><property name="poolMaximumCheckoutTime" value="20000"/><property name="poolTimeToWait" value="20000"/><property name="poolPingEnabled" value="true"/><property name="poolPingQuery" value="SELECT 1"/><property name="poolPingConnectionsNotUsedFor" value="3600000"/></dataSource></environment></environments>
</configuration>
// 通过 MyBatis 获取 Connection
public class MyBatisConnectionExample {public static Connection getConnectionFromMyBatis() throws SQLException {// 1. 创建 SqlSessionFactoryString resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 2. 获取 SqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();// 3. 从 SqlSession 获取 Connectionreturn sqlSession.getConnection();}// 推荐方式:让 MyBatis 管理连接public void letMyBatisManageConnection() {SqlSession sqlSession = null;try {SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();sqlSession = sqlSessionFactory.openSession();// 获取 Mapper 执行操作,MyBatis 自动管理连接UserMapper userMapper = sqlSession.getMapper(UserMapper.class);User user = userMapper.selectById(1L);sqlSession.commit(); // 提交事务} catch (Exception e) {if (sqlSession != null) {sqlSession.rollback();}throw e;} finally {if (sqlSession != null) {sqlSession.close(); // 自动关闭连接}}}
}
3.2 MyBatis 集成第三方连接池
<!-- 使用 HikariCP 作为 MyBatis 数据源 -->
<configuration><properties resource="db.properties"/><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="com.example.MyBatisHikariDataSourceFactory"><!-- HikariCP 配置 --><property name="jdbcUrl" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/><property name="driverClassName" value="${jdbc.driver}"/><property name="maximumPoolSize" value="20"/><property name="minimumIdle" value="5"/></dataSource></environment></environments>
</configuration>
// 自定义 DataSourceFactory 集成 HikariCP
public class MyBatisHikariDataSourceFactory implements DataSourceFactory {private HikariDataSource dataSource;@Overridepublic void setProperties(Properties properties) {HikariConfig config = new HikariConfig(properties);dataSource = new HikariDataSource(config);}@Overridepublic DataSource getDataSource() {return dataSource;}
}
4. Spring 框架方式
4.1 Spring JDBC Template
@Configuration
@PropertySource("classpath:db.properties")
public class SpringJdbcConfig {@Value("${jdbc.driver}")private String driverClassName;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String username;@Value("${jdbc.password}")private String password;@Beanpublic DataSource dataSource() {HikariDataSource dataSource = new HikariDataSource();dataSource.setDriverClassName(driverClassName);dataSource.setJdbcUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);dataSource.setMaximumPoolSize(20);return dataSource;}@Beanpublic JdbcTemplate jdbcTemplate(DataSource dataSource) {return new JdbcTemplate(dataSource);}@Beanpublic TransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}
}// 使用方式
@Service
public class UserService {@Autowiredprivate JdbcTemplate jdbcTemplate;@Autowiredprivate DataSource dataSource;// 方式1:使用 JdbcTemplate(推荐)public List<User> getUsers() {return jdbcTemplate.query("SELECT * FROM users", (rs, rowNum) -> {User user = new User();user.setId(rs.getLong("id"));user.setUsername(rs.getString("username"));return user;});}// 方式2:直接从 DataSource 获取 Connectionpublic void useDirectConnection() {try (Connection conn = dataSource.getConnection();PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM users");ResultSet rs = pstmt.executeQuery()) {while (rs.next()) {// 处理结果}} catch (SQLException e) {throw new RuntimeException("数据库操作失败", e);}}
}
4.2 Spring Boot 自动配置
# application.yml
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mydatabaseusername: rootpassword: passwordtype: com.zaxxer.hikari.HikariDataSourcehikari:maximum-pool-size: 20minimum-idle: 5connection-timeout: 30000idle-timeout: 600000max-lifetime: 1800000
// Spring Boot 中使用
@Service
@Transactional
public class UserService {@Autowiredprivate DataSource dataSource; // Spring Boot 自动配置的 DataSource@Autowiredprivate JdbcTemplate jdbcTemplate; // Spring Boot 自动配置的 JdbcTemplatepublic void businessMethod() {// 使用 JdbcTemplatejdbcTemplate.update("UPDATE users SET email = ? WHERE id = ?", "new@example.com", 1L);// 或者直接获取 Connection(不推荐,让 Spring 管理事务)try (Connection conn = DataSourceUtils.getConnection(dataSource)) {// 使用连接,Spring 会管理事务// ...} catch (SQLException e) {throw new RuntimeException("数据库操作失败", e);}}
}
5. JNDI 方式(Java EE 环境)
5.1 配置 JNDI 数据源
<!-- Tomcat context.xml -->
<Context><Resource name="jdbc/MyDB"auth="Container"type="javax.sql.DataSource"driverClassName="com.mysql.cj.jdbc.Driver"url="jdbc:mysql://localhost:3306/mydatabase"username="root"password="password"maxTotal="20"maxIdle="10"maxWaitMillis="10000"/>
</Context><!-- 或者在 web.xml 中配置 -->
<resource-ref><description>MySQL Datasource</description><res-ref-name>jdbc/MyDB</res-ref-name><res-type>javax.sql.DataSource</res-type><res-auth>Container</res-auth>
</resource-ref>
5.2 从 JNDI 获取连接
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;public class JNDIExample {public static Connection getJNDIConnection() throws SQLException, NamingException {Context initContext = new InitialContext();Context envContext = (Context) initContext.lookup("java:comp/env");DataSource dataSource = (DataSource) envContext.lookup("jdbc/MyDB");return dataSource.getConnection();}// 简化版本(在某些容器中)public static Connection getJNDIConnectionSimple() throws SQLException, NamingException {Context context = new InitialContext();DataSource dataSource = (DataSource) context.lookup("java:comp/env/jdbc/MyDB");return dataSource.getConnection();}
}
6. 工具类封装
6.1 通用数据库工具类
public class DBUtil {private static DataSource dataSource;private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();static {initDataSource();}private static void initDataSource() {// 根据环境选择数据源配置if (isProduction()) {dataSource = createHikariDataSource();} else {dataSource = createDevDataSource();}}private static HikariDataSource createHikariDataSource() {HikariConfig config = new HikariConfig();config.setJdbcUrl(getProperty("jdbc.url"));config.setUsername(getProperty("jdbc.username"));config.setPassword(getProperty("jdbc.password"));config.setMaximumPoolSize(20);return new HikariDataSource(config);}private static BasicDataSource createDevDataSource() {BasicDataSource ds = new BasicDataSource();ds.setUrl(getProperty("jdbc.url"));ds.setUsername(getProperty("jdbc.username"));ds.setPassword(getProperty("jdbc.password"));ds.setInitialSize(5);ds.setMaxTotal(10);return ds;}/*** 获取数据库连接*/public static Connection getConnection() throws SQLException {// 检查当前线程是否已有连接(用于事务)Connection conn = threadLocal.get();if (conn != null && !conn.isClosed()) {return conn;}// 从数据源获取新连接conn = dataSource.getConnection();return conn;}/*** 开启事务(绑定连接到当前线程)*/public static void beginTransaction() throws SQLException {Connection conn = threadLocal.get();if (conn != null) {throw new SQLException("事务已开启");}conn = getConnection();conn.setAutoCommit(false);threadLocal.set(conn);}/*** 提交事务*/public static void commitTransaction() throws SQLException {Connection conn = threadLocal.get();if (conn == null) {throw new SQLException("没有开启的事务");}conn.commit();conn.close();threadLocal.remove();}/*** 回滚事务*/public static void rollbackTransaction() throws SQLException {Connection conn = threadLocal.get();if (conn == null) {throw new SQLException("没有开启的事务");}conn.rollback();conn.close();threadLocal.remove();}/*** 释放连接(非事务情况下使用)*/public static void releaseConnection(Connection conn) throws SQLException {Connection threadConn = threadLocal.get();// 如果不是事务连接,可以关闭if (threadConn == null || threadConn != conn) {if (conn != null && !conn.isClosed()) {conn.close();}}}private static String getProperty(String key) {// 从配置文件读取属性return System.getProperty(key);}private static boolean isProduction() {return "prod".equals(System.getProperty("app.env"));}
}
7. 性能对比与选择建议
7.1 各种方式对比
| 方式 | 性能 | 易用性 | 功能完整性 | 适用场景 |
|---|---|---|---|---|
| DriverManager | 低 | 简单 | 基础 | 测试、简单应用 |
| HikariCP | 非常高 | 中等 | 完整 | 生产环境首选 |
| DBCP2 | 高 | 中等 | 完整 | 传统项目 |
| C3P0 | 中等 | 复杂 | 完整 | 老项目维护 |
| MyBatis 内置 | 中等 | 简单 | 基础 | 小型项目 |
| Spring 管理 | 高 | 简单 | 完整 | Spring 项目 |
| JNDI | 高 | 复杂 | 完整 | Java EE 环境 |
7.2 选择建议
- 新项目:Spring Boot + HikariCP
- 传统 Spring 项目:Spring + HikariCP/DBCP2
- Java EE 项目:JNDI 数据源
- 简单测试:DriverManager
- MyBatis 项目:MyBatis + HikariCP
7.3 最佳实践
// 生产环境推荐配置
public class ProductionDataSourceConfig {@Bean@ConfigurationProperties(prefix = "spring.datasource.hikari")public DataSource dataSource() {return DataSourceBuilder.create().type(HikariDataSource.class).build();}// 使用连接时的最佳实践@Service@Transactional // 让Spring管理事务public class BusinessService {@Autowiredprivate JdbcTemplate jdbcTemplate; // 推荐使用@Autowiredprivate DataSource dataSource;public void businessMethod() {// 方式1:使用JdbcTemplate(首选)jdbcTemplate.update("SQL语句", params);// 方式2:需要复杂操作时获取连接jdbcTemplate.execute((ConnectionCallback<Void>) conn -> {// 使用连接执行复杂操作// Spring会自动管理事务return null;});}}
}
通过理解这些获取 Connection 对象的方式,你可以根据项目需求选择最合适的方案。在生产环境中,推荐使用连接池(特别是 HikariCP)结合框架的事务管理,以获得最佳性能和可靠性。
