写点什么

Spring Data JPA:轻松实现数据持久化

作者:Java你猿哥
  • 2023-05-08
    湖南
  • 本文字数:5202 字

    阅读完需:约 17 分钟

Spring Data JPA 是一个流行的 Java 持久化框架,它在 Java 应用程序中提供了一种简单、一致和易于使用的方式来访问各种数据库。由于它的简单性和强大的功能,它已经成为许多开发人员的首选框架。通过使用 Spring Data JPA,开发人员可以更快地开发应用程序,减少代码量,提高代码的可读性和可维护性。本文将介绍 Spring Data JPA 的基本概念和用法,并提供一个完整的实例,帮助您更好地理解它的使用方法和优势。

什么是 Spring Data JPA

Spring Data JPA 是 Spring Framework 的一个子项目,它提供了一种易于使用的方式来访问各种关系型数据库的数据。它通过将 JPA(Java Persistence API)和 Spring Framework 的强大功能相结合,简化了数据库访问的复杂性,并提供了许多特性和工具,如基于注解的 Repository 模型、自动化查询、分页和排序支持、复杂查询 DSL 等。

Spring Data JPA 的历史可以追溯到 2011 年,当时 SpringSource(Spring Framework 的开发公司)发布了一个名为“Spring Data JPA”的项目,旨在帮助开发人员更好地利用 JPA 规范进行数据持久化。此后,该项目得到了广泛的应用和认可,并在不断地发展和完善。目前,Spring Data JPA 已成为 Java 开发人员中最流行的持久化框架之一,广泛应用于各种企业级应用程序和互联网应用程序中。

入门案例

准备数据

