4.1 Spring Data JPA概述

JPA简介

JPA(Java Persistence API)是Java EE平台的标准ORM规范,Spring Data JPA是Spring对JPA的封装和扩展。

核心概念: - Entity(实体):映射到数据库表的Java对象 - Repository(仓库):数据访问层接口 - EntityManager:JPA核心接口,管理实体生命周期 - JPQL:Java持久化查询语言

依赖配置

<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Starter Data JPA -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    
    <!-- H2数据库(测试用) -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

数据库配置

# application.properties

# MySQL数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/springboot_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# JPA配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect

# 连接池配置
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
spring.datasource.hikari.max-lifetime=1800000

4.2 实体类设计

基础实体类

@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "username", unique = true, nullable = false, length = 50)
    private String username;
    
    @Column(name = "email", unique = true, nullable = false)
    private String email;
    
    @Column(name = "password", nullable = false)
    private String password;
    
    @Column(name = "first_name", length = 50)
    private String firstName;
    
    @Column(name = "last_name", length = 50)
    private String lastName;
    
    @Column(name = "phone", length = 20)
    private String phone;
    
    @Enumerated(EnumType.STRING)
    @Column(name = "status")
    private UserStatus status = UserStatus.ACTIVE;
    
    @Column(name = "birth_date")
    private LocalDate birthDate;
    
    @Column(name = "created_at", nullable = false, updatable = false)
    @CreationTimestamp
    private LocalDateTime createdAt;
    
    @Column(name = "updated_at")
    @UpdateTimestamp
    private LocalDateTime updatedAt;
    
    @Version
    private Long version;
}

// 用户状态枚举
public enum UserStatus {
    ACTIVE, INACTIVE, SUSPENDED, DELETED
}

实体关系映射

// 用户实体(一对多关系)
@Entity
@Table(name = "users")
@Data
@EqualsAndHashCode(exclude = {"posts", "profile"})
@ToString(exclude = {"posts", "profile"})
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String username;
    private String email;
    
    // 一对多:一个用户有多篇文章
    @OneToMany(mappedBy = "author", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Post> posts = new ArrayList<>();
    
    // 一对一:一个用户有一个档案
    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private UserProfile profile;
    
    // 多对多:用户和角色的关系
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
        name = "user_roles",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles = new HashSet<>();
}

// 文章实体(多对一关系)
@Entity
@Table(name = "posts")
@Data
@EqualsAndHashCode(exclude = {"author", "comments", "tags"})
@ToString(exclude = {"author", "comments", "tags"})
public class Post {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false)
    private String title;
    
    @Column(columnDefinition = "TEXT")
    private String content;
    
    @Enumerated(EnumType.STRING)
    private PostStatus status = PostStatus.DRAFT;
    
    // 多对一:多篇文章属于一个用户
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "author_id", nullable = false)
    private User author;
    
    // 一对多:一篇文章有多个评论
    @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Comment> comments = new ArrayList<>();
    
    // 多对多:文章和标签的关系
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
        name = "post_tags",
        joinColumns = @JoinColumn(name = "post_id"),
        inverseJoinColumns = @JoinColumn(name = "tag_id")
    )
    private Set<Tag> tags = new HashSet<>();
    
    @CreationTimestamp
    private LocalDateTime createdAt;
    
    @UpdateTimestamp
    private LocalDateTime updatedAt;
}

// 用户档案实体(一对一关系)
@Entity
@Table(name = "user_profiles")
@Data
@EqualsAndHashCode(exclude = "user")
@ToString(exclude = "user")
public class UserProfile {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String bio;
    private String avatar;
    private String website;
    private String location;
    
    // 一对一:档案属于一个用户
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
}

// 角色实体
@Entity
@Table(name = "roles")
@Data
public class Role {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(unique = true, nullable = false)
    private String name;
    
    private String description;
    
    @ManyToMany(mappedBy = "roles")
    private Set<User> users = new HashSet<>();
}

// 标签实体
@Entity
@Table(name = "tags")
@Data
public class Tag {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(unique = true, nullable = false)
    private String name;
    
    private String color;
    
    @ManyToMany(mappedBy = "tags")
    private Set<Post> posts = new HashSet<>();
}

