數據源是持久層的心臟。本文系統覆蓋 JNDI 容器級配置、Properties 外部化配置、主流連接池 (Druid/HikariCP/C3P0) 整合、SessionFactory 構建、聲明式事務管理 五大塊,提供生產級 XML 與 Java Config 雙版本。
優勢:應用與數據源解耦、容器統一連接池監控、多應用共享池、密碼不落應用代碼。
<!-- Tomcat context.xml (全局) 或 META-INF/context.xml (單應用) -->
<Resource name="jdbc/myDS" auth="Container" type="javax.sql.DataSource"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://db:3306/myapp?useSSL=false&serverTimezone=UTC"
username="app_user" password="secret"
maxTotal="50" maxIdle="20" maxWaitMillis="10000"
validationQuery="SELECT 1" testOnBorrow="true" />
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/myDS"/>
<property name="proxyInterface" value="javax.sql.DataSource"/>
<property name="lookupOnStartup" value="false"/> <!-- 延遲查找,啟動快 -->
</bean>
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/myapp?useSSL=false&serverTimezone=UTC jdbc.username=dev jdbc.password=dev123 jdbc.pool.maxActive=20 jdbc.pool.maxIdle=10 jdbc.pool.initialSize=5
<context:property-placeholder location="classpath:jdbc.properties" ignore-unresolvable="true"/>
<!-- Druid (推薦生產) -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="${jdbc.pool.maxActive}"/>
<property name="minIdle" value="${jdbc.pool.maxIdle}"/>
<property name="initialSize" value="${jdbc.pool.initialSize}"/>
<property name="validationQuery" value="SELECT 1"/>
<property name="testOnBorrow" value="true"/>
<property name="filters" value="stat,wall,log4j2"/> <!-- 監控、防 SQL 注入、日誌 -->
</bean>
<!-- HikariCP (Spring Boot 2+ 默認,性能最強) -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maximumPoolSize" value="${jdbc.pool.maxActive}"/>
<property name="minimumIdle" value="${jdbc.pool.maxIdle}"/>
</bean>
<!-- MyBatis SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
<property name="typeAliasesPackage" value="com.app.domain"/>
<property name="configurationProperties">
<props>
<prop key="mapUnderscoreToCamelCase">true</prop>
<prop key="cacheEnabled">true</prop>
</props>
</property>
</bean>
<!-- Mapper 掃描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.app.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
<!-- 事務管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- TX Advice 定義規則 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="select*" read-only="true"/>
<tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="*" propagation="REQUIRED" rollback-for="Exception"/> <!-- 兜底 -->
</tx:attributes>
</tx:advice>
<!-- AOP 切面織入 -->
<aop:config>
<aop:pointcut id="serviceMethods" expression="execution(* com.app.service..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>
</aop:config>
<!-- 或註解驅動 (推薦現代項目) -->
<tx:annotation-driven transaction-manager="txManager"/>
@Configuration
@MapperScan("com.app.mapper")
@EnableTransactionManagement
public class DataSourceConfig {
@Value("${jdbc.url}") String url;
@Value("${jdbc.username}") String user;
@Value("${jdbc.password}") String pwd;
@Value("${jdbc.driver}") String driver;
@Bean(destroyMethod = "close")
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(url); ds.setUsername(user); ds.setPassword(pwd); ds.setDriverClassName(driver);
ds.setMaximumPoolSize(20); ds.setMinimumIdle(5);
return ds;
}
@Bean public SqlSessionFactory sqlSessionFactory(DataSource ds) throws Exception {
SqlSessionFactoryBean fb = new SqlSessionFactoryBean();
fb.setDataSource(ds);
fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
return fb.getObject();
}
@Bean public DataSourceTransactionManager txManager(DataSource ds) {
return new DataSourceTransactionManager(ds);
}
}
| 連接池 | 優勢 | 劣勢 | 推薦場景 |
|---|---|---|---|
| HikariCP | 性能最強、代碼極簡、Spring Boot 默認 | 功能相對精簡、監控需配合 Micrometer | 高併發、微服務、現代 Spring Boot 項目 |
| Druid | 監控極強 (SQL統計、防火墻、日誌)、阿里生態標配 | 代碼較重、部分功能依賴私有 API | 傳統單體、需詳細 SQL 監控/審計、國產化項目 |
| C3P0 | 老牌穩定、Hibernate 歷史默認 | 性能一般、維護緩慢、配置繁瑣 | 維護舊項目、Hibernate 原生項目 |
| Tomcat/JNDI | 容器級共享、運維統一管理、應用零配置 | 耦合容器、調試不便、開發環境麻煩 | 生產集群、多應用共享數據庫、嚴格運維規範 |
數據源配置核心原則:生產優先 JNDI 或 HikariCP,監控必上 Druid/Actuator,事務規則按業務語義定義,配置外部化,密碼加密。