找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

697

积分

0

好友

87

主题
发表于 3 小时前 | 查看: 0| 回复: 0

在国内,使用 JPA 的开发者相对较少,甚至出现了 Hibernate 将被 MyBatis 替代的说法。各大技术社区中,“Hibernate 已死”的论调也不鲜见。然而在欧美地区,类似 MyBatis 这样的 SQL 模板工具几乎没有市场。同时,关于 JPA 的中文资料也较为匮乏,网上能找到的教程大多浅尝辄止,一旦遇到稍复杂的需求便难寻解决方案。

我使用 JPA 已有多年,也看过许多人对它的吐槽。本文将结合我遇到的实际问题以及在各大论坛看到的常见疑问,做一个总结,希望能帮助大家更客观地看待 JPA 这一技术。

1. 简介

1.1 JPA 和 Hibernate 的关系

JPA(Java Persistence API)是 Java 官方的 ORM 标准,而 Hibernate 是 JPA 规范的一个实现。它们之间的关系类似于 Servlet 规范与 Tomcat 容器,或者 JDBC 接口与 MySQL Connector/J 驱动。

JPA 主要定义了以下内容:

  • 如何用注解(如 @Entity@Id@OneToMany)描述对象与数据库表的映射关系。
  • 如何通过 EntityManager 接口进行增删改查操作。
  • JPQL(Java Persistence Query Language)的语法。这是一种面向对象的查询语言,其操作对象是实体,而非直接操作数据库表。

除了 Hibernate,还有其他 JPA 实现,例如 EclipseLink 和 OpenJPA。但在实现成熟度上,Hibernate 更胜一筹,并且也是 Spring 框架默认集成的 JPA 实现。

1.2. 使用 JPA 的优势

  • 完整的 ORM:实现了对象与关系数据库的映射,支持继承、关联、生命周期回调等面向对象特性。
  • 类型安全与 IDE 支持:基于实体对象和接口方法进行查询,避免了字符串拼接,编译期即可发现错误,IDE 也能提供更好的自动补全和重构支持。
  • 数据库方言透明:通过配置即可切换不同的数据库,通常无需修改 SQL 或 XML 映射文件(某些复杂场景下可能失效)。
  • Spring 官方强力支持:对于广大 Java 开发者而言,Spring 生态的选择值得信赖,这为JPA的普及和应用提供了坚实基础。
  • 对 GraalVM Native 的完美支持:在 Spring 框架体系下,若希望应用转换为原生镜像以获得极致启动速度和内存占用,选择 JPA 通常更为顺畅。

1.3. 使用 JPA 的劣势

  • 学习成本较高:注解体系庞杂,且从数据库表反向生成实体代码的质量往往不佳。建议始终坚持从代码模型生成数据库表结构。
  • 复杂查询编写有挑战:对于非常复杂的嵌套关联查询,JPQL 或 Criteria API 可能显得有些繁琐,有时需要借助 QueryDSL 等第三方工具。

2. 起步

下面我们通过一个完整的 User(用户)和 Role(角色)模型示例,来构建一个简单的 JPA 工程。

2.1 依赖引入

本例基于 Spring Boot 4.0,部分依赖写法与 Spring Boot 3.x 可能有所不同。

在 Spring Boot 项目中引入 Spring Data JPA 非常简单,只需添加对应的 starter 依赖即可。

dependencies {

    // 添加spring boot data jpa依赖
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")

    // 添加h2数据库控制台依赖
    implementation("org.springframework.boot:spring-boot-h2console")

    // 添加h2数据库依赖
    runtimeOnly("com.h2database:h2")
}

其他依赖说明:

  • spring-boot-h2console:本示例使用 H2 作为内存数据库,此依赖提供了一个网页控制台用于查看和管理 H2 数据库。
  • com.h2database:h2:H2 数据库的 JDBC 驱动。

2.2 添加配置文件

application.yml 中加入相关配置。

spring:
  datasource:
    url: "jdbc:h2:~/test"
    username: sa
    password: ""
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
  h2:
    console:
      enabled: true

基于以上配置,可以实现如下效果:

  • 项目启动时,连接到本地的 H2 数据库,数据库文件将持久化在 ~/test.mv.db 中。
  • 数据库连接用户名为 sa,密码为空。
  • 项目启动时,会根据实体类的定义,自动更新数据库表结构。
  • 程序运行时,会在控制台打印出执行的 SQL 语句。

2.3 构建 User 模型

在 model 包中,构建与数据库表对应的 User 实体类。

@Entity
@Table(name = "t_user_")
class User {
    @Id
    @Column(name = "id_")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long? = null

