SpringBoot 数据库框架

SpringBoot连接数据库基本都通过数据库框架,主要框架分为纯底层框架,半ORM框架,ORM框架。

ORM框架:ORM(Object-Relational Mapping)框架是一种编程技术,用于在对象模型和关系数据库之间建立映射,从而实现数据的持久化存储和操作。具体来说,ORM框架将数据库中的表(table)映射为编程语言中的类(class),表中的记录(record)映射为类的实例(object),字段(field)映射为对象的属性(attribute)。

Java 数据库框架

JDBC(Java Database Connectivity)

JDBC(Java Database Connectivity,Java 数据库连接)是 Java 官方设计的一套 关系型数据库访问标准 API

设计目标:统一访问标准,屏蔽关系型数据库差异。

  • 关系型数据库:MySQL、PostgreSQL、Oracle、SQL Server、DB2、SQLite、MariaDB
  • 嵌入式数据库:H2、HSQLDB、Derby
  • 云数据库:Amazon RDS、Google Cloud SQL、Azure SQL Database

注:部分非关系型数据库如MongoDB虽然侧面支持JDBC驱动但仍然推荐使用原生驱动,redis也需要使用原生驱动。

  1. 加载驱动:通过Class.forName()或DriverManager注册数据库驱动,加载JDBC驱动类。
  2. 建立连接:使用DriverManager.getConnection(url, user, password)创建与数据库的连接,生成Connection对象。
  3. 创建语句:通过Connection创建Statement或PreparedStatement对象,用于执行SQL查询。
  4. 执行SQL:调用executeQuery()(查询)或executeUpdate()(增删改)发送SQL语句到数据库,获取结果。
  5. 处理结果:对于查询,处理ResultSet对象,提取数据;对于更新,返回受影响行数。
  6. 释放资源:关闭ResultSet、Statement和Connection对象,释放数据库和JDBC资源。

ORM(Object Relational Mapping)

面向对象编程语言中的对象 映射为 关系型数据库中的数据表 的技术。

  • 将数据库表结构映射为 Java 对象
  • 提供 CRUD(增删改查)接口,自动生成 SQL
  • 提供查询语法(如 JPQL、HQL)
  • 提供事务、缓存、懒加载等扩展功能

流程图:

1
Java 对象 → ORM 映射 → SQL 生成 → JDBC 执行 → 结果映射回 Java 对象

详细流程:

  1. ORM 扫描实体类(@Entity)读取注解或 XML 映射信息。
  2. ORM 根据对象操作(save、find、update、delete)动态生成 SQL。
  3. ORM 调用 JDBC 执行 SQL。
  4. ORM 将 ResultSet 转换为 Java 对象,自动封装数据。
框架 特点 适用场景
Hibernate 功能最全,自动 SQL,支持缓存 复杂对象关系,高度自动化需求
JPA(规范) 标准接口(Hibernate 常用实现) 需要标准化 ORM 接口,Spring 官方推荐
MyBatis 半自动映射,手写 SQL SQL 灵活控制,复杂查询
Spring Data JPA 基于 JPA,进一步封装 简单快速开发,配合 Spring Boot 最佳

SpringBoot数据库框架

在使用 Spring Boot 开发应用时,选择合适的数据库框架(主要是 ORM 框架)对项目的开发效率和性能至关重要。

1.Spring Data JPA(ORM框架)

Spring Data JPA 是 Spring Boot 的默认 ORM 框架,基于 Hibernate 实现。它提供了简单易用的接口和注解,可以极大减少样板代码。

程序员不需要编写SQL语句,框架已经高度封装CRUD,调用对应的Java方法即可执行SQL语句。

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

2. MyBatis(半ORM框架)

MyBatis 是一个轻量级的持久层框架,提供 SQL 映射功能,开发者可以直接编写 SQL 语句。

MyBatis允许程序员自行编写SQL语句,并通过@Mapper将编写的SQL封装到Java对象并注册为Spring的Bean中,程序员可以通过@Autowired获取Java对象并调用方法执行对应的SQL语句。

