Spring Boot 数据访问
1. 数据访问概述
1.1 Spring Data 简介
Spring Data 提供了一致的数据访问模型,支持多种数据存储:
- Spring Data JPA:关系型数据库
- Spring Data MongoDB:MongoDB
- Spring Data Redis:Redis
- Spring Data Elasticsearch:Elasticsearch
1.2 数据源配置
yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 10
minimum-idle: 5
idle-timeout: 300000
connection-timeout: 20000
pool-name: MyHikariCP2. Spring Data JPA
2.1 添加依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>2.2 实体类
java
@Entity
@Table(name = "users")
@EntityListeners(AuditingEntityListener.class)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 50)
private String username;
@Column(nullable = false)
private String password;
@Column(length = 100)
private String email;
@Enumerated(EnumType.STRING)
private UserStatus status = UserStatus.ACTIVE;
@CreatedDate
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Order> orders = new ArrayList<>();
// getters and setters
}2.3 Repository 接口
java
public interface UserRepository extends JpaRepository<User, Long>,
JpaSpecificationExecutor<User> {
Optional<User> findByUsername(String username);
Optional<User> findByEmail(String email);
List<User> findByStatus(UserStatus status);
List<User> findByCreatedAtBetween(LocalDateTime start, LocalDateTime end);
@Query("SELECT u FROM User u WHERE u.email LIKE %:domain")
List<User> findByEmailDomain(@Param("domain") String domain);
@Query(value = "SELECT * FROM users WHERE status = :status", nativeQuery = true)
List<User> findByStatusNative(@Param("status") String status);
@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
int updateStatus(@Param("id") Long id, @Param("status") UserStatus status);
boolean existsByUsername(String username);
boolean existsByEmail(String email);
long countByStatus(UserStatus status);
void deleteByUsername(String username);
}2.4 分页查询
java
@Service
public class UserService {
private final UserRepository userRepository;
public Page<User> findByPage(int page, int size, String sortBy) {
Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy).ascending());
return userRepository.findAll(pageable);
}
public Page<User> search(String keyword, int page, int size) {
Pageable pageable = PageRequest.of(page, size);
Specification<User> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (keyword != null && !keyword.isEmpty()) {
predicates.add(cb.or(
cb.like(root.get("username"), "%" + keyword + "%"),
cb.like(root.get("email"), "%" + keyword + "%")
));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
return userRepository.findAll(spec, pageable);
}
}2.5 审计功能
java
@Configuration
@EnableJpaAuditing
public class JpaConfig {
@Bean
public AuditorAware<String> auditorProvider() {
return () -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return Optional.empty();
}
return Optional.of(authentication.getName());
};
}
}
@Entity
@EntityListeners(AuditingEntityListener.class)
public class User {
@CreatedBy
@Column(name = "created_by", updatable = false)
private String createdBy;
@LastModifiedBy
@Column(name = "updated_by")
private String updatedBy;
// ...
}3. Spring Data MongoDB
3.1 添加依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>3.2 配置连接
yaml
spring:
data:
mongodb:
uri: mongodb://localhost:27017/mydb
# 或分开配置
host: localhost
port: 27017
database: mydb
username: root
password: password3.3 文档实体
java
@Document(collection = "users")
public class User {
@Id
private String id;
@Indexed(unique = true)
private String username;
private String email;
private List<String> roles = new ArrayList<>();
private Address address;
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
// getters and setters
}
@Document(collection = "products")
public class Product {
@Id
private String id;
private String name;
private BigDecimal price;
private String category;
@TextIndexed
private String description;
// getters and setters
}3.4 Repository 接口
java
public interface UserRepository extends MongoRepository<User, String> {
Optional<User> findByUsername(String username);
List<User> findByRolesIn(List<String> roles);
List<User> findByAddressCity(String city);
@Query("{ 'price' : { $gt: ?0 } }")
List<Product> findByPriceGreaterThan(BigDecimal price);
@Aggregation(pipeline = {
"{ $match: { 'category': ?0 } }",
"{ $group: { _id: null, avgPrice: { $avg: '$price' } } }"
})
Double findAveragePriceByCategory(String category);
}4. Spring Data Redis
4.1 添加依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>4.2 配置 Redis
yaml
spring:
data:
redis:
host: localhost
port: 6379
password:
database: 0
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 04.3 Redis 实体
java
@RedisHash(value = "user", timeToLive = 3600)
public class User {
@Id
private Long id;
@Indexed
private String username;
private String email;
// getters and setters
}4.4 Repository 接口
java
public interface UserRepository extends CrudRepository<User, Long> {
Optional<User> findByUsername(String username);
List<User> findByEmail(String email);
}4.5 RedisTemplate
java
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(
RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
@Service
public class CacheService {
private final RedisTemplate<String, Object> redisTemplate;
public void set(String key, Object value, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, value, timeout, unit);
}
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
public void delete(String key) {
redisTemplate.delete(key);
}
public boolean hasKey(String key) {
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
}
}5. 事务管理
5.1 声明式事务
java
@Service
@Transactional
public class UserService {
private final UserRepository userRepository;
private final OrderRepository orderRepository;
@Transactional(readOnly = true)
public User findById(Long id) {
return userRepository.findById(id).orElse(null);
}
@Transactional(rollbackFor = Exception.class)
public User create(User user) {
return userRepository.save(user);
}
@Transactional(rollbackFor = Exception.class)
public void deleteUserWithOrders(Long userId) {
orderRepository.deleteByUserId(userId);
userRepository.deleteById(userId);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logOperation(String operation) {
// 新事务中记录日志
}
}5.2 事务传播行为
| 传播行为 | 说明 |
|---|---|
| REQUIRED | 有事务则加入,无则新建(默认) |
| REQUIRES_NEW | 总是新建事务 |
| SUPPORTS | 有事务则加入,无则非事务运行 |
| NOT_SUPPORTED | 非事务运行 |
| MANDATORY | 必须在事务中运行 |
| NEVER | 不能在事务中运行 |
| NESTED | 嵌套事务 |
5.3 事务隔离级别
| 隔离级别 | 说明 |
|---|---|
| DEFAULT | 使用数据库默认隔离级别 |
| READ_UNCOMMITTED | 读未提交 |
| READ_COMMITTED | 读已提交 |
| REPEATABLE_READ | 可重复读 |
| SERIALIZABLE | 串行化 |
6. 多数据源
6.1 配置多数据源
yaml
spring:
datasource:
primary:
url: jdbc:mysql://localhost:3306/db1
username: root
password: password
secondary:
url: jdbc:mysql://localhost:3306/db2
username: root
password: password6.2 主数据源配置
java
@Configuration
@EnableJpaRepositories(
basePackages = "com.example.repository.primary",
entityManagerFactoryRef = "primaryEntityManagerFactory",
transactionManagerRef = "primaryTransactionManager"
)
public class PrimaryDataSourceConfig {
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
EntityManagerFactoryBuilder builder,
DataSource primaryDataSource) {
return builder
.dataSource(primaryDataSource)
.packages("com.example.entity.primary")
.persistenceUnit("primary")
.build();
}
@Bean
@Primary
public PlatformTransactionManager primaryTransactionManager(
LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory) {
return new JpaTransactionManager(
primaryEntityManagerFactory.getObject()
);
}
}6.3 从数据源配置
java
@Configuration
@EnableJpaRepositories(
basePackages = "com.example.repository.secondary",
entityManagerFactoryRef = "secondaryEntityManagerFactory",
transactionManagerRef = "secondaryTransactionManager"
)
public class SecondaryDataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(
EntityManagerFactoryBuilder builder,
DataSource secondaryDataSource) {
return builder
.dataSource(secondaryDataSource)
.packages("com.example.entity.secondary")
.persistenceUnit("secondary")
.build();
}
@Bean
public PlatformTransactionManager secondaryTransactionManager(
LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory) {
return new JpaTransactionManager(
secondaryEntityManagerFactory.getObject()
);
}
}7. 数据库迁移
7.1 Flyway
xml
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-mysql</artifactId>
</dependency>yaml
spring:
flyway:
enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
validate-on-migrate: true# db/migration/V1__Create_users_table.sql
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
email VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
# db/migration/V2__Create_orders_table.sql
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
order_no VARCHAR(50) NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);7.2 Liquibase
xml
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>yaml
spring:
liquibase:
enabled: true
change-log: classpath:db/changelog/db.changelog-master.yamlyaml
# db/changelog/db.changelog-master.yaml
databaseChangeLog:
- include:
file: db/changelog/changes/001-create-users-table.yaml
- include:
file: db/changelog/changes/002-create-orders-table.yamlyaml
# db/changelog/changes/001-create-users-table.yaml
databaseChangeLog:
- changeSet:
id: 1
author: developer
changes:
- createTable:
tableName: users
columns:
- column:
name: id
type: BIGINT
autoIncrement: true
constraints:
primaryKey: true
- column:
name: username
type: VARCHAR(50)
constraints:
nullable: false
unique: true8. 小结
本章学习了 Spring Boot 数据访问的核心内容:
| 内容 | 要点 |
|---|---|
| Spring Data JPA | 实体类、Repository、分页查询 |
| Spring Data MongoDB | 文档实体、Repository |
| Spring Data Redis | RedisTemplate、缓存实体 |
| 事务管理 | @Transactional、传播行为、隔离级别 |
| 多数据源 | 配置类、EntityManagerFactory |
| 数据库迁移 | Flyway、Liquibase |
下一章将学习 Spring Boot 缓存机制。