第5章:数据访问层

5.1 Spring Data JPA使用

文字讲解

Spring Data JPA 是Spring Data项目的一部分,旨在极大地简化JPA(Java Persistence API)的使用。它通过提供基于Repository接口的编程模型,让我们无需编写任何实现代码就能完成大多数数据访问操作。

核心概念:

  • Entity:一个使用@Entity注解的普通Java对象(POJO),它映射到数据库中的一张表。
  • Repository:一个接口,我们通过继承Spring Data JPA提供的JpaRepositoryCrudRepository来获得丰富的CRUD(创建、读取、更新、删除)和分页查询方法。
  • 查询方法(Query Methods):Spring Data JPA最强大的功能之一。我们只需在Repository接口中按照特定规则定义方法名,框架就会自动为我们生成SQL查询。例如,findByEmail(String email)会自动生成SELECT * FROM user WHERE email = ?
代码示例

1. 添加依赖
pom.xml中添加spring-boot-starter-data-jpa和数据库驱动(如H2)。

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
    <groupId>com.h2databasegroupId>
    <artifactId>h2artifactId>
    <scope>runtimescope>
dependency>

2. 配置数据源
application.properties中配置数据库连接信息。

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

3. 创建Entity

import javax.persistence.*;

@Entity
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String firstName;
    private String lastName;
    // Getters and Setters
}

4. 创建Repository接口

import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;

public interface CustomerRepository extends JpaRepository<Customer, Long> {
    List<Customer> findByLastName(String lastName);
}

5. 使用Repository

@Service
public class CustomerService {
    @Autowired
    private CustomerRepository repository;

    public void demonstrateJpa() {
        // 保存客户
        repository.save(new Customer("Jack", "Bauer"));
        repository.save(new Customer("Chloe", "O'Brian"));

        // 按姓氏查询
        List<Customer> bauers = repository.findByLastName("Bauer");
        bauers.forEach(System.out::println);
    }
}

5.2 MyBatis集成

文字讲解

MyBatis 是一个优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。它可以使用简单的XML或注解来配置和映射原始类型、接口和Java POJO(Plain Old Java Objects)为数据库中的记录。

与JPA不同,MyBatis将开发者从ORM的复杂性中解放出来,给予了完全控制SQL的能力,因此在处理复杂查询和数据库优化时更具灵活性。

代码示例

1. 添加依赖
使用MyBatis官方提供的Spring Boot Starter mybatis-spring-boot-starter

<dependency>
    <groupId>org.mybatis.spring.bootgroupId>
    <artifactId>mybatis-spring-boot-starterartifactId>
    <version>2.2.2version>
dependency>

2. 配置MyBatis
application.properties中指定Mapper XML文件的位置。

mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.configuration.map-underscore-to-camel-case=true

3. 创建Model和Mapper接口

// Model
public class City {
    private Long id;
    private String name;
    private String state;
    // Getters and Setters
}

// Mapper接口
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface CityMapper {
    @Select("SELECT * FROM city WHERE state = #{state}")
    City findByState(String state);
}

4. 创建Mapper XML文件 (可选,用于复杂SQL)
resources/mapper/CityMapper.xml

DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.CityMapper">
    <select id="findByName" resultType="com.example.demo.model.City">
        SELECT * FROM city WHERE name = #{name}
    select>
mapper>

5. 使用Mapper

@RestController
public class CityController {
    @Autowired
    private CityMapper cityMapper;

    @GetMapping("/city/{state}")
    public City getCityByState(@PathVariable String state) {
        return cityMapper.findByState(state);
    }
}

5.3 多数据源配置

文字讲解

在某些业务场景下,一个应用可能需要连接多个不同的数据库,例如读写分离、或连接不同业务的数据库。Spring Boot本身不直接支持多数据源的自动配置,但我们可以通过手动配置来实现。

基本思路:

  1. 在配置文件中定义多个数据源的连接信息。
  2. 创建多个DataSource Bean。
  3. 为每个数据源分别配置SqlSessionFactory(MyBatis)或EntityManagerFactory(JPA)。
  4. 为每个数据源配置TransactionManager(事务管理器)。
  5. 使用@Primary注解指定一个主数据源,当不明确指定时,默认使用主数据源。
代码示例 (以MyBatis为例)

1. 配置文件

# Primary DataSource
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/db1
spring.datasource.primary.username=root
spring.datasource.primary.password=password