    @Column(name = "username_", length = 50)
    var username: String = ""

    @Column(name = "password_", length = 64)
    var password: String = ""

    @Column(name = "created_time_")
    val cratedTime: ZonedDateTime = ZonedDateTime.now()

    @Column(name = "last_modified_time_")
    var lastModifiedTime: ZonedDateTime = ZonedDateTime.now()
}

这个模型就是一个添加了 JPA 注解的普通 Kotlin 数据类。这些注解来自 jakarta.persistence 包,是 JPA 的核心:

  • @Entity 必需,标识该类是一个 JPA 实体。
  • @Id 必需,标识该属性对应数据库表的主键。
  • @Table 可选,用于配置实体对应的数据库表名、索引等信息。
  • @Column 可选,用于配置属性对应的数据库字段名、长度、精度等。
  • @GeneratedValue 可选,标识主键的生成策略(如自增)。

我们不需要在 @Column 中显式指定数据库字段类型,JPA 提供商会自动完成 Java/Kotlin 基础类型到数据库类型的映射。

2.4 创建 Repository 接口

接下来,创建一个接口并继承 JpaRepository。之后便可以在项目中注入这个接口的实例,并使用其内置方法实现基本的增删改查。

interface UserRepository : JpaRepository<User, Long>

2.5 创建相应的 Service

我们通常在 Service 层定义业务方法,并在此注入 Repository 来实现业务逻辑。

  • UserService.kt (接口)
interface UserService {
    fun save(request: UserRequest): UserDto
    fun update(id: Long, request: UserRequest): UserDto
    fun delete(id: Long)
    fun findById(id: Long): UserDto?
    fun find(pageable: Pageable): PagedModel<UserDto>
}
  • UserServiceImpl.kt (实现类)
@Service
class UserServiceImpl(
    private val userRepository: UserRepository
) : UserService {

    @Transactional
    override fun save(request: UserRequest): UserDto {
        val user = User().apply {
            username = request.username
            password = request.password
            lastModifiedTime = ZonedDateTime.now()
        }
        val savedUser = userRepository.save(user)
        return UserDto(savedUser)
    }

    @Transactional
    override fun update(id: Long, request: UserRequest): UserDto {
        val user = userRepository.findByIdOrNull(id) ?: throw IllegalArgumentException("用户不存在")
        user.apply {
            username = request.username
            password = request.password
            lastModifiedTime = ZonedDateTime.now()
        }
        val updatedUser = userRepository.save(user)
        return UserDto(updatedUser)
    }

    @Transactional
    override fun delete(id: Long) {
        userRepository.deleteById(id)
    }

    @Transactional(readOnly = true)
    override fun findById(id: Long): UserDto? {
        return userRepository.findByIdOrNull(id)?.let { UserDto(it) }
    }

    @Transactional(readOnly = true)
    override fun find(pageable: Pageable): PagedModel<UserDto> {
        return userRepository.findAll(pageable).map {
            UserDto.Companion(it)
        }.let {
            PagedModel(it)
        }
    }
}

整体逻辑非常清晰:构建实体对象、赋值、调用 Repository 的 savefindByIddeleteById 等方法完成持久化操作。

最后,在 Controller 中调用 Service 即可提供 RESTful API。

@RestController
@RequestMapping("/users")
class UserController(
    private val userService: UserService
) {

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    fun save(@RequestBody request: UserRequest): UserDto {
        return userService.save(request)
    }

    @PutMapping("/{id}")
    @ResponseStatus(HttpStatus.CREATED)
    fun update(@PathVariable id: Long, @RequestBody request: UserRequest): UserDto {
        return userService.update(id, request)
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    fun delete(@PathVariable id: Long) {
        userService.delete(id)
    }

    @GetMapping("/{id}")
    fun findById(@PathVariable id: Long): UserDto? {
        return userService.findById(id)
    }

    @GetMapping
    fun find(
        pageable: Pageable
    ): PagedModel<UserDto> {
        return userService.find(pageable)
    }
}

本文演示了 Spring Data JPA 结合 Spring Boot 4.0 的快速入门。后续将持续更新 JPA 的更多进阶应用,例如复杂关联映射、动态查询、性能优化等内容。完整的示例项目代码可在 https://github.com/ldwqh0/jpa-demo.git 获取。欢迎在云栈社区与其他开发者交流探讨 JPA 的使用心得与最佳实践。




上一篇:Meta STEM模型:让Transformer通过查表学习,实现算力效率与知识容量双提升
下一篇:探索顶尖设计理念:从Google Design到动态视觉奖项与长效设计哲学
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-24 16:34 , Processed in 0.231859 second(s), 43 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表