Spring Data为数据访问提供了统一的编程模型,本文将详细介绍Spring Data的核心特性和最佳实践。
基础配置
Maven依赖
1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId> org.springframework.boot</groupId>
<artifactId> spring-boot-starter-data-jpa</artifactId>
<version> 2.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId> mysql</groupId>
<artifactId> mysql-connector-java</artifactId>
<version> 8.0.13</version>
</dependency>
数据源配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spring :
datasource :
url : jdbc:mysql://localhost:3306/mydb?useSSL=false
username : root
password : root
driver-class-name : com.mysql.cj.jdbc.Driver
jpa :
hibernate :
ddl-auto : update
show-sql : true
properties :
hibernate :
dialect : org.hibernate.dialect.MySQL8Dialect
format_sql : true
实体映射
基本映射
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Entity
@Table ( name = "users" )
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue ( strategy = GenerationType . IDENTITY )
private Long id ;
@Column ( nullable = false , unique = true )
private String username ;
@Column ( nullable = false )
private String email ;
@Column ( name = "created_at" )
@CreationTimestamp
private LocalDateTime createdAt ;
@Version
private Integer version ;
}
关系映射
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Entity
@Table ( name = "orders" )
public class Order {
@Id
@GeneratedValue ( strategy = GenerationType . IDENTITY )
private Long id ;
@ManyToOne ( fetch = FetchType . LAZY )
@JoinColumn ( name = "user_id" )
private User user ;
@OneToMany ( mappedBy = "order" , cascade = CascadeType . ALL , orphanRemoval = true )
private List < OrderItem > items = new ArrayList <> ();
public void addItem ( OrderItem item ) {
items . add ( item );
item . setOrder ( this );
}
public void removeItem ( OrderItem item ) {
items . remove ( item );
item . setOrder ( null );
}
}
数据访问
Repository接口
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface UserRepository extends JpaRepository < User , Long > {
Optional < User > findByUsername ( String username );
List < User > findByEmailEndingWith ( String domain );
@Query ( "SELECT u FROM User u WHERE u.createdAt > :date" )
List < User > findRecentUsers ( @Param ( "date" ) LocalDateTime date );
@Modifying
@Query ( "UPDATE User u SET u.email = :email WHERE u.id = :id" )
int updateEmail ( @Param ( "id" ) Long id , @Param ( "email" ) String email );
}
分页和排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Service
public class UserService {
@Autowired
private UserRepository userRepository ;
public Page < User > findUsers ( int page , int size ) {
PageRequest pageRequest = PageRequest . of ( page , size ,
Sort . by ( "createdAt" ). descending ());
return userRepository . findAll ( pageRequest );
}
public List < User > findUsersSorted () {
Sort sort = Sort . by ( "username" ). ascending ()
. and ( Sort . by ( "email" ). descending ());
return userRepository . findAll ( sort );
}
}
查询方法
命名查询
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface OrderRepository extends JpaRepository < Order , Long > {
// 自动生成查询
List < Order > findByUserAndStatus ( User user , OrderStatus status );
// 使用JPQL
@Query ( "SELECT o FROM Order o JOIN FETCH o.items WHERE o.user.id = :userId" )
List < Order > findByUserWithItems ( @Param ( "userId" ) Long userId );
// 使用原生SQL
@Query ( value = "SELECT * FROM orders WHERE total_amount > ?1" , nativeQuery = true )
List < Order > findExpensiveOrders ( BigDecimal amount );
}
Specification查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class UserService {
public List < User > findUsers ( String username , String email ) {
return userRepository . findAll (( root , query , cb ) -> {
List < Predicate > predicates = new ArrayList <> ();
if ( username != null ) {
predicates . add ( cb . like ( root . get ( "username" ), "%" + username + "%" ));
}
if ( email != null ) {
predicates . add ( cb . equal ( root . get ( "email" ), email ));
}
return cb . and ( predicates . toArray ( new Predicate [ 0 ] ));
});
}
}
事务管理
事务配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
@Transactional
public class OrderService {
@Autowired
private OrderRepository orderRepository ;
@Transactional ( readOnly = true )
public Order findOrder ( Long id ) {
return orderRepository . findById ( id )
. orElseThrow (() -> new OrderNotFoundException ( id ));
}
@Transactional ( rollbackFor = OrderException . class )
public Order createOrder ( Order order ) {
validateOrder ( order );
return orderRepository . save ( order );
}
}
审计功能
审计配置
1
2
3
4
5
6
7
8
9
10
11
@Configuration
@EnableJpaAuditing
public class JpaConfig {
@Bean
public AuditorAware < String > auditorProvider () {
return () -> Optional . ofNullable ( SecurityContextHolder . getContext ())
. map ( SecurityContext :: getAuthentication )
. map ( Authentication :: getName );
}
}
审计实体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@EntityListeners ( AuditingEntityListener . class )
@MappedSuperclass
@Data
public abstract class Auditable {
@CreatedBy
protected String createdBy ;
@CreatedDate
protected LocalDateTime createdDate ;
@LastModifiedBy
protected String lastModifiedBy ;
@LastModifiedDate
protected LocalDateTime lastModifiedDate ;
}
@Entity
public class Product extends Auditable {
@Id
@GeneratedValue ( strategy = GenerationType . IDENTITY )
private Long id ;
private String name ;
private BigDecimal price ;
}
缓存支持
缓存配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager () {
SimpleCacheManager cacheManager = new SimpleCacheManager ();
cacheManager . setCaches ( Arrays . asList (
new ConcurrentMapCache ( "users" ),
new ConcurrentMapCache ( "orders" )
));
return cacheManager ;
}
}
缓存使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class UserService {
@Cacheable ( value = "users" , key = "#id" )
public User findUser ( Long id ) {
return userRepository . findById ( id )
. orElseThrow (() -> new UserNotFoundException ( id ));
}
@CacheEvict ( value = "users" , key = "#user.id" )
public void updateUser ( User user ) {
userRepository . save ( user );
}
@CacheEvict ( value = "users" , allEntries = true )
public void clearCache () {
// 清除缓存
}
}
最佳实践
领域模型设计
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Entity
public class Order {
// 使用充血模型
public void addItem ( Product product , int quantity ) {
OrderItem item = new OrderItem ( this , product , quantity );
items . add ( item );
recalculateTotal ();
}
private void recalculateTotal () {
this . total = items . stream ()
. map ( OrderItem :: getSubtotal )
. reduce ( BigDecimal . ZERO , BigDecimal :: add );
}
}
性能优化
1
2
3
4
5
6
7
8
9
10
11
12
13
@Repository
public interface OrderRepository extends JpaRepository < Order , Long > {
// 使用投影
interface OrderSummary {
Long getId ();
BigDecimal getTotal ();
String getStatus ();
}
// 避免N+1问题
@EntityGraph ( attributePaths = { "items" , "user" })
List < Order > findAllWithItemsAndUser ();
}
总结
Spring Data提供了强大的数据访问功能,通过合理使用其特性,可以大大提高开发效率。
参考资料
Spring Data JPA参考文档
Java Persistence with Hibernate
Spring实战(第5版)
Licensed under CC BY-NC-SA 4.0