1
2
3
4
5
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency

3.Spring JDBC(纯底层框架)

Spring JDBC 是 Spring 提供的最底层的 JDBC 封装,适合直接操作数据库的场景。

所有的SQL都需要程序员手动编写,不支持ORM的功能。

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

4. Hibernate(ORM框架)

Hibernate 是一个功能强大的 ORM 框架,Spring Data JPA 底层就是基于 Hibernate 实现的。如果你需要直接使用 Hibernate,可以绕过 Spring Data JPA。这里就不做详细介绍了。

框架 推荐场景 学习曲线 性能优化空间
Spring Data JPA 中小型项目,快速开发 中等
MyBatis 性能敏感型项目,复杂查询 中等
Hibernate 需要高级 ORM 特性 中等
JOOQ 类型安全 SQL 查询,复杂数据库操作 中等
Spring JDBC 极致性能,轻量级应用 最高

对于有经验的程序员,Mybatis框架是一个很好的选择,程序员可以自定义SQL语句用于优化性能,又可以通过ORM框架将SQL语句封装到Java对象中。

如果对数据库不甚了解,可以选择Spring Data JPA通过Java对象直接执行SQL语句,不需要了解SQL语句。

SpringBoot数据库连接原理

SpringBoot需要创建一个DataSource的bean类,用于告诉框架如何连接数据库,而且其中也可以包含连接池。

DataSource 是一个数据库连接的统一管理接口,用于获取数据库连接。

原理:

  • DataSource数据库连接池的门面
  • 程序通过 DataSource 向数据库请求连接(Connection)。
  • 连接池的管理细节(如 HikariCP、Druid、Tomcat Pool)都被 DataSource 封装起来,对上层透明。
  • 数据库的驱动:DataSource通过DriverManager利用Java SPI机制(ServiceLoader.load(Driver.class))加载类路径中的 JDBC 驱动(基于 META-INF/services/java.sql.Driver 配置)。

Mybatis数据库框架

添加依赖:

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>

加载DataSource

当类路径中存在javax.sql.DataSource和嵌入式数据库相关的类,会激活自动配置类DataSourceAutoConfiguration用于配置DataSource的Bean。

其中DataSourceAutoConfiguration存在于org.springframework.boot:spring-boot-autoconfigureMETA-INF/spring/org.sringframework.boot.autoconfigure.AutoConfiguration.imports

SpringBoot启动过程中,在处理注解@EnableAutoConfiguration时,会在@Import(AutoConfigurationImportSelector.class)导入AutoConfigurationImportSelector,该类会搜索classpath内的所有/META-INF/spring/xxx.imports内的自动配置类。

注解的处理逻辑存在于AbstractApplicationContext.refresh.invokeBeanFactoryPostProcessors(beanFactory)中。

org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration:

1
2
3
4
5
6
7
8
9
@AutoConfiguration(before = SqlInitializationAutoConfiguration.class)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceCheckpointRestoreConfiguration.class })
public class DataSourceAutoConfiguration {
//......
}

触发条件

@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })只有Spring中同时具有DataSource.classEmbeddedDatabaseType.class才会加载该配置类。其中DataSource.class是JDK标准库的一部分所以条件横为真。

  • 当引入mysql-connector-j会提供com.mysql.cj.jdbc.MysqlDataSource和数据库驱动。

  • 当引入mybatis-spring-boot-starter,包含spring-jdbc依赖,其中包含了/org/springframework/spring-jdbc/6.2.3/spring-jdbc-6.2.3.jar!/org/springframework/jdbc/datasource/embedded/EmbeddedDatabaseType.class

    1
    2
    3
    4
    5
    6
    7
    mybatis-spring-boot-starter
    ├── mybatis-spring-boot-autoconfigure
    ├── mybatis
    ├── mybatis-spring
    └── spring-boot-starter-jdbc
    ├── spring-jdbc
    └── HikariCP(默认连接池)

