Spring Data JPA 实战指南
作者:014-code
本文主要介绍了Spring Data JPA 实战指南,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
用过 MyBatis 再用 JPA,可能会觉得 JPA 很别扭——SQL 都不用写了,框架自动搞定。
但用久了会发现,JPA 写起来其实很爽,尤其单表操作,几乎不需要写 SQL。
基础配置
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useSSL=false
username: root
password: 123456
jpa:
hibernate:
ddl-auto: update # 开发用 update,生产用 validate
show-sql: true # 打印 SQL
properties:
hibernate:
format_sql: trueddl-auto 几个选项:
update:自动更新表结构(不会删数据)create:每次启动删表重建validate:只验证,不改表none:什么都不做
实体映射
基本映射
@Entity
@Table(name = "sys_user")
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "user_name", length = 50, nullable = false)
private String userName;
private Integer age;
@Column(columnDefinition = "varchar(20) default 'ACTIVE'")
private String status;
@Column(name = "create_time")
private LocalDateTime createTime;
@Transient // 不映射到数据库
private String tempField;
}主键生成策略
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) // MySQL 自增 // 或 @GeneratedValue(strategy = GenerationType.SEQUENCE) // Oracle、序列 // 或 @GeneratedValue(strategy = GenerationType.UUID) // UUID private String id;
Repository 基础 CRUD
继承 JpaRepository 就有基本的增删改查:
public interface UserRepository extends JpaRepository<User, Long> {
// 继承来的方法:
// save(entity) - 保存或更新
// findById(id) - 查询
// deleteById(id) - 删除
// count() - 计数
// existsById(id) - 是否存在
}@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
public User createUser(String name, Integer age) {
User user = new User();
user.setUserName(name);
user.setAge(age);
return userRepository.save(user); // 自动 insert
}
public Optional<User> getUser(Long id) {
return userRepository.findById(id); // 自动 select
}
public void deleteUser(Long id) {
userRepository.deleteById(id); // 自动 delete
}
public User updateUser(Long id, String newName) {
User user = userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("用户不存在"));
user.setUserName(newName);
return userRepository.save(user); // 自动 update
}
}方法名查询(单表神器)
这是 JPA 最爽的地方——不用写 SQL,方法名就是查询。
基础规则
public interface UserRepository extends JpaRepository<User, Long> {
// 等值查询:findBy + 字段名
List<User> findByUserName(String userName);
// → SELECT * FROM sys_user WHERE user_name = ?
// 模糊查询:findBy + 字段名 + Like
List<User> findByUserNameLike(String userName);
// → SELECT * FROM sys_user WHERE user_name LIKE ?
// 多条件:findBy + 字段1 + And + 字段2
List<User> findByUserNameAndAge(String userName, Integer age);
// → SELECT * FROM sys_user WHERE user_name = ? AND age = ?
// Or 条件
List<User> findByUserNameOrAge(String userName, Integer age);
// → SELECT * FROM sys_user WHERE user_name = ? OR age = ?
}常用关键词
// 大于/小于/等于 List<User> findByAgeGreaterThan(Integer age); // age > ? List<User> findByAgeLessThan(Integer age); // age < ? List<User> findByAgeGreaterThanEqual(Integer age); // age >= ? List<User> findByStatusEquals(String status); // status = ? // BETWEEN 范围 List<User> findByAgeBetween(Integer min, Integer max); // → WHERE age BETWEEN ? AND ? // IN 查询 List<User> findByUserNameIn(List<String> names); // → WHERE user_name IN (?, ?, ?) // NULL 判断 List<User> findByEmailIsNull(); List<User> findByEmailIsNotNull(); // True/False List<User> findByActiveTrue(); // WHERE active = true List<User> findByActiveFalse(); // WHERE active = false // 排序 List<User> findByStatusOrderByAgeDesc(String status); // → WHERE status = ? ORDER BY age DESC
分页和排序
// 分页:参数加 Pageable
Page<User> findByStatus(String status, Pageable pageable);
// 排序:Sort
List<User> findByStatus(String status, Sort sort);
// 使用
@Service
public class UserService {
public Page<User> getUserPage(int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("age").descending());
return userRepository.findByStatus("ACTIVE", pageable);
}
}@Query 自定义查询
方法名解决不了的,用 @Query。
JPQL 查询
@Query("SELECT u FROM User u WHERE u.userName = ?1")
User findByName(String userName);
// 占位符 ?1、?2 按参数顺序
@Query("SELECT u FROM User u WHERE u.userName = ?1 AND u.age > ?2")
List<User> findByNameAndAge(String name, Integer age);
// 命名参数(更清晰)
@Query("SELECT u FROM User u WHERE u.userName = :name AND u.age > :age")
List<User> findByNameAndAgeV2(@Param("name") String name, @Param("age") Integer age);多表联查
假设 User 关联 Department:
@Entity
public class User {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "dept_id")
private Department department;
}
// 查询用户并带出部门名称
@Query("SELECT u FROM User u JOIN FETCH u.department WHERE u.id = :id")
User findByIdWithDepartment(@Param("id") Long id);原生 SQL
@Query(value = "SELECT * FROM sys_user WHERE user_name = :name", nativeQuery = true)
User findByNameNative(@Param("name") String name);
// 原生 SQL 分页(注意 countQuery)
@Query(
value = "SELECT * FROM sys_user WHERE status = :status ORDER BY age DESC",
countQuery = "SELECT count(*) FROM sys_user WHERE status = :status",
nativeQuery = true
)
Page<User> findByStatusPage(@Param("status") String status, Pageable pageable);@Modifying 修改操作
@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
int updateStatus(@Param("id") Long id, @Param("status") String status);
@Modifying
@Query("DELETE FROM User u WHERE u.status = 'INACTIVE'")
void deleteInactiveUsers();记得在 Service 层加事务:
@Transactional
public void updateStatus(Long id, String status) {
userRepository.updateStatus(id, status);
}
一对多 / 多对一
实体定义
@Entity
@Table(name = "department")
@Data
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<User> users;
}
@Entity
@Table(name = "sys_user")
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String userName;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "dept_id")
private Department department;
}查询
// 根据部门查用户
List<User> findByDepartmentId(Long deptId);
// 根据用户查部门(直接访问属性就行)
User user = userRepository.findById(id).orElseThrow(...);
String deptName = user.getDepartment().getName(); // 懒加载,会再查一次
// 一次性查出来(解决 N+1)
@Query("SELECT u FROM User u JOIN FETCH u.department WHERE u.id = :id")
User findByIdWithDept(@Param("id") Long id);
常见问题
1. 懒加载异常
// 报错:LazyInitializationException User user = userRepository.findById(1L).orElseThrow(...); String deptName = user.getDepartment().getName(); // session 已关闭
解决:在 Service 层加 @Transactional,或者用 JOIN FETCH 一次性加载。
2. N+1 问题
// N+1:查 1 个用户,再查 N 次部门 List<User> users = userRepository.findAll(); // SELECT * FROM sys_user // SELECT * FROM department WHERE id = ? // SELECT * FROM department WHERE id = ? // ...
解决:用 @EntityGraph 或 JOIN FETCH:
@EntityGraph(attributePaths = {"department"})
List<User> findAllWithDept();
// 或
@Query("SELECT u FROM User u JOIN FETCH u.department")
List<User> findAllWithDept();
3. save 和 update 的区别
// save() - 如果 id 已存在就是 update,不存在就是 insert
User user = new User();
user.setId(1L); // id 存在,变成 update
user.setUserName("newName");
userRepository.save(user); // UPDATE
总结
| 方式 | 适用场景 |
|---|---|
| 继承 JpaRepository | 基本 CRUD |
| 方法名查询 | 单表简单查询 |
| @Query + JPQL | 多表关联、复杂查询 |
| @Query + 原生 SQL | 特定数据库语法 |
| JOIN FETCH | 解决懒加载和 N+1 |
JPA 最大的好处是不用写 SQL,单表操作特别爽。但多表关联和复杂查询,还是得用 @Query。用久了会发现,JPA + @Query 配合起来,效率比 MyBatis 高多了。
到此这篇关于Spring Data JPA 实战指南的文章就介绍到这了,更多相关Spring Data JPA 实战内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