继承映射

// 基础实体类
@MappedSuperclass
@Data
public abstract class BaseEntity {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @CreationTimestamp
    @Column(name = "created_at", nullable = false, updatable = false)
    private LocalDateTime createdAt;
    
    @UpdateTimestamp
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;
    
    @Version
    private Long version;
}

// 继承基础实体
@Entity
@Table(name = "products")
@Data
@EqualsAndHashCode(callSuper = true)
public class Product extends BaseEntity {
    
    @Column(nullable = false)
    private String name;
    
    private String description;
    
    @Column(nullable = false)
    private BigDecimal price;
    
    private Integer stock;
}

// 表继承策略
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "account_type")
public abstract class Account {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String accountNumber;
    private BigDecimal balance;
}

@Entity
@DiscriminatorValue("SAVINGS")
public class SavingsAccount extends Account {
    private Double interestRate;
}

@Entity
@DiscriminatorValue("CHECKING")
public class CheckingAccount extends Account {
    private BigDecimal overdraftLimit;
}

4.3 Repository接口

基础Repository

// 用户Repository
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    
    // 方法名查询
    Optional<User> findByUsername(String username);
    Optional<User> findByEmail(String email);
    List<User> findByStatus(UserStatus status);
    List<User> findByFirstNameContaining(String firstName);
    List<User> findByCreatedAtBetween(LocalDateTime start, LocalDateTime end);
    
    // 复合查询
    List<User> findByFirstNameAndLastName(String firstName, String lastName);
    List<User> findByUsernameOrEmail(String username, String email);
    
    // 排序和分页
    List<User> findByStatusOrderByCreatedAtDesc(UserStatus status);
    Page<User> findByStatus(UserStatus status, Pageable pageable);
    
    // 存在性检查
    boolean existsByUsername(String username);
    boolean existsByEmail(String email);
    
    // 计数查询
    long countByStatus(UserStatus status);
    
    // 删除操作
    void deleteByStatus(UserStatus status);
    long deleteByCreatedAtBefore(LocalDateTime date);
}

自定义查询

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    
    // JPQL查询
    @Query("SELECT u FROM User u WHERE u.email = ?1")
    Optional<User> findByEmailJPQL(String email);
    
    @Query("SELECT u FROM User u WHERE u.firstName LIKE %:name% OR u.lastName LIKE %:name%")
    List<User> findByNameContaining(@Param("name") String name);
    
    @Query("SELECT u FROM User u JOIN u.roles r WHERE r.name = :roleName")
    List<User> findByRoleName(@Param("roleName") String roleName);
    
    // 原生SQL查询
    @Query(value = "SELECT * FROM users WHERE DATE(created_at) = CURDATE()", nativeQuery = true)
    List<User> findUsersCreatedToday();
    
    @Query(value = "SELECT u.*, COUNT(p.id) as post_count " +
           "FROM users u LEFT JOIN posts p ON u.id = p.author_id " +
           "GROUP BY u.id ORDER BY post_count DESC", nativeQuery = true)
    List<Object[]> findUsersWithPostCount();
    
    // 更新查询
    @Modifying
    @Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
    int updateUserStatus(@Param("id") Long id, @Param("status") UserStatus status);
    
    @Modifying
    @Query("UPDATE User u SET u.updatedAt = CURRENT_TIMESTAMP WHERE u.status = :status")
    int updateTimestampByStatus(@Param("status") UserStatus status);
    
    // 删除查询
    @Modifying
    @Query("DELETE FROM User u WHERE u.status = :status AND u.createdAt < :date")
    int deleteInactiveUsers(@Param("status") UserStatus status, @Param("date") LocalDateTime date);
}

投影查询

// 接口投影
public interface UserSummary {
    String getUsername();
    String getEmail();
    String getFullName(); // 计算属性
    
    @Value("#{target.firstName + ' ' + target.lastName}")
    String getFullName();
}

// 类投影
@Data
@AllArgsConstructor
public class UserDTO {
    private String username;
    private String email;
    private String fullName;
    private LocalDateTime createdAt;
}

