破局全栈:前端开发的Java入门实战记录—JPA(2)

Spring Boot + JPA + MySQL:前端转全栈学 JPA 的实践记录

这篇是前端转全栈 从 0 上手 JPA 的全过程,全是踩过的坑 + 真实跑通的代码。


0. 前端转后端视角:JPA 是什么

JPA = Java 世界的 ORM(对象关系映射) 。和你写 Node 后端时用的 TypeORM / Sequelize 是同类东西:操作对象就操作数据库,不用手写单表 SQL

先给三个定位,帮你快速建立直觉:

你熟悉的 Java 里的 一句话定位
SELECT * FROM user WHERE name='xx'(原生 SQL) JDBC 最底层:你自己拼 SQL,自己拼连接,自己处理结果集
TypeORM / Sequelize JPA(Spring Data JPA) ORM:把数据库映射成对象,不用写单表 SQL
--- MyBatis / MyBatis-Plus SQL 友好:帮你写 SQL/XML,单表 CRUD 也不用写 SQL,复杂 SQL 你可以自己写

一句话结论(后面第 6 节详细对比):做 CRUD 多、业务规整的业务(用户、权限、字典),JPA 爽;多表联查/复杂统计 SQL,MyBatis 灵活


1. 项目长什么样?

先把项目目录摆出来,和 study-h2(MyBatis-Plus 版)对齐,方便对比:

bash 复制代码
study-jpa/
 pom.xml                                     # Maven 依赖
 src/main/
     java/com/wy/study/jpa/
        JPAApp.java                         # 启动类(main 在这里)
        controller/
           JPARoleController.java          # HTTP 接口(对外面)
        entity/
           JPARoleEntity.java              # 角色表对应的 Java 对象(核心!)
        repository/
           JPARoleRepository.java          # 数据访问层(JPA 的 Mapper)
        service/
            JPARoleService.java             # 业务层
     resources/
         application.yml                     # 数据库 + JPA 配置

和 MyBatis-Plus 的项目结构几乎一样(Controller/Service/Entity),只是 Mapper 改叫 Repository。


案例代码地址:gitee.com/banmaxiaoba...

2. 第一步:加依赖(JPA 是官方 Starter,比 MyBatis 更"正统")

父 pom 统一管版本,子模块只写坐标:

xml 复制代码
<dependencies>
    <!-- Spring Data JPA:官方 ORM Starter,自带 Hibernate -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

就这两个。Spring Data JPA 内部自动依赖 Hibernate(实现 ORM 的底层),你不用自己加 Hibernate 依赖。


3. 第二步:连 MySQL + 配 JPA 关键参数

打开 src/main/resources/application.yml

yaml 复制代码
server:
  port: 9204                      # 端口(和 study-h2 的 9203 错开)
  servlet:
    context-path: /jpa             # 所有接口前面都要加 /jpa

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/demo?useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root

  jpa:
    show-sql: true                 #  强制开:控制台打印 Hibernate 生成的 SQL,排错神器
    open-in-view: false            #  必须关:Spring 默认会开,生产会炸(懒加载问题)
    hibernate:
      ddl-auto: none               # 建表策略,后面讲 4 种模式
    properties:
      hibernate:
        format_sql: true           # SQL 格式化,好看
        use_sql_comments: true     # 加注释,知道 SQL 来自哪个方法

重要:ddl-auto 四种模式(你可能只见过 update)

模式 作用 场景
none 完全不管 DDL 生产环境首选,用 Flyway/Liquibase 管表
update 自动更新表结构(加列不加删列) 开发环境省事,改实体自动改表
create 每次启动重建表(数据清零) 临时 Demo 用,不敢生产
validate 只校验实体和表是否匹配 启动时检查,不修改表

重要:命名策略(踩过的坑)

SpringPhysicalNamingStrategy(Spring Boot 默认)会把实体的 camelCase 转 snake_case:

  • 实体字段 createTime 数据库列 create_time
  • 实体字段 roleId 数据库列 role_id

如果你数据库表是 驼峰列名 (比如旧项目、别的团队建的表),就要在实体每个字段加 @Column(name="createTime"),或者全局关掉命名策略(我用了后者)。


4. 重点来了:JPA 的三层结构(和 MyBatis 分层一致,但 Repository 更爽)

