JPA 的说明和使用
JPA 的说明和使用
JPA(Java Persistence API)是Java的ORM规范,用面向对象的方式操作数据库,不用写繁琐的SQL。
核心思想:对象即数据
// 操作对象 = 操作数据库
User user = new User("张三", "zhangsan@email.com");
userRepository.save(user); // 自动生成 INSERT SQLList<User> users = userRepository.findByName("张三"); // 自动生成 SELECT SQL
一、JPA详细使用
1.实体类定义
@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.关联关系配置
// 一对多关系
@Entity
public class User {@Idprivate Long id;// 一个用户有多个订单@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)private List<Order> orders = new ArrayList<>();
}@Entity
public class Order {@Idprivate Long id;// 多个订单属于一个用户@ManyToOne@JoinColumn(name = "user_id") // 外键private User user;private BigDecimal amount;
}
二、JPA常用方法大全
1.基础CRUD方法
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.方法名派生查询(最强大!)
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.自定义查询
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.复杂查询示例
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.服务层使用实例
@Service
@Transactional
public class UserService {@Autowiredprivate 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方式
// Repository 接口
List<User> findByAgeBetweenAndStatusOrderByCreateTimeDesc(Integer minAge, Integer maxAge, UserStatus status);// 使用
List<User> users = userRepository.findByAgeBetweenAndStatusOrderByCreateTimeDesc(18, 30, UserStatus.ACTIVE);
Mybatis方式
<!-- 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>
// 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.新项目推荐
# 微服务架构选择:
用户服务: JPA # 业务复杂,对象关系多
订单服务: JPA # 事务要求高
报表服务: MyBatis # 复杂查询,需要SQL优化
2.混合使用策略
// 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.注意事务管理:保证数据一致性