// Repository中使用投影
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    
    // 接口投影
    List<UserSummary> findByStatus(UserStatus status);
    
    @Query("SELECT u.username as username, u.email as email, " +
           "CONCAT(u.firstName, ' ', u.lastName) as fullName " +
           "FROM User u WHERE u.status = :status")
    List<UserSummary> findUserSummaryByStatus(@Param("status") UserStatus status);
    
    // 类投影
    @Query("SELECT new com.example.dto.UserDTO(u.username, u.email, " +
           "CONCAT(u.firstName, ' ', u.lastName), u.createdAt) " +
           "FROM User u WHERE u.status = :status")
    List<UserDTO> findUserDTOByStatus(@Param("status") UserStatus status);
}

自定义Repository实现

// 自定义Repository接口
public interface UserRepositoryCustom {
    List<User> findUsersWithComplexCriteria(UserSearchCriteria criteria);
    Page<User> findUsersWithDynamicQuery(UserSearchCriteria criteria, Pageable pageable);
}

// 自定义Repository实现
@Repository
public class UserRepositoryImpl implements UserRepositoryCustom {
    
    @PersistenceContext
    private EntityManager entityManager;
    
    @Override
    public List<User> findUsersWithComplexCriteria(UserSearchCriteria criteria) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<User> query = cb.createQuery(User.class);
        Root<User> root = query.from(User.class);
        
        List<Predicate> predicates = new ArrayList<>();
        
        // 动态构建查询条件
        if (criteria.getUsername() != null) {
            predicates.add(cb.like(root.get("username"), "%" + criteria.getUsername() + "%"));
        }
        
        if (criteria.getEmail() != null) {
            predicates.add(cb.like(root.get("email"), "%" + criteria.getEmail() + "%"));
        }
        
        if (criteria.getStatus() != null) {
            predicates.add(cb.equal(root.get("status"), criteria.getStatus()));
        }
        
        if (criteria.getCreatedAfter() != null) {
            predicates.add(cb.greaterThanOrEqualTo(root.get("createdAt"), criteria.getCreatedAfter()));
        }
        
        if (criteria.getCreatedBefore() != null) {
            predicates.add(cb.lessThanOrEqualTo(root.get("createdAt"), criteria.getCreatedBefore()));
        }
        
        query.where(predicates.toArray(new Predicate[0]));
        query.orderBy(cb.desc(root.get("createdAt")));
        
        return entityManager.createQuery(query).getResultList();
    }
    
    @Override
    public Page<User> findUsersWithDynamicQuery(UserSearchCriteria criteria, Pageable pageable) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        
        // 查询数据
        CriteriaQuery<User> query = cb.createQuery(User.class);
        Root<User> root = query.from(User.class);
        
        List<Predicate> predicates = buildPredicates(cb, root, criteria);
        query.where(predicates.toArray(new Predicate[0]));
        
        // 添加排序
        if (pageable.getSort().isSorted()) {
            List<Order> orders = new ArrayList<>();
            for (Sort.Order order : pageable.getSort()) {
                if (order.isAscending()) {
                    orders.add(cb.asc(root.get(order.getProperty())));
                } else {
                    orders.add(cb.desc(root.get(order.getProperty())));
                }
            }
            query.orderBy(orders);
        }
        
        TypedQuery<User> typedQuery = entityManager.createQuery(query);
        typedQuery.setFirstResult((int) pageable.getOffset());
        typedQuery.setMaxResults(pageable.getPageSize());
        
        List<User> users = typedQuery.getResultList();
        
        // 查询总数
        CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
        Root<User> countRoot = countQuery.from(User.class);
        countQuery.select(cb.count(countRoot));
        countQuery.where(buildPredicates(cb, countRoot, criteria).toArray(new Predicate[0]));
        
        Long total = entityManager.createQuery(countQuery).getSingleResult();
        
        return new PageImpl<>(users, pageable, total);
    }
    
    private List<Predicate> buildPredicates(CriteriaBuilder cb, Root<User> root, UserSearchCriteria criteria) {
        List<Predicate> predicates = new ArrayList<>();
        
        if (criteria.getUsername() != null) {
            predicates.add(cb.like(cb.lower(root.get("username")), 
                                 "%" + criteria.getUsername().toLowerCase() + "%"));
        }
        
        if (criteria.getEmail() != null) {
            predicates.add(cb.like(cb.lower(root.get("email")), 
                                 "%" + criteria.getEmail().toLowerCase() + "%"));
        }
        
        if (criteria.getStatus() != null) {
            predicates.add(cb.equal(root.get("status"), criteria.getStatus()));
        }
        
        if (criteria.getAgeMin() != null) {
            predicates.add(cb.greaterThanOrEqualTo(
                cb.function("YEAR", Integer.class, cb.currentDate()),
                cb.function("YEAR", Integer.class, root.get("birthDate"))
            ));
        }
        
        return predicates;
    }
}

