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数据访问与持久化的核心内容:
- Spring Data JPA:ORM框架集成和配置
- 实体设计:实体映射、关系映射、继承策略
- Repository接口:查询方法、自定义查询、投影
- 事务管理:声明式事务、编程式事务
- 数据库迁移:Flyway版本控制
- 连接池配置:HikariCP优化、多数据源
- 缓存集成:Spring Cache、Redis缓存
- 性能优化:查询优化、批量操作、索引设计
这些知识点构成了Spring Boot数据访问层的完整体系,掌握这些内容可以构建高性能、可维护的数据访问层。