Spring家族提供了一套完整且方便的访问数据库的方式。其中,Spring Framework为数据库访问提供了底层支持,Spring Data为数据库访问封装了Repository等实用工具,Spring Boot则简化了数据库组件的配置。

一、SpringData核心组件

Spring数据库访问的核心组件分别位于Spring FrameworkSpring DataSpring Boot模块中,每个模块中又含有若干独立的专用组件,如:

  • Spring Boot中的spring-boot-starter-data-jpaspring-boot-starter-data-jdbc
  • Spring Data中的spring-data-jpaspring-data-jdbc
  • Spring Framework中的spring-jdbcspring-ormspring-tx

它们的依赖关系如下图所示:

@startuml SpringData关键组件依赖图

title SpringData关键组件依赖图

' folder "Spring-Boot" {
    component [spring-boot-starter-data-jpa] as spring_boot_starter_data_jpa #LightPink
    component [spring-boot-starter-data-jdbc] as spring_boot_starter_data_jdbc #LightPink
    component [spring-boot-starter-jdbc] as spring_boot_starter_jdbc #LightPink
' }

' folder "Spring-Data" {
    component [spring-data-jpa] as spring_data_jpa #LightSkyBlue
    component [spring-data-jdbc] as spring_data_jdbc #LightSkyBlue
    component [spring-data-commons] as spring_data_commons #LightSkyBlue
    component [spring-data-relational] as spring_data_relational #LightSkyBlue
' }

' folder "Spring-Framework" {
    component [spring-jdbc] as spring_jdbc #LightGoldenRodYellow
    component [spring-orm] as spring_orm #LightGoldenRodYellow
    component [spring-tx] as spring_tx #LightGoldenRodYellow
' }

component [hibernate-core] as hibernate_core
component [jakarta.persistence-api] as jakarta.persistence_api
component [jakarta.transaction-api] as jakarta.transaction_api
component [HikariCP] as HikariCP

spring_boot_starter_data_jpa --> hibernate_core
spring_boot_starter_data_jpa --> jakarta.persistence_api
spring_boot_starter_data_jpa --> jakarta.transaction_api
spring_boot_starter_data_jpa --> spring_boot_starter_jdbc
spring_boot_starter_data_jpa --> spring_data_jpa

spring_boot_starter_jdbc --> spring_jdbc
spring_boot_starter_jdbc --> HikariCP

spring_data_jpa --> spring_data_commons
spring_data_jpa --> spring_orm
' spring_data_jpa --> spring_tx

spring_jdbc --> spring_tx
spring_orm --> spring_tx

spring_boot_starter_data_jdbc --> spring_boot_starter_jdbc
spring_boot_starter_data_jdbc --> spring_data_jdbc

spring_data_jdbc --> spring_data_commons
spring_data_jdbc --> spring_data_relational
spring_data_jdbc --> spring_jdbc
' spring_data_jdbc --> spring_tx
' spring_data_relational --> spring_data_commons
spring_data_relational -->spring_tx

@enduml

上图中,Spring Boot模块组件用浅粉色表示,Spring Data模块组件用浅蓝色表示,Spring Framework模块组件用浅黄色表示。

二、Spring数据库访问过程

下面将基于Spring Boot与Spring Data JPA,通过一段Demo演示Spring访问数据库的完整过程。

(一)定义数据源

首先,使用脚手架新建一个SpringBoot应用,并在resources修改application.yml文件,如下:

spring:
  h2:
    console:
      enabled: true # 开启h2控制台页面
      path: /h2 # 定义h2控制台页面访问地址
  datasource:
    url: jdbc:h2:mem:test # 定义h2数据库访问路径
  jpa:
    show-sql: true # 在日志中打印sql
    hibernate:
      ddl-auto: none # 禁用hibernate自动生成表结构

为了避免jpa自动生成表结构与resources下的schema.sql产生冲突,除了「ddl-auto: none」配置外,也可以使用「defer-datasource-initialization: true」配置。

这里使用了一个h2内存数据库,如有需要,也可以将datasource中url的数据库协议替换为其它数据库,以mysql为例,如下:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: luanrz
    password: luanrz

