JPA 的说明和使用
JPA(Java Persistence API)是Java的ORM规范,用面向对象的方式操作数据库,不用写繁琐的SQL。
核心思想:对象即数据
java
// 操作对象 = 操作数据库
User user = new User("张三", "zhangsan@email.com");
userRepository.save(user); // 自动生成 INSERT SQL
List<User> users = userRepository.findByName("张三"); // 自动生成 SELECT SQL
一、JPA详细使用
1.实体类定义
java
@Entity
@Table(name = "users") // 对应数据库表
public class User {
// 写法一:主键自增
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 写法二:主键uuid
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
@Column(name = "ID", length = 50, nullable = false)
private String id;
@Column(name = "user_name", length = 50, unique = true, nullable = false)
private String username;
private String email;
private Integer age;
@Enumerated(EnumType.STRING) // 枚举存储为字符串
private UserStatus status;
@CreationTimestamp // 自动设置创建时间
private LocalDateTime createTime;
@UpdateTimestamp // 自动更新修改时间
private LocalDateTime updateTime;
// 必须有无参构造器
public User() {}
// 有参构造器
public User(String username, String email) {
this.username = username;
this.email = email;
}
// getter/setter 省略...
}
enum UserStatus {
ACTIVE, INACTIVE, DELETED
}
2.关联关系配置
java
// 一对多关系
@Entity
public class User {
@Id
private Long id;
// 一个用户有多个订单
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Order> orders = new ArrayList<>();
}
@Entity
public class Order {
@Id
private Long id;
// 多个订单属于一个用户
@ManyToOne
@JoinColumn(name = "user_id") // 外键
private User user;
private BigDecimal amount;
}
二、JPA常用方法大全
1.基础CRUD方法
java
public interface UserRepository extends JpaRepository<User, Long> {
// === 增删改 ===
// 保存(新增或更新)
User save(User user);
List<User> saveAll(List<User> users);
// 删除
void delete(User user);
void deleteById(Long id);
void deleteAll();
void deleteAll(List<User> users);
// === 查询 ===
// 根据ID查询
Optional<User> findById(Long id);
boolean existsById(Long id);
// 查询所有
List<User> findAll();
List<User> findAllById(List<Long> ids);
// 计数
long count();
}
2.方法名派生查询(最强大!)
java
public interface UserRepository extends JpaRepository<User, Long> {
// === 基本查询 ===
List<User> findByUsername(String username);
User findFirstByUsername(String username); // 查询第一条
Optional<User> findOneByUsername(String username);
// === 条件查询 ===
List<User> findByAgeGreaterThan(Integer age); // 大于
List<User> findByAgeLessThanEqual(Integer age); // 小于等于
List<User> findByAgeBetween(Integer start, Integer end); // 区间
List<User> findByUsernameLike(String pattern); // 模糊查询
List<User> findByEmailContaining(String keyword); // 包含
List<User> findByUsernameStartingWith(String prefix); // 开头匹配
List<User> findByUsernameEndingWith(String suffix); // 结尾匹配
// === 多条件查询 ===
List<User> findByUsernameAndAge(String username, Integer age);
List<User> findByUsernameOrEmail(String username, String email);
List<User> findByAgeGreaterThanAndStatus(Integer age, UserStatus status);
// === 空值检查 ===
List<User> findByEmailIsNull();
List<User> findByEmailIsNotNull();
// === IN 查询 ===
List<User> findByStatusIn(List<UserStatus> statusList);
List<User> findByAgeIn(Integer[] ages);
// === 排序 ===
List<User> findByStatusOrderByCreateTimeDesc(UserStatus status);
List<User> findByAgeGreaterThanOrderByAgeAscCreateTimeDesc(Integer age);
// === 分页 ===
Page<User> findByStatus(UserStatus status, Pageable pageable);
Slice<User> findByAgeGreaterThan(Integer age, Pageable pageable);
// === 限制数量 ===
List<User> findFirst10ByStatus(UserStatus status);
List<User> findTop5ByAgeGreaterThanOrderByAgeDesc(Integer age);
}
3.自定义查询
java
public interface UserRepository extends JpaRepository<User, Long> {
// === @Query 注解查询 ===
// JPQL 查询(面向对象)
@Query("SELECT u FROM User u WHERE u.age > :age AND u.status = :status")
List<User> findAdultUsers(@Param("age") Integer age,
@Param("status") UserStatus status);
// 原生 SQL 查询
@Query(value = "SELECT * FROM users u WHERE u.create_time > :date",
nativeQuery = true)
List<User> findUsersAfterDate(@Param("date") LocalDateTime date);
// 投影查询(只返回部分字段)
@Query("SELECT u.username, u.email FROM User u WHERE u.status = 'ACTIVE'")
List<Object[]> findActiveUserInfo();
// DTO 投影查询
@Query("SELECT new com.example.UserDTO(u.username, u.email) FROM User u")
List<UserDTO> findUserDTOs();
// === 修改操作 ===
@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("DELETE FROM User u WHERE u.status = :status")
int deleteByStatus(@Param("status") UserStatus status);
}
4.复杂查询示例
java
public interface UserRepository extends JpaRepository<User, Long> {
// 复杂分页查询
@Query("SELECT u FROM User u WHERE " +
"(:username IS NULL OR u.username LIKE %:username%) AND " +
"(:minAge IS NULL OR u.age >= :minAge) AND " +
"(:maxAge IS NULL OR u.age <= :maxAge)")
Page<User> searchUsers(@Param("username") String username,
@Param("minAge") Integer minAge,
@Param("maxAge") Integer maxAge,
Pageable pageable);
// 统计查询
@Query("SELECT COUNT(u) FROM User u WHERE u.status = :status")
long countByStatus(@Param("status") UserStatus status);
@Query("SELECT u.status, COUNT(u) FROM User u GROUP BY u.status")
List<Object[]> countUsersByStatus();
}
5.服务层使用实例
java
@Service
@Transactional
public class UserService {
@Autowired
private UserRepository userRepository;
public void demoUsage() {
// 1. 新增
User user = new User("李四", "lisi@email.com");
user.setAge(25);
userRepository.save(user);
// 2. 查询
List<User> youngUsers = userRepository.findByAgeLessThanEqual(30);
Optional<User> userOpt = userRepository.findByUsername("李四");
// 3. 分页查询
Page<User> userPage = userRepository.findByStatus(
UserStatus.ACTIVE,
PageRequest.of(0, 10, Sort.by("createTime").descending())
);
// 4. 更新
user.setEmail("new_email@example.com");
userRepository.save(user); // 自动更新
// 5. 删除
userRepository.delete(user);
}
}
三、JPA vs Mybatis详细对比
1.开发模式对比
| 方面 | JPA | Mybatis |
|---|---|---|
| 思维模式 | 面向对象 | 面向SQL |
| 查询方式 | 方法名/JPQL | XML/注解SQL |
| 数据库变更 | 影响较小 | 影响较大 |
| 学习曲线 | 较陡峭 | 较平缓 |
2.代码对比实例
JPA方式
java
// Repository 接口
List<User> findByAgeBetweenAndStatusOrderByCreateTimeDesc(
Integer minAge, Integer maxAge, UserStatus status);
// 使用
List<User> users = userRepository.findByAgeBetweenAndStatusOrderByCreateTimeDesc(
18, 30, UserStatus.ACTIVE);
Mybatis方式
xml
<!-- Mapper XML -->
<select id="selectUsersByCondition" resultType="User">
SELECT * FROM users
WHERE age BETWEEN #{minAge} AND #{maxAge}
AND status = #{status}
ORDER BY create_time DESC
</select>
java
// Mapper 接口
List<User> selectUsersByCondition(@Param("minAge") Integer minAge,
@Param("maxAge") Integer maxAge,
@Param("status") String status);
3.性能对比
| 场景 | JPA | Mybatis |
|---|---|---|
| 简单CRUD | 效率极高 | 需要写SQL |
| 复杂查询 | JPQL可能性能差 | SQL可精细优化 |
| 关联查询 | 容易N+1问题 | 可手动优化JOIN |
| 批量操作 | 需要配置 | 直接批量SQL |
4.适用场景总结
选择JPA
1.业务模型复杂,对象关系多
2.需要快速开发标准CRUD
3.团队熟悉面向对象设计
4.可能更换数据库
选择Mybatis
1.复杂SQL、存储过程需求多
2.需要精细控制SQL性能
3.遗留数据库表结构复杂
4.团队SQL能力强
四、实际建议
1.新项目推荐
yaml
# 微服务架构选择:
用户服务: JPA # 业务复杂,对象关系多
订单服务: JPA # 事务要求高
报表服务: MyBatis # 复杂查询,需要SQL优化
2.混合使用策略
java
// JPA 处理标准业务
public interface UserRepository extends JpaRepository<User, Long> {
// 标准CRUD
}
// MyBatis 处理复杂查询
public interface UserReportMapper {
// 复杂报表查询
List<UserReportDTO> getUserComplexReport(ReportQuery query);
}
总结
JPA核心优势:
1.开发效率:方法名即查询,减少80%的SQL编写
2.维护性:对象变更自动同步到数据库
3.类型安全:编译时检查,减少运行时错误
4.标准化:代码更规范,易于团队协作
使用建议:
1.掌握方法名规则:解决大部分查询需求
2.合理使用关联:避免N+1查询问题
3.复杂查询用@Query:保持代码清晰
4.注意事务管理:保证数据一致性