// 扩展主Repository接口
public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {
    // 标准方法...
}

// 搜索条件类
@Data
public class UserSearchCriteria {
    private String username;
    private String email;
    private UserStatus status;
    private LocalDateTime createdAfter;
    private LocalDateTime createdBefore;
    private Integer ageMin;
    private Integer ageMax;
}

4.4 事务管理

声明式事务

@Service
@Transactional
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private UserProfileRepository profileRepository;
    
    // 默认事务(读写)
    public User createUser(User user) {
        // 验证用户名唯一性
        if (userRepository.existsByUsername(user.getUsername())) {
            throw new BusinessException("Username already exists");
        }
        
        // 保存用户
        User savedUser = userRepository.save(user);
        
        // 创建用户档案
        UserProfile profile = new UserProfile();
        profile.setUser(savedUser);
        profileRepository.save(profile);
        
        return savedUser;
    }
    
    // 只读事务
    @Transactional(readOnly = true)
    public User findById(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("User not found"));
    }
    
    @Transactional(readOnly = true)
    public Page<User> findUsers(UserSearchCriteria criteria, Pageable pageable) {
        return userRepository.findUsersWithDynamicQuery(criteria, pageable);
    }
    
    // 指定事务传播行为
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logUserActivity(Long userId, String activity) {
        // 这个方法总是在新事务中执行
        UserActivityLog log = new UserActivityLog();
        log.setUserId(userId);
        log.setActivity(activity);
        log.setTimestamp(LocalDateTime.now());
        activityLogRepository.save(log);
    }
    
    // 指定异常回滚规则
    @Transactional(rollbackFor = {Exception.class}, noRollbackFor = {BusinessWarningException.class})
    public User updateUser(Long id, User userUpdate) {
        User existingUser = findById(id);
        
        // 更新用户信息
        existingUser.setFirstName(userUpdate.getFirstName());
        existingUser.setLastName(userUpdate.getLastName());
        existingUser.setEmail(userUpdate.getEmail());
        
        // 记录活动日志(在新事务中)
        logUserActivity(id, "User information updated");
        
        return userRepository.save(existingUser);
    }
    
    // 批量操作事务
    @Transactional
    public void batchUpdateUserStatus(List<Long> userIds, UserStatus status) {
        for (Long userId : userIds) {
            User user = findById(userId);
            user.setStatus(status);
            userRepository.save(user);
            
            // 每100个用户刷新一次
            if (userIds.indexOf(userId) % 100 == 0) {
                userRepository.flush();
            }
        }
    }
}

编程式事务

@Service
public class TransactionService {
    
    @Autowired
    private PlatformTransactionManager transactionManager;
    
    @Autowired
    private UserRepository userRepository;
    
    public void programmaticTransaction() {
        TransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(def);
        
        try {
            // 业务逻辑
            User user = new User();
            user.setUsername("test");
            userRepository.save(user);
            
            // 提交事务
            transactionManager.commit(status);
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(status);
            throw e;
        }
    }
    
    // 使用TransactionTemplate
    @Autowired
    private TransactionTemplate transactionTemplate;
    
    public User createUserWithTemplate(User user) {
        return transactionTemplate.execute(status -> {
            // 在事务中执行的代码
            if (userRepository.existsByUsername(user.getUsername())) {
                throw new BusinessException("Username exists");
            }
            return userRepository.save(user);
        });
    }
    
    // 只读事务模板
    public List<User> findUsersWithTemplate() {
        TransactionTemplate readOnlyTemplate = new TransactionTemplate(transactionManager);
        readOnlyTemplate.setReadOnly(true);
        
        return readOnlyTemplate.execute(status -> {
            return userRepository.findAll();
        });
    }
}