简单起见,后续将使用上述h2数据库配置。随后,在resources下增加DDL与DML:

DDLschema.sql

-- drop table person if exists;
create table person (
    id varchar(20) not null ,
    name varchar(20) null
);

DMLdata.sql

insert into person values('0001', '张三')

至此,数据源准备工作完成。

(二)定义数据库访问基础组件

Spring提供了多种访问数据库的方法,比较常见的有以下两种:

  • 位于spring-jdbc(Spring Framework)模块的JdbcTemplate
  • 位于spring-data-common(Spring Data)模块的CrudRepository

下面将以基于JPA的CrudRepository创建对应的定义实体类与访问接口。

实体类Person.java

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Person {
    @Id
    private String id;
    private String name;

    public Person(String id, String name) {
        this.id = id;
        this.name = name;
    }

    /**
     * Hibernate需要空参构造函数
     */
    public Person() {
    }

    /**
     * SpringMVC序列化工具jackson反序列化(请求解析)时需要set方法
     */
    public Person setId(String id) {
        this.id = id;
        return this;
    }

    /**
     * SpringMVC序列化工具jackson序列化(响应转换)时需要get方法
     */
    public String getId() {
        return id;
    }

    public Person setName(String name) {
        this.name = name;
        return this;
    }

    public String getName() {
        return name;
    }
}

访问接口PersonRepository

import org.springframework.data.repository.CrudRepository;

public interface PersonRepository extends CrudRepository<Person, String> {
}

通过以上代码,PersonRepository接口已经提供了基于Person实体的数据库CRUD功能,有关Repository的更多细节详见SpringDataJpa官方文档

(三)定义访问入口

最后,基于SpringMVC定义访问入口,来调用上述数据库访问基础组件。

访问控制器PersonController.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
public class PersonController {

    @Autowired
    private PersonRepository repository;

    @GetMapping( "/")
    public Person get(String id) {
        return repository.findById(id).orElseThrow(() -> new RuntimeException("empty"));
    }
    @PostMapping(value = "/")
    public Person add(@RequestBody Person person) {
        return repository.save(person);
    }

    @DeleteMapping("/")
    public void delete(String id) {
        repository.deleteById(id);
    }

}

为了代码完整性,这里将脚手架自动生成的Application也贴上。

入口类MyApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

(四)验证应用

启动应用后,在PostMan或其它Api工具中访问服务。访问参数如下:

### get person
GET http://localhost:8080?id=0001

### add person
POST http://localhost:8080
Content-Type: application/json

{
    "id": "0002",
    "name": "李四"
}

### delete person
DELETE http://localhost:8080?id=0002

或在终端使用curl访问:

curl --request GET \
     --url 'http://localhost:8080/?id=0001'

curl --request POST \
     --url http://localhost:8080/ \
     --header 'content-type: application/json' \
     --data '{"id": "0002","name": "李四"}'

curl --request DELETE \
     --url 'http://localhost:8080/?id=0002' 

调用上述接口之后,可以进入h2控制台页面(localhost:8080/h2/),查看数据结果是否符合预期。

三、Spring事务支持

Spring使用spring-tx组件提供事务支持。在前文SpringData核心组件的SpringData关键组件依赖图中,spring-tx位于整个SpringData依赖关系中最底层,换言之,spring-tx作为一个基础组件,为大多数上层组件(如:Spring Data JPA)提供了事务能力。

可以使用spring-tx中的@Transactional注解,为方法增加事务支持,如下所示:

package org.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.transaction.Transactional;

@RestController
public class PersonController {

    @Autowired
    private PersonRepository repository;

    @DeleteMapping("/")
    @Transactional
    public void delete(String id) {
        repository.deleteById(id);
        throw new RuntimeException("some exception happened");
    }
}

含有@Transactional注解的方法内部一旦出现异常,事务将执行回滚。有关事务的更多内容,详见SpringData事务官方文档

参考文档

  1. Spring Projects
  2. Spring Boot With H2 Database