javax.sql.DataSource:由 JDK 提供,总是存在(Java 6 及以上版本)。

EmbeddedDatabaseType:由 spring-jdbc 提供,通过 mybatis-spring-boot-starter 间接引入。

读取数据库配置信息

@EnableConfigurationProperties(DataSourceProperties.class),该注解会将配置中的数据库信息封装到DataSourceProperties.class,并加载为Spring Bean。

加载数据池

@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceCheckpointRestoreConfiguration.class })通过DataSourcePoolMetadataProvidersConfiguration.class配置数据池。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
@Configuration(proxyBeanMethods = false)
public class DataSourcePoolMetadataProvidersConfiguration {

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
static class TomcatDataSourcePoolMetadataProviderConfiguration {

@Bean
DataSourcePoolMetadataProvider tomcatPoolDataSourceMetadataProvider() {
return (dataSource) -> {
org.apache.tomcat.jdbc.pool.DataSource tomcatDataSource = DataSourceUnwrapper.unwrap(dataSource,
ConnectionPoolMBean.class, org.apache.tomcat.jdbc.pool.DataSource.class);
if (tomcatDataSource != null) {
return new TomcatDataSourcePoolMetadata(tomcatDataSource);
}
return null;
};
}
}

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class)
static class HikariPoolDataSourceMetadataProviderConfiguration {

@Bean
DataSourcePoolMetadataProvider hikariPoolDataSourceMetadataProvider() {
return (dataSource) -> {
HikariDataSource hikariDataSource = DataSourceUnwrapper.unwrap(dataSource, HikariConfigMXBean.class,
HikariDataSource.class);
if (hikariDataSource != null) {
return new HikariDataSourcePoolMetadata(hikariDataSource);
}
return null;
};
}

}

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(BasicDataSource.class)
static class CommonsDbcp2PoolDataSourceMetadataProviderConfiguration {

@Bean
DataSourcePoolMetadataProvider commonsDbcp2PoolDataSourceMetadataProvider() {
return (dataSource) -> {
BasicDataSource dbcpDataSource = DataSourceUnwrapper.unwrap(dataSource, BasicDataSourceMXBean.class,
BasicDataSource.class);
if (dbcpDataSource != null) {
return new CommonsDbcp2DataSourcePoolMetadata(dbcpDataSource);
}
return null;
};
}

}

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ PoolDataSource.class, OracleConnection.class })
static class OracleUcpPoolDataSourceMetadataProviderConfiguration {

@Bean
DataSourcePoolMetadataProvider oracleUcpPoolDataSourceMetadataProvider() {
return (dataSource) -> {
PoolDataSource ucpDataSource = DataSourceUnwrapper.unwrap(dataSource, PoolDataSource.class);
if (ucpDataSource != null) {
return new OracleUcpDataSourcePoolMetadata(ucpDataSource);
}
return null;
};
}
}
}

通过源码可以得出,提供了4种数据库链接池:

HikariCP(默认):

  • 类:com.zaxxer.hikari.HikariDataSource
  • 依赖:com.zaxxer:HikariCP
  • 默认引入:通过 spring-boot-starter-jdbcspring-boot-starter-data-jpa

Tomcat JDBC

  • 类:org.apache.tomcat.jdbc.pool.DataSource
  • 依赖:org.apache.tomcat:tomcat-jdbc
  • 使用条件:类路径中存在,且未引入 HikariCP。

Commons DBCP2

  • 类:org.apache.commons.dbcp2.BasicDataSource
  • 依赖:org.apache.commons:commons-dbcp2
  • 使用条件:类路径中存在,且未引入 HikariCP 或 Tomcat JDBC。

OracleUcp:

spring-boot-starter-jdbc中引入了HikariCP,所以Mybatis会自动配置HikariDataSourcePoolMetadata

可以通过配置文件指定数据池类型:

1
2
3
spring:
datasource:
type: org.apache.tomcat.jdbc.pool.DataSource

并加入依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<exclusions>
<exclusion>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</dependency>