事务配置

@Configuration
@EnableTransactionManagement
public class TransactionConfig {
    
    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }
    
    @Bean
    public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
        return new TransactionTemplate(transactionManager);
    }
}

4.5 数据库迁移

Flyway配置

<!-- pom.xml -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
</dependency>
# application.properties

# Flyway配置
spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migration
spring.flyway.baseline-on-migrate=true
spring.flyway.validate-on-migrate=true

迁移脚本

-- src/main/resources/db/migration/V1__Create_users_table.sql
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    email VARCHAR(100) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    first_name VARCHAR(50),
    last_name VARCHAR(50),
    phone VARCHAR(20),
    status VARCHAR(20) DEFAULT 'ACTIVE',
    birth_date DATE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    version BIGINT DEFAULT 0
);

CREATE INDEX idx_users_username ON users(username);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_status ON users(status);
-- V2__Create_posts_table.sql
CREATE TABLE posts (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(200) NOT NULL,
    content TEXT,
    status VARCHAR(20) DEFAULT 'DRAFT',
    author_id BIGINT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE CASCADE
);

CREATE INDEX idx_posts_author ON posts(author_id);
CREATE INDEX idx_posts_status ON posts(status);
CREATE INDEX idx_posts_created_at ON posts(created_at);
-- V3__Add_user_profile_table.sql
CREATE TABLE user_profiles (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT NOT NULL UNIQUE,
    bio TEXT,
    avatar VARCHAR(255),
    website VARCHAR(255),
    location VARCHAR(100),
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

4.6 数据库连接池

HikariCP配置

# application.properties

# HikariCP连接池配置
spring.datasource.hikari.pool-name=SpringBootHikariCP
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
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.leak-detection-threshold=60000

# 连接池监控
spring.datasource.hikari.register-mbeans=true

多数据源配置

@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();
    }
    
    @Primary
    @Bean(name = "primaryEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
            EntityManagerFactoryBuilder builder,
            @Qualifier("primaryDataSource") DataSource dataSource) {
        
        return builder
                .dataSource(dataSource)
                .packages("com.example.primary.entity")
                .persistenceUnit("primary")
                .build();
    }
    
    @Bean(name = "secondaryEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(
            EntityManagerFactoryBuilder builder,
            @Qualifier("secondaryDataSource") DataSource dataSource) {
        
        return builder
                .dataSource(dataSource)
                .packages("com.example.secondary.entity")
                .persistenceUnit("secondary")
                .build();
    }
    
    @Primary
    @Bean(name = "primaryTransactionManager")
    public PlatformTransactionManager primaryTransactionManager(
            @Qualifier("primaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
    
    @Bean(name = "secondaryTransactionManager")
    public PlatformTransactionManager secondaryTransactionManager(
            @Qualifier("secondaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}

// 主数据源Repository配置
@Configuration
@EnableJpaRepositories(
    basePackages = "com.example.primary.repository",
    entityManagerFactoryRef = "primaryEntityManagerFactory",
    transactionManagerRef = "primaryTransactionManager"
)
public class PrimaryDataSourceConfig {
}

// 次数据源Repository配置
@Configuration
@EnableJpaRepositories(
    basePackages = "com.example.secondary.repository",
    entityManagerFactoryRef = "secondaryEntityManagerFactory",
    transactionManagerRef = "secondaryTransactionManager"
)
public class SecondaryDataSourceConfig {
}
# 多数据源配置
spring.datasource.primary.url=jdbc:mysql://localhost:3306/primary_db
spring.datasource.primary.username=root
spring.datasource.primary.password=password
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.secondary.url=jdbc:mysql://localhost:3306/secondary_db
spring.datasource.secondary.username=root
spring.datasource.secondary.password=password
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver

4.7 缓存集成

Spring Cache配置

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
@Configuration
@EnableCaching
public class CacheConfig {
    
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(10))
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()));
        
        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(config)
                .build();
    }
}

缓存注解使用