还是"点菜类比",和 study-h2 的服务员/领班/厨师对齐,一眼明白:

markdown 复制代码
  Controller(服务员)                                
   接收 HTTP 请求,转给 Service                      
    对应:JPARoleController.java                      

                        调用

  Service(后厨领班)                                
   业务流程控制,事务、缓存、发消息                    
    对应:JPARoleService.java                        

                        调用

  Repository(厨师,JPA 版)                         
   继承 JpaRepository,**不用写单表 SQL!**           
    对应:JPARoleRepository.java                     

和 MyBatis-Plus 的核心不同:两者继承一下都有单表 CRUD,但拿 SQL 的方式不一样:

  • JPA:ORM 黑盒,Hibernate 自动生成 SQL,你看不到也管不了(可开 show-sql 看,复杂查询用 @Query 写 JPQL/原生 SQL)
  • MyBatis-Plus:SQL 友好,默认 CRUD 也不用写,但复杂 SQL 你可以直接写注解 @Select 或 XML,所见即所得

各自适合什么场景?

sql 复制代码
你的业务:
├─ 主要是用户、权限、字典这种 CRUD 多、业务规整 → JPA(黑盒自动 SQL 够了)
├─ 有大量多表联查、复杂统计 SQL(报表、大数据)→ MyBatis-Plus(SQL 友好)
└─ 两者都有 → 同项目可以混用:简单表 JPA,复杂查询 MyBatis(Spring Boot 两者兼容)

派生查询(方法名即 SQL)会生成什么样的真实 SQL?

Repository 里写一个方法,Hibernate 自动拼 SQL,不用你写一行 SQL:

1. JPARoleEntity findByName(String name);

sql 复制代码
-- Hibernate 自动生成(带表别名,MySQL limit 1):
select jparoleent0_.id as id1_0_, jparoleent0_.code as code2_0_, 
       jparoleent0_.create_time as create_t3_0_, jparoleent0_.description as descript4_0_, 
       jparoleent0_.modify_time as modify_t5_0_, jparoleent0_.name as name6_0_, 
       jparoleent0_.status as status7_0_ 
from study_jpa_role jparoleent0_ 
where jparoleent0_.name = ? 
limit ?
-- 最终参数:name='系统管理员', limit=1

2. List<JPARoleEntity> findRoleEntitiesByName(String name);

sql 复制代码
-- 和上面一样,但没有 limit(因为返回 List):
select jparoleent0_.id as id1_0_, ... 
from study_jpa_role jparoleent0_ 
where jparoleent0_.name = ?

3. 模糊查询(@Query 写 JPQL)

java 复制代码
@Query("select e from JPARoleEntity e where e.code like concat('%', :code, '%')")
List<JPARoleEntity> findByCodeLike(@Param("code") String code);
sql 复制代码
-- JPQL 自动转原生 SQL(写的是实体名 JPARoleEntity,转成表名 study_jpa_role):
select jparoleent0_.id as id1_0_, ... 
from study_jpa_role jparoleent0_ 
where jparoleent0_.code like concat('%', ?, '%')
-- 参数:code='te' → 匹配 teacher

注意:Hibernate 生成的 SQL 带很长的别名(jparoleent0_id1_0_),这是它的内部实现,不影响 MySQL 执行。开 show-sql=true + format_sql=true 可以在控制台看到。

相关推荐
代码丰2 小时前
RAG 系统如何实现全链路追踪:AOP 埋点与流式调用追踪实践
后端
小码编匠2 小时前
C# 工控上位机必备:数据转换工具类与十个核心模块
后端·c#·.net
神奇小汤圆3 小时前
一文读懂 OpenAI Codex 源码的原理、架构与未来
后端
道友可好3 小时前
AI 是最好的混乱放大器:代码熵管理实战
前端·人工智能·后端
掘金者阿豪4 小时前
写了很多内容后,我还是决定给自己搭一个Typecho博客
后端
Younglina4 小时前
打了3年羽毛球球才发现:我对自己的装备和胜率一无所知
前端·后端
Go_error5 小时前
Datatypes:Go 轻松支持数据库JSON类型
后端·go
长大19885 小时前
新手必踩 Redis 10 个低级坑:过期时间、KEYS 命令、持久化误区
后端