# Secondary DataSource
spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/db2
spring.datasource.secondary.username=root
spring.datasource.secondary.password=password

2. 数据源配置类

@Configuration
public class DataSourceConfig {

    @Primary
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }
}

3. 为每个数据源配置MyBatis
这部分配置较为复杂,需要为每个数据源创建SqlSessionFactorySqlSessionTemplate,并使用@MapperScan指定每个Mapper接口属于哪个数据源。通常会为每个数据源创建一个单独的配置类。


5.4 数据库连接池配置

文字讲解

数据库连接是一种昂贵的资源。数据库连接池通过维护一组可重用的数据库连接,避免了为每个请求都创建和销毁连接的开销,从而显著提升应用性能。

Spring Boot 2.x 默认使用 HikariCP 作为数据库连接池,它以其高性能和稳定性而闻名。我们几乎不需要任何配置就可以获得一个表现优异的连接池。当然,也可以通过application.properties对其进行微调。

常用配置项:

  • spring.datasource.hikari.minimum-idle:最小空闲连接数。
  • spring.datasource.hikari.maximum-pool-size:最大连接数。
  • spring.datasource.hikari.idle-timeout:空闲连接的超时时间(毫秒),超时后连接会被释放。
  • spring.datasource.hikari.connection-timeout:获取连接的超时时间(毫秒)。
  • spring.datasource.hikari.max-lifetime:连接的最大生命周期(毫秒),到期后连接会被关闭。
代码示例

application.properties中进行配置:

# HikariCP Configuration
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.pool-name=MyWebAppPool
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.idle-timeout=600000

5.5 事务管理

文字讲解

事务是一组必须作为一个整体成功或失败的数据库操作。Spring通过@Transactional注解提供了强大的声明式事务管理功能,使得我们可以将业务代码与事务管理代码完全解耦。

核心概念:

  • ACID:事务的四个基本特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
  • @Transactional:可以应用在类或方法上。当应用在类上时,该类所有public方法都将拥有该注解的事务属性。
  • 传播行为(Propagation Behavior):定义了当一个事务方法被另一个事务方法调用时,事务应该如何表现。最常见的REQUIRED表示如果当前存在事务,则加入该事务;如果不存在,则创建一个新事务。
  • 隔离级别(Isolation Level):定义了一个事务可能受其他并发事务影响的程度。Spring支持数据库提供的所有标准隔离级别。
  • 回滚规则(Rollback Rules):默认情况下,Spring只在抛出RuntimeExceptionError时才会回滚事务。对于受检异常(Checked Exception),它不会回滚。我们可以通过rollbackFor属性来改变这个行为。
代码示例
@Service
public class BankService {

    @Autowired
    private AccountRepository accountRepository;

    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Exception.class)
    public void transfer(Long fromAccountId, Long toAccountId, double amount) throws Exception {
        // 1. 扣款
        Account fromAccount = accountRepository.findById(fromAccountId).orElseThrow();
        if (fromAccount.getBalance() < amount) {
            throw new Exception("Insufficient balance");
        }
        fromAccount.setBalance(fromAccount.getBalance() - amount);
        accountRepository.save(fromAccount);

        // 模拟异常
        if (amount > 1000) {
            throw new RuntimeException("Transfer failed!");
        }

        // 2. 存款
        Account toAccount = accountRepository.findById(toAccountId).orElseThrow();
        toAccount.setBalance(toAccount.getBalance() + amount);
        accountRepository.save(toAccount);
    }
}

代码解释:

  • transfer方法被@Transactional注解。这意味着整个方法将在一个事务中执行。
  • 如果在方法执行过程中(例如在模拟异常处)抛出了RuntimeException,所有已经完成的数据库操作(如扣款)都将被回滚,数据库将恢复到方法调用之前的状态。
  • rollbackFor = Exception.class指定了即使是受检异常也会触发回滚。

本章总结

本章我们掌握了在Spring Boot中进行数据访问的关键技术。我们学习了如何使用Spring Data JPA快速实现数据操作,以及如何通过MyBatis获得对SQL的完全控制。此外,我们还探讨了多数据源配置、连接池优化和声明式事务管理等企业级应用中必不可少的实践。有了这些知识,我们就可以构建出高效、健壮的数据持久层。

你可能感兴趣的:(微服务,后端,架构)