CREATE TABLE `user` (  `id` bigint(20) NOT NULL AUTO_INCREMENT,  `name` varchar(255) NOT NULL,  `email` varchar(255) NOT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `user` (`name`, `email`) VALUES('刘德华', 'liudehua@qq.com'),('张学友', 'zhangxueyou@qq.com'),('黎明', 'liming@qq.com'),('郭富城', 'guofucheng@qq.com'),('梁朝伟', 'liangchaowei@qq.com');
复制代码

添加依赖

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-jpa</artifactId></dependency>
复制代码

创建实体类

import lombok.Data;  import javax.persistence.*;    @Entity  @Table(name = "user")  @Data  public class User {        @Id      @GeneratedValue(strategy = GenerationType.IDENTITY)      private Long id;        @Column(nullable = false)      private String name;        @Column(nullable = false)      private String email;  }
复制代码

使用 JPA 注解 @Entity 和 @Table 来标记该类为实体类,并指定映射的表名。@Id 注解标记了实体类中的唯一标识符,@GeneratedValue 注解指定了主键生成策略。同时,我们使用 @Column 注解指定了实体类中的字段名以及是否允许为空。

@Column 注解

该注解可以应用在实体类的属性和 Getter 方法上。如果同时出现在属性和 Getter 方法上,以 Getter 方法上的注解为准。该注解的各个属性含义如下:

  • name:可选,用于指定数据库表字段名,默认值为属性名。

  • unique:可选,用于指定该字段是否唯一,默认值为 false。

  • nullable:可选,用于指定该字段是否可空,默认值为 true。

  • insertable:可选,用于指定在执行 INSERT 操作时是否插入该字段的值,默认值为 true。

  • updatable:可选,用于指定在执行 UPDATE 操作时是否更新该字段的值,默认值为 true。

  • columnDefinition:可选,用于指定该字段在数据库中的实际类型,如 VARCHAR(255)、INTEGER 等。默认情况下,根据属性类型自动生成。

  • table:可选,用于指定该字段所在的表名,默认值为主表。

  • length:可选,用于指定该字段的长度,仅在字段类型为字符串类型时生效,默认值为 255。

  • precision:可选,用于指定该字段的精度,仅在字段类型为 DECIMAL 时生效,默认值为 0。

  • scale:可选,用于指定该字段的小数位数,仅在字段类型为 DECIMAL 时生效,默认值为 0。

@GeneratedValue 注解指定了主键生成策略。常见的主键生成策略有以下几种:

  1. GenerationType.IDENTITY:主键由数据库自动生成(适用于 MySQL、PostgreSQL 等关系型数据库)。

  2. GenerationType.AUTO:JPA 自动选择合适的策略,根据底层数据库自动选择主键生成策略。

  3. GenerationType.SEQUENCE:通过序列生成主键(适用于 Oracle 数据库)。

  4. GenerationType.TABLE:使用表模拟序列生成主键。

其中 GenerationType.IDENTITY 是最常用的主键生成策略,因为它简单易用且性能不错。如果你使用的是 MySQL、PostgreSQL 等数据库,则建议使用 GenerationType.IDENTITY 生成主键。如果你使用的是 Oracle 数据库,则可以考虑使用 GenerationType.SEQUENCE 生成主键。如果你使用的是其他数据库,则可以根据具体情况选择合适的主键生成策略。

创建 Repository

public interface UserRepository extends JpaRepository<User,Long> {  }
复制代码

我们使用了 JpaRepository 接口,该接口提供了许多常用的持久化操作,如增加、删除、修改、查询等。我们只需要继承该接口,并指定实体类的类型和主键类型即可。

两个泛型 ,一个写实体类,一个写主键类型

find{By|FirstBy|AllBy}[属性名称][查询关键字][连接符][属性名称][查询关键字]...

  • By:表示该方法是一个查询方法,紧随其后的是查询条件的属性名称。

  • FirstBy:表示查询结果的第一条记录。

  • AllBy:表示查询所有符合条件的记录。

  • [属性名称]:表示查询条件的属性名称,属性名称要与实体类中的属性名称保持一致。

  • [查询关键字]:表示查询条件的关键字,如 Equals、GreaterThan、LessThan、IsNotNull 等。

  • [连接符]:表示连接条件的连接符,如 And、Or 等。

public interface UserRepository extends JpaRepository<User, Long> {
// 查询所有性别为男性的用户 List<User> findByGender(String gender);
// 查询用户名为指定值的用户 User findByUsername(String username);
// 查询用户名和密码都匹配的用户 User findByUsernameAndPassword(String username, String password);
// 查询用户年龄大于等于指定值且性别为指定值的用户 List<User> findByAgeGreaterThanEqualAndGender(int age, String gender);
}
复制代码

除此之外,还可以使用 @Query 注解自定义 JPQL 或 SQL 查询语句,例如:

@Query("SELECT u FROM User u WHERE u.username = :username")User findByUsername(@Param("username") String username);
复制代码

这个例子定义了一个自定义查询方法 findByUsername,使用 JPQL 查询语句查询用户对象。在方法上使用了 @Query 注解,并传入 JPQL 查询语句作为 value 值,其中 :username 是一个占位符,使用 @Param 注解标注其对应的方法参数。


还可以在 @Query 注解中使用原生 SQL 查询,例如:

@Query(value = "SELECT * FROM users WHERE username = :username", nativeQuery = true)User findByUsername(@Param("username") String username);
复制代码

这个例子定义了一个使用原生 SQL 查询的自定义查询方法 findByUsername。在 @Query 注解中将 nativeQuery 属性设置为 true,表示使用原生 SQL 查询,其中 :username 是一个占位符,使用 @Param 注解标注其对应的方法参数。

编写业务逻辑

@Servicepublic class UserService {    @Autowired    private UserRepository userRepository;
public List<User> findAll() { return userRepository.findAll(); }
public Optional<User> findById(Long id) { return userRepository.findById(id); }
public User save(User user) { return userRepository.save(user); }
public void deleteById(Long id) { userRepository.deleteById(id); }}
复制代码

编写控制器

@RestController@RequestMapping("/users")public class UserController {    @Autowired    private UserService userService;
@GetMapping("all") public List<User> getAllUsers() { return userService.findAll(); }
@GetMapping("/{id}") public ResponseEntity<User> getUserById(@PathVariable Long id) { Optional<User> user = userService.findById(id); if (user.isPresent()) { return new ResponseEntity<>(user.get(), HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } }
@PostMapping public User addUser(@RequestBody User user) { return userService.save(user); }
@DeleteMapping("/{id}") public ResponseEntity<Void> deleteUser(@PathVariable Long id) { userService.deleteById(id); return ResponseEntity.noContent().build(); }}
复制代码

结果

配置文件详解

spring:  datasource:    url: jdbc:mysql://localhost:3306/mydb    username: user    password: pass    driver-class-name: com.mysql.jdbc.Driver
jpa: database-platform: org.hibernate.dialect.MySQL8Dialect show-sql: true # 是否打印生成的 SQL 语句 hibernate: ddl-auto: update # 应用启动时,自动更新数据库的表结构 properties: hibernate: format_sql: true # 是否格式化打印 SQL 语句
复制代码

hibernate.ddl-auto

hibernate.ddl-auto 是一个 Hibernate 的配置属性,用于控制在应用启动时如何自动创建数据库模式(schema)。

其可选值包括:

  • validate:验证 Hibernate 的所有实体类是否与数据库中的表结构匹配,如果不匹配则抛出异常,不做任何修改。

  • update:根据 Hibernate 的实体类自动更新数据库模式(schema),不会删除数据,只会修改表结构和数据类型。

  • create:根据 Hibernate 的实体类自动创建数据库模式(schema),会删除旧表并重新创建新表,但不会删除表中的数据。

  • create-drop:根据 Hibernate 的实体类自动创建数据库模式(schema),在应用程序关闭时删除所有表和数据。

  • none:不进行自动数据库模式(schema)管理,需要手动管理数据库模式(schema)。

需要注意的是,hibernate.ddl-auto 属性在生产环境下不应该使用,因为它可能会导致数据丢失和不可逆的表结构修改。在开发和测试环境下,它可以加快开发迭代速度和方便数据库模式(schema)的重建。

使用 hibernate.ddl-auto 属性中的 create 或者 create-drop 值可以让 Hibernate 根据实体类自动创建表。

jpa.database-platform

jpa.database-platform 是 Spring Data JPA 中一个可选的配置项,用于指定要使用的数据库平台,以便在应用程序启动时创建或更新数据库时生成正确的 SQL 语句。该配置项需要指定一个数据库方言(database dialect)的类名或者 Hibernate 方言名称。

在默认情况下,Spring Data JPA 会根据数据源的元数据信息自动检测使用的数据库类型和版本,并根据此信息自动选择合适的方言。但是,有些情况下自动检测可能无法正确识别数据库类型或版本,或者应用程序需要使用某些数据库特定的特性,这时候就需要手动指定方言。

jpa.database-platform 的取值通常为一下三种类型:

  • Hibernate 方言名称(如 org.hibernate.dialect.MySQL5Dialect)

  • 数据库方言类名(如 com.mysql.cj.jdbc.Driver)

  • Hibernate 的方言实现类(如 org.hibernate.dialect.MySQL8Dialect)

如果不指定此配置项,Spring Data JPA 会尝试根据数据源的元数据自动检测数据库类型和版本,并选择合适的方言。如果自动检测失败,可能会导致应用程序启动失败或生成的 SQL 语句无法正常执行。

需要注意的是,如果应用程序使用的是多个数据源,那么需要为每个数据源单独指定方言。可以通过在 application.yaml 中为每个数据源添加不同的 jpa.database-platform 配置项来实现。

spring data jpa 与其他 orm 框架的对比

Spring Data JPA 是一个基于 Spring Framework 的数据访问框架,封装了 JPA 规范,简化了 JPA 的使用,提供了一些常用的数据访问操作。

下面是 Spring Data JPA 与其他 ORM 框架的对比:

  1. Hibernate

Hibernate 是一个流行的 ORM 框架,与 JPA 相比,它提供了更多的特性和灵活性,可以更好地处理复杂的映射关系和查询。但是,Hibernate 使用起来相对复杂,需要处理更多的配置和细节。相比之下,Spring Data JPA 更加简洁易用,适合快速开发和小型项目。

  1. MyBatis

MyBatis 是一个轻量级的 ORM 框架,相比之下,它更加灵活,可以根据具体需求自定义 SQL 语句和映射关系。但是,使用 MyBatis 需要自己编写 SQL 语句,相对繁琐,而且需要自己处理一些细节。Spring Data JPA 则提供了更加简便的操作,可以根据方法名自动生成 SQL,使用起来更加方便。

  1. JOOQ

JOOQ 是一个基于 Java 的 SQL 构建器和 ORM 框架,它提供了类型安全的 SQL 构建和查询,可以生成可维护的 Java 代码。相比之下,Spring Data JPA 更加简单易用,可以省去编写 SQL 的麻烦,但是相对缺少类型安全性和可维护性。

总之,Spring Data JPA 适合快速开发和小型项目,能够极大地简化数据访问操作,减少代码量,但是相对缺少灵活性和复杂映射关系的处理能力。而 Hibernate 和 MyBatis 则更加灵活,可以处理更复杂的场景,但需要更多的配置和细节处理。


用户头像

Java你猿哥

关注

一只在编程路上渐行渐远的程序猿 2023-03-09 加入

关注我,了解更多Java、架构、Spring等知识

评论

发布
暂无评论
Spring Data JPA:轻松实现数据持久化_Java_Java你猿哥_InfoQ写作社区