@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    // 缓存查询结果
    @Cacheable(value = "users", key = "#id")
    public User findById(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("User not found"));
    }
    
    @Cacheable(value = "users", key = "#username")
    public User findByUsername(String username) {
        return userRepository.findByUsername(username)
                .orElseThrow(() -> new ResourceNotFoundException("User not found"));
    }
    
    // 条件缓存
    @Cacheable(value = "users", key = "#id", condition = "#id > 0")
    public User findByIdConditional(Long id) {
        return userRepository.findById(id).orElse(null);
    }
    
    // 更新缓存
    @CachePut(value = "users", key = "#result.id")
    public User updateUser(User user) {
        return userRepository.save(user);
    }
    
    // 清除缓存
    @CacheEvict(value = "users", key = "#id")
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
    
    // 清除所有缓存
    @CacheEvict(value = "users", allEntries = true)
    public void clearAllUsers() {
        // 清除所有用户缓存
    }
    
    // 组合缓存操作
    @Caching(
        cacheable = @Cacheable(value = "users", key = "#id"),
        put = @CachePut(value = "userStats", key = "#id")
    )
    public User findUserWithStats(Long id) {
        User user = userRepository.findById(id).orElse(null);
        if (user != null) {
            // 计算用户统计信息
            calculateUserStats(user);
        }
        return user;
    }
}

4.8 性能优化

查询优化

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    
    // 使用JOIN FETCH避免N+1问题
    @Query("SELECT u FROM User u JOIN FETCH u.posts WHERE u.id = :id")
    Optional<User> findByIdWithPosts(@Param("id") Long id);
    
    @Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.status = :status")
    List<User> findByStatusWithRoles(@Param("status") UserStatus status);
    
    // 批量查询
    @Query("SELECT u FROM User u WHERE u.id IN :ids")
    List<User> findByIdIn(@Param("ids") List<Long> ids);
    
    // 分页查询优化
    @Query(value = "SELECT u.* FROM users u WHERE u.status = :status ORDER BY u.created_at DESC LIMIT :limit OFFSET :offset", 
           nativeQuery = true)
    List<User> findByStatusWithPagination(@Param("status") String status, 
                                         @Param("limit") int limit, 
                                         @Param("offset") int offset);
}

@Service
public class UserService {
    
    // 批量操作优化
    @Transactional
    public void batchSaveUsers(List<User> users) {
        int batchSize = 50;
        for (int i = 0; i < users.size(); i += batchSize) {
            int end = Math.min(i + batchSize, users.size());
            List<User> batch = users.subList(i, end);
            userRepository.saveAll(batch);
            userRepository.flush();
            entityManager.clear(); // 清除持久化上下文
        }
    }
    
    // 懒加载优化
    @Transactional(readOnly = true)
    public User findUserWithPostsLazy(Long id) {
        User user = userRepository.findById(id).orElse(null);
        if (user != null) {
            // 强制初始化懒加载集合
            Hibernate.initialize(user.getPosts());
        }
        return user;
    }
}

数据库索引

@Entity
@Table(name = "users", indexes = {
    @Index(name = "idx_username", columnList = "username"),
    @Index(name = "idx_email", columnList = "email"),
    @Index(name = "idx_status_created", columnList = "status, created_at"),
    @Index(name = "idx_name_composite", columnList = "first_name, last_name")
})
public class User {
    // 实体定义...
}

// 复合索引示例
@Entity
@Table(name = "posts", indexes = {
    @Index(name = "idx_author_status", columnList = "author_id, status"),
    @Index(name = "idx_created_status", columnList = "created_at, status")
})
public class Post {
    // 实体定义...
}

总结

本章详细介绍了Spring Boot数据访问与持久化的核心内容:

  1. Spring Data JPA:ORM框架集成和配置
  2. 实体设计:实体映射、关系映射、继承策略
  3. Repository接口:查询方法、自定义查询、投影
  4. 事务管理:声明式事务、编程式事务
  5. 数据库迁移:Flyway版本控制
  6. 连接池配置:HikariCP优化、多数据源
  7. 缓存集成:Spring Cache、Redis缓存
  8. 性能优化:查询优化、批量操作、索引设计

这些知识点构成了Spring Boot数据访问层的完整体系,掌握这些内容可以构建高性能、可维护的数据访问层。