一、JPA 简介
1.1 什么是 JPA?
Java Persistence API(JPA)是 Java EE 的一部分,提供了一套 对象关系映射(ORM) 的标准接口。它通过注解和 API 将 Java 对象与数据库表映射,简化了数据库操作。
1.2 JPA 的核心功能
- 对象-关系映射(ORM):将 Java 类映射为数据库表,对象属性映射为表字段。
- CRUD 操作:通过 EntityManager 提供增删改查操作。
- 查询语言:JPQL(Java Persistence Query Language)和 Criteria API。
- 事务管理:支持声明式事务(
@Transactional)。 - 缓存机制:一级缓存(Session 级)、二级缓存(应用级)。
二、JPA 入门
2.1 依赖配置(Spring Boot 示例)
<!-- Maven -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
2.2 配置文件(application.properties)
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
三、实体类映射
3.1 基本注解
| 注解 | 说明 |
|---|---|
@Entity | 标记类为实体 |
@Table(name = "user") | 指定数据库表名 |
@Id | 标记主键字段 |
@GeneratedValue | 主键生成策略 |
@Column(name = "username") | 映射字段名 |
@Temporal(TemporalType.DATE) | 映射日期类型 |
@Enumerated(EnumType.STRING) | 映射枚举类型 |
示例:用户实体类
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username", unique = true, nullable = false)
private String username;
@Column(name = "email", unique = true)
private String email;
@Temporal(TemporalType.DATE)
private Date birthDate;
@Enumerated(EnumType.STRING)
private Role role;
// Getters and Setters
}
3.2 主键生成策略
| 策略 | 说明 |
|---|---|
GenerationType.IDENTITY | 自增主键(MySQL、PostgreSQL) |
GenerationType.SEQUENCE | 序列(Oracle) |
GenerationType.TABLE | 使用数据库表维护主键 |
GenerationType.AUTO | 自动选择策略 |
四、JPA 操作
4.1 EntityManager 基本操作
EntityManager em = ...;
User user = new User();
user.setUsername("admin");
user.setEmail("admin@example.com");
// 保存
em.persist(user);
// 查询
User foundUser = em.find(User.class, 1L);
// 更新
foundUser.setEmail("new@example.com");
em.merge(foundUser);
// 删除
em.remove(foundUser);
4.2 Spring Data JPA 简化操作
Spring Data JPA 提供了 JpaRepository 接口,自动实现 CRUD 方法。
示例:Repository 接口
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
List<User> findByEmailContaining(String email);
}
五、JPQL 与查询
5.1 JPQL 基本语法
String jpql = "SELECT u FROM User u WHERE u.username LIKE :username";
TypedQuery<User> query = em.createQuery(jpql, User.class);
query.setParameter("username", "%admin%");
List<User> users = query.getResultList();
5.2 命名查询(@NamedQuery)
@Entity
@NamedQuery(name = "User.findByRole",
query = "SELECT u FROM User u WHERE u.role = :role")
public class User {
// ...
}
5.3 Criteria API(类型安全查询)
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> user = cq.from(User.class);
cq.select(user).where(cb.equal(user.get("role"), Role.ADMIN));
List<User> admins = em.createQuery(cq).getResultList();
六、关联关系映射
6.1 一对一(@OneToOne)
@Entity
public class User {
@Id
private Long id;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "profile_id")
private Profile profile;
}
@Entity
public class Profile {
@Id
private Long id;
private String bio;
}
6.2 一对多(@OneToMany)
@Entity
public class User {
@Id
private Long id;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Order> orders = new ArrayList<>();
}
@Entity
public class Order {
@Id
private Long id;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
}
6.3 多对多(@ManyToMany)
@Entity
public class Student {
@Id
private Long id;
@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
}
@Entity
public class Course {
@Id
private Long id;
}
七、事务管理
7.1 声明式事务(@Transactional)
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void transferMoney(Long fromId, Long toId, Double amount) {
User fromUser = userRepository.findById(fromId).orElseThrow(...);
User toUser = userRepository.findById(toId).orElseThrow(...);
fromUser.setBalance(fromUser.getBalance() - amount);
toUser.setBalance(toUser.getBalance() + amount);
userRepository.save(fromUser);
userRepository.save(toUser);
}
}
7.2 事务传播行为
| 传播行为 | 说明 |
|---|---|
REQUIRED | 如果存在事务则加入,否则新建(默认) |
REQUIRES_NEW | 总是新建事务 |
NEVER | 不允许事务 |
八、性能优化
8.1 懒加载 vs 急加载
@OneToMany(fetch = FetchType.LAZY) // 懒加载(默认)
private List<Order> orders;
@OneToMany(fetch = FetchType.EAGER) // 急加载
private List<Order> orders;
8.2 避免 N+1 问题
- 使用
JOIN FETCH:
String jpql = "SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :id";
- 使用
@BatchSize:
@OneToMany(fetch = FetchType.LAZY)
@BatchSize(size = 10)
private List<Order> orders;
8.3 缓存配置
spring.jpa.open-in-view=false
spring.cache.type=caffeine
九、高级功能
9.1 实体监听器(@EntityListeners)
@Entity
@EntityListeners(AuditingEntityListener.class)
public class User {
@CreatedDate
private Date createdAt;
@LastModifiedDate
private Date updatedAt;
}
9.2 乐观锁(@Version)
@Entity
public class Product {
@Id
private Long id;
private String name;
@Version
private Integer version; // 乐观锁版本号
}
十、完整使用案例
10.1 用户管理系统
实体类
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String username;
private String email;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Order> orders = new ArrayList<>();
}
Repository 接口
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
Service 层
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User createUser(String username, String email) {
User user = new User();
user.setUsername(username);
user.setEmail(email);
return userRepository.save(user);
}
public List<User> searchByEmail(String email) {
return userRepository.findByEmailContaining(email);
}
}
Controller 层
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public User createUser(@RequestBody UserDTO dto) {
return userService.createUser(dto.getUsername(), dto.getEmail());
}
@GetMapping("/search")
public List<User> searchByEmail(@RequestParam String email) {
return userService.searchByEmail(email);
}
}
十一、常见问题与解决方案
| 问题 | 解决方案 |
|---|---|
| LazyInitializationException | 使用 JOIN FETCH 或在事务中访问关联字段 |
| StaleObjectStateException | 检查乐观锁版本号是否冲突 |
| 主键冲突 | 检查 @GeneratedValue 策略 |
| 查询性能差 | 使用分页(Pageable)或缓存 |
十二、总结
JPA 通过注解和 API 简化了数据库操作,但合理使用需要掌握以下要点:
- 实体映射:正确使用注解定义表结构。
- 查询优化:避免 N+1 问题,合理使用缓存。
- 事务管理:保证数据一致性。
- 性能调优:结合懒加载、缓存和索引。
在实际开发中,推荐使用 Spring Data JPA 来简化代码,但理解底层 JPA 原理对排查问题至关重要。
1218

被折叠的 条评论
为什么被折叠?



