JPA是什么
JPA = Java Persistence API (Java持久化规范)
JPA不是框架,而是一套“标准接口规范”
它规定了:
- 实体怎么写
- 怎么把Java对象存进数据库
- 怎么把从数据库查出来的再变回对象
真正去实现的是实现框架,比如:
- Hibernate(最常见)
- EclipseLink
- OpenJPA
平时我们说“用 JPA”,90% 实际是在用 Hibernate + JPA 规范
JPA想解决什么问题
不用JPA -> 纯SQL/MyBatis
需要写很多
SELECT * FROM user WHERE id = 1;
然后:
- 手动映射成 Java 对象
- 表结构变了 → SQL 改一堆
- 业务里 SQL 越来越多
JPA的思路:你操作的是对象,不是表
`User user = entityManager.find(User.class, 1L);`
JPA 帮你做了:
- SQL 生成
- 字段 ↔ 属性映射
- 事务管理
- 对象状态管理
JPA的核心概念
Entity(实体)
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
}
@Entity:这是一个会映射到表的对象@Id:主键- 一个实体 ≈ 一张表
EntityManager(核心)
JPA的所有操作都围绕它:
entityManager.persist(user); // 新增
entityManager.find(User.class, 1L); // 查
entityManager.remove(user); // 删
在 Spring Boot 里你一般用不到它本体,因为有更好用的东西↓
Repository(你常见的那个)
public interface UserRepository extends JpaRepository<User, Long> {
}
你什么都没写,却能直接用:
userRepository.save(user);
userRepository.findById(1L);
userRepository.deleteById(1L);
这就是 Spring Data JPA:
- 在 JPA 之上
- 帮你封装了 EntityManager
- 自动生成常见 CRUD
JPA的优点/缺点
优点
- CRUD 极快(写得非常少)
- 对象化思维,业务代码干净
- 事务、缓存、懒加载都帮你管了
- 中小项目、后台系统很省心
缺点
- 复杂 SQL 很难受
- 性能不可控(N+1 问题)
- JPQL 学习成本
- 出问题时不好定位
和MyBatis的区别
MyBatis和JPA是两条完全不同的技术路线
MyBatis = 写 SQL 的 Java 框架 (你告诉我你要什么对象,我帮你处理数据库。) = ORM(对象关系映射) JPA = 用对象“代替”SQL 的规范 (你把 SQL 写清楚,我只负责帮你跑。)
ORM
ORM是对象关系映射,简单来说,ORM就是把数据库里的表,映射成Java对象,让你写操作对象而不是写SQL
为什么要用ORM
- 数据库和对象的差异
- 数据库是表格形式(行列)
- Java是对象形式(类,属性,方法)
如果不做映射,每次都要:
// SQL 查询
SELECT * FROM user WHERE id = 1;
// 手动把结果变成对象
User u = new User();
u.setId(rs.getLong("id"));
u.setName(rs.getString("name"));
麻烦,容易出错,表结构一改就要改很多代码
ORM是什么
- 映射表和对象
- 生成 SQL
- 对象状态管理
- 支持事务、缓存、关联关系
用了ORM后,可以直接:
User user = entityManager.find(User.class, 1L);
user.setName("Tom"); // JPA 会自动生成 update
不用再手写SQL
常见 ORM 框架
| 框架 | 特点 |
|---|---|
| Hibernate | 最经典的 JPA 实现,功能全 |
| MyBatis (严格来说不是 ORM) | 半 ORM,SQL 可控,结果映射对象 |
| EclipseLink / OpenJPA | 其他 JPA 实现 |
注意:严格意义上,MyBatis 更像 SQL 映射框架,只做结果集映射,不做对象状态管理。
什么是对象状态管理
在 JPA 或 ORM 框架里,每个实体对象(Entity)都有自己的状态,JPA 会跟踪这个状态,并在合适的时候自动生成 SQL,把对象的变化同步到数据库。
对象状态管理 = 框架帮你跟踪对象“现在是什么状态”,自动决定什么时候插入、更新或删除数据库
demo
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
</parent>
<dependencies>
<!-- JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- H2 内存数据库 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
- Spring Boot 自带 依赖管理,不需要特意指定jpa和h2的版本,它会帮你选合适的版本。
- JPA依赖:JPA + Hibernate + Spring Data
- h2: H2 Database 是一个 轻量级、纯 Java 的数据库,通常用于测试,不需要安装mySQL
spring.datasource.url=jdbc:h2:mem:testdb
mem:testdb 就表示数据库在内存中,程序关闭数据就消失
application.properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=root
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
- ddl-auto:update : 如果表不存在会创建表,如果字段不存在会新增字段,如果字段类型变了会尝试修改,但不会删除表和字段
| 值 | 行为 | 常用场景 |
|---|---|---|
none | 什么都不做 | 生产(推荐) |
validate | 只校验,不改 | 生产 / 预发布 |
update | 自动改表 | 本地开发 |
create | 每次删表重建 | 测试 |
create-drop | 启动建,关闭删 | 集成测试 |
- show-sql: 控制台能看到生成的sql
定义实体
@Entity
@Table(name = "refund_order")
public class RefundOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "out_trade_no", nullable = false)
private String outTradeNo;
private String flowCode;
@Temporal(TemporalType.TIMESTAMP)
@CreationTimestamp
private Date createdAt;
private BigDecimal money;
private Integer payType;
private String hospitalCode;
public RefundOrder() {}
public RefundOrder(String outTradeNo){
this.outTradeNo = outTradeNo;
}
@Override
public String toString(){
return "RefundOrder{id=" + id + ", outTradeNo=" + outTradeNo + "}";
}
}
类注解
@Entity:告诉 JPA:这个类是一个「实体类」,要映射到数据库表。JPA启动时会扫描这个类,允许你用这个Java对象操作数据库记录@Table(name = "users"): 指定这个实体对应的数据库表名是users,可以不写name,默认表名=类名
属性注解
-
@Id: 声明这是主键,每个@Entity必须有且只有一个@Id,否则报错 -
@GeneratedValue: 主键的值由 JPA / 数据库自动生成 策略 | 含义 | | -------- | -------------------------- | | IDENTITY | 数据库自增(MySQL 最常用) | | AUTO | JPA 自动选择 | | SEQUENCE | 数据库序列(Oracle / PostgreSQL) | | TABLE | 用表模拟(很少用)| -
@Column(name = "name", nullable = false): 映射数据库中的列,name:数据库列名,可以不加,默认命名策略会将userName -> user_name。 nullable: 是否允许为null。还有length,unique等 -
@Temporal(TemporalType.TIMESTAMP):处理“时间类型映射”的注解,只和java.util.Date / Calendar有关 -
@CreationTimestamp:在 INSERT 时自动填充当前时间,类似的:@UpdateTimestamp
| 数据库类型 | 含义 |
|---|---|
DATE | 只有年月日 |
TIME | 只有时分秒 |
TIMESTAMP | 年月日 + 时分秒 |
Repository
public interface RefundOrderRepository extends JpaRepository<RefundOrder, Long> {
}
RefundOrderRepository:数据访问接口,Spring在运行时会自动帮你生成实现类,用来操作refund_order表
-extends JpaRepository<RefundOrder, Long>: 泛型的第一个参数->RefundOrder,声明了这个Repository就是用来操作refundOrder这个实体类的。第二个参数->Long,对应实体类里的private Long id;也就是主键的字段类型
JPA为你实现了哪些类
save(entity) // insert / update
findById(id) // select by id
findAll() // select *
deleteById(id) // delete
count() // select count(*)
existsById(id) // 是否存在
Spring在启动时生成了一个代理类实现这些方法
- save是插入还是更新主要取决于id有没有值
默认生成之外的方法
方法名派生(实现最简单)
- 按字段查
RefundOrder findByOutTradeNo(String outTradeNo);
SpringData会自动生成:
select * from refund_order where out_trade_no = ?
- 多个条件
List<RefundOrder> findByHospitalCodeAndPayType(
String hospitalCode, Integer payType);
- 范围/排序
List<RefundOrder> findByCreatedAtBetween(
LocalDateTime start, LocalDateTime end);
List<RefundOrder> findByHospitalCodeOrderByCreatedAtDesc(
String hospitalCode);
核心规则:
- findBy
- 字段名(实体类的字段名)
- And/Or/Between/Like/In/OrderBy
@Query(JPQL)
注意这里是用JPQL,不是SQL
JPQL特点:
- 面向Entity
- 不写表名
- 不写列名
- 写的是类名+字段名
示例:
@Query("select r from RefundOrder r where r.money > :amount")
List<RefundOrder> findBigRefund(@Param("amount") BigDecimal amount);
Hibernate 会自动翻译成 SQL。
适合:join,子查询,复杂条件
写SQL
示例:
@Query("select r from RefundOrder r where r.money > :amount")
List<RefundOrder> findBigRefund(@Param("amount") BigDecimal amount);
适合:数据库特有函数,复杂 SQL,legacy 表,性能关键路径
main
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
CommandLineRunner demo(RefundOrderRepository refundOrderRepository){
return args -> {
RefundOrder order = new RefundOrder("1234");
refundOrderRepository.save(order);
System.out.println("All order:");
refundOrderRepository.findAll().forEach(System.out::println);
};
}
}
- CommandLineRunner:SpringBoot提供的接口,SpringBoot启动完成后,自动执行run方法
- return args:lambda表达式:
return new CommandLineRunner() {
@Override
public void run(String... args) {
...
}
};
自定义Repository
属于进阶用法
场景:动态SQL,查询条件非常复杂,查询逻辑无法表达成方法名/JPQL
RefundOrderRepositoryCustom.java
public interface RefundOrderRepositoryCustom {
List<RefundOrder> findByComplexCondition(...);
}
RefundOrderRepositoryImpl.java
public class RefundOrderRepositoryImpl
implements RefundOrderRepositoryCustom {
@PersistenceContext
private EntityManager em;
@Override
public List<RefundOrder> findByComplexCondition(...) {
// JPQL / Criteria / Native SQL
}
}
public interface RefundOrderRepository
extends JpaRepository<RefundOrder, Long>,
RefundOrderRepositoryCustom {
}