什么是 JPA(Java Persistence API)?
JPA是Java平台上的一种持久化规范,用于将Java对象映射到数据库中的数据。它提供了一种标准的方法来管理应用程序的数据持久化和对象-关系映射(ORM)。JPA的目标是简化数据库操作,提供一种面向对象的编程方式来处理数据存储和检索。
1、JPA的设计目标
JPA由Java社区领导开发,具有以下主要设计目标:
- 对象关系映射(ORM):JPA允许开发人员使用Java类和对象来表示数据库中的表和行,从而实现对象关系映射,无需手动编写SQL语句。
- 标准化API:JPA提供了一组标准的API,使您可以在不同的JPA实现之间切换而不需要改变代码。流行的JPA实现包括Hibernate、EclipseLink等。
- 自动化查询生成:JPA可以自动生成SQL查询,简化开发过程并提高代码的可维护性。
- 事务管理:JPA支持事务管理,允许您在持久化操作中维护数据的一致性。
- 缓存管理:JPA提供了一些缓存机制,可以提高应用程序的性能。
- 多数据库支持:JPA允许开发人员在不同类型的数据库之间切换,只需要更改配置而不需要更改代码。
总的来说,JPA提供了一种面向对象的数据库操作方式,允许您以类似于面向对象编程的方式来对数据库进行操作,从而大大简化了数据库操作的流程,提高了开发效率。
2、为什么选择 Spring Data JPA?
Spring Data JPA是Spring Data家族的一部分,专为基于JPA的数据存储库设计。它是一种强大且简化的工具,具有以下主要优势:
简化的数据访问层开发
- 使用Spring Data JPA,您只需通过声明性的方式定义数据存储库接口,就可以大大简化数据访问层的开发。Spring Data JPA会自动生成数据访问方法的实现,让您无需手动编写繁琐的代码。
提供自动生成查询
- Spring Data JPA可以根据方法名的约定自动生成查询语句,极大地减少了手动编写SQL查询的工作量。这不仅提高了开发效率,还降低了错误的风险。
支持多种数据源
- 不论您使用何种类型的数据存储,Spring Data JPA都提供了一致的API,轻松实现切换数据源,无需修改代码。
集成Spring生态系统
- Spring Data JPA无缝集成了Spring Framework,充分利用Spring的依赖注入、事务管理等特性,使您的开发更加高效和便捷。
减少ORM框架切换成本
- 如果未来需要切换底层的ORM框架,例如从Hibernate切换到其他实现,Spring Data JPA的抽象层将大大降低这种切换的成本,使您的项目更具灵活性。
3、JPA与Spring Data JPA
要理解Spring Data JPA,我们首先需要了解JPA(Java Persistence API)。JPA是一种Java EE规范,用于通过对象-关系映射(ORM)实现Java对象与数据库之间的映射。不同的ORM框架(如Hibernate、TopLink等)可以作为JPA规范的具体实现。
Spring Data JPA在JPA之上提供了另一层抽象,专注于简化数据存储库的开发。通过定义Repository接口中的方法签名,Spring Data JPA自动生成底层JPA查询的实现,实现基本的CRUD操作和自定义查询。
总之,Spring Data JPA是学习和使用JPA的最佳选择之一。它简化了数据访问层的开发,提供自动生成查询语句,支持多种数据源,并与Spring Framework集成,帮助您构建高效的数据访问层。
一、搭建环境
首先,使用Maven管理包,使用Spring Boot框架,建一个空Maven项目即可。可以通过以下方式引入 Spring Boot 依赖:
<!-- 在你的 pom.xml 文件中 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
注意: 这个jpa启动的时候会自动建表, 先检查有没有表名称, 如果有, 则不创建, 否则建表; 如果实体类结构发生修改,也会自动修改对应的表结构。
jpa分页查询, 默认从第0页算第一页
二、配置文件设置
在 application.properties
或 application.yml
文件中,配置你的数据库连接信息,例如:
# application.yml
spring:
datasource:
# url配置有一个自动建库的配置, 很重要的前提 mysql安装的时候已经配置了utf8mb4默认字符集
url: jdbc:mysql://服务器IP地址:3306/jpa23?createDatabaseIfNotExist=true&useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: 密码
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update #自动更新
show-sql: true #日志中显示sql语句
url配置有一个自动建库的配置: createDatabaseIfNotExist=true
三、使用注解
使用 Spring Data JPA,你可以通过注解来定义实体类和数据表的映射关系。以下是一个使用JPA注解的示例,用于定义实体类和数据表的映射关系以及一些元数据信息。
@Data
@Entity
@Table(name = "JPA_USER")
@EntityListeners(AuditingEntityListener.class)
public class JpaUser {
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "NAME")
private String name;
@Column(name = "OBJECT_VERSION")
@Version
private Long objectVersion;
@Column(name = "CREATED_BY")
@CreatedBy
private String createdBy;
@Column(name = "CREATED_DATE")
@CreatedDate
private Date createdDate;
@Column(name = "LAST_UPDATED_BY")
@LastModifiedBy
private String lastUpdatedBy;
@Column(name = "LAST_UPDATED_DATE")
@LastModifiedDate
private Date lastUpdatedDate;
}
@Data
: 这是一个Lombok注解,自动生成equals
、hashCode
、toString
等方法。它可以减少一些样板代码。@Entity
: 用于将Java类标记为JPA实体,表示该类将映射到数据库表。@Table(name = "JPA_USER")
: 定义了与实体关联的数据库表JPA_USER的名称。@EntityListeners(AuditingEntityListener.class)
: 指定实体监听器,用于处理审计信息等功能。在这个例子中,使用了Spring Data JPA的审计功能。@Id
: 表示这个字段是主键。@Column(name = "ID")
: 定义了与数据库列的映射关系,指定了数据库列的名称。@GeneratedValue(strategy = GenerationType.IDENTITY)
: 用于指定主键的生成策略,这里是数据库自增长。
这些注解用于在实体类中定义与数据库表之间的映射关系以及其他元数据信息,使得使用JPA时可以方便地操作数据库,同时还可以利用Spring Data JPA的一些功能,如审计、版本控制等。
注意:
有了@EntityListeners(AuditingEntityListener.class)
这个注解,@CreatedBy
、@CreatedDate
、@LastModifiedBy
、@LastModifiedDate
才生效哦,而且创建人和更新人需要另作注入操作。
1、Mysql自增长主键策略
1.1 GenerationType.IDENTITY 主键生成策略
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
GenerationType.IDENTITY
主键生成策略适用于支持自增列的数据库,例如 MySQL 中的 auto_increment
。使用这个策略,你只需要将 @GeneratedValue
注解与 strategy = GenerationType.IDENTITY
一起使用,JPA 会自动利用数据库的自增特性生成主键值。这是最简单和直接的方式,不需要其他配置。
1.2 GenerationType.TABLE 主键生成策略
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.TABLE, generator = "sequence_table")
@TableGenerator(name = "sequence_table",
allocationSize = 1,
table = "sequence_table",
pkColumnName = "sequence_name",
valueColumnName = "sequence_count")
private Long id;
GenerationType.TABLE
主键生成策略可以用于任何数据库,它通过在数据库中维护一个特定的表来生成主键值。这个策略更加灵活,可以在不同数据库之间移植。以下是相关的解释:
@GeneratedValue(strategy = GenerationType.TABLE, generator = "sequence_table")
: 这里使用strategy = GenerationType.TABLE
来指定主键生成策略为表生成,并通过generator
参数指定生成器的名称。@TableGenerator(name = "sequence_table",...)
: 这个注解用于配置表生成器的详细信息。在这个例子中,我们使用了名为sequence_table
的生成器。
allocationSize = 1
: 这个参数表示每次从数据库表中获取主键值的数量,一般设置为1。table = "sequence_table"
: 这个参数指定了用于维护主键值的数据库表的名称。pkColumnName = "sequence_name"
: 这个参数指定了在表中记录主键名称的列名。valueColumnName = "sequence_count"
: 这个参数指定了在表中记录当前主键值的列名。
总之,GenerationType.TABLE
策略适用于跨数据库平台的场景,通过维护一个专门的序列表,可以生成唯一的主键值。
2、使用审计功能来自动填充实体的创建时间、创建人、最后修改时间和最后修改人等信息
这段文档主要讲解了在 Spring Data JPA 中如何使用审计功能来自动填充实体的创建时间、创建人、最后修改时间和最后修改人等信息,并且详细描述了如何通过 session 存储用户信息,将其注入到审计功能中。我会对这些内容进行补充整理和解释。
2.1 修饰五个系统字段的注解
这部分描述了 JPA 中用于进行审计的注解,它们用于自动填充实体的一些系统字段,包括版本号、创建时间、创建人、最后修改时间和最后修改人等。
-
@Version
:版本号,启用乐观锁,在更新操作时使用,要求字段值与数据库中一致才能进行修改。 -
@CreatedDate
:创建时间,在插入操作时自动填充当前时间。 -
@CreatedBy
:创建人,在插入操作时自动填充当前用户名。 -
@LastModifiedDate
:最后一次修改时间,在更新操作时自动填充当前时间。 -
@LastModifiedBy
:最后一次修改的修改人,在更新操作时自动填充当前用户名。
2.2 启动审计
这部分描述了启用审计功能的步骤:
- 在 Spring Boot 启动类上加上
@EnableJpaAuditing
注解,以启用 JPA 审计功能。 - 在实体类上使用
@EntityListeners(AuditingEntityListener.class)
注解,指定要监听审计信息的实体类。
2.3 session 保存用户信息并进行审计注入
使用 session 来存储用户登录信息,并将其注入到审计功能中,以便在创建人和最后修改人字段中填充用户信息。
具体思路
实现从 session 中获取用户信息并注入到审计功能:
- 声明一个
HttpServletRequest
对象request
,声明一个字符串username
,默认值为 “anonymous”。 - 通过
RequestContextHolder
拿到 request 的属性,转换成可以得到 request 对象的ServletRequestAttributes
。 - 判断当前线程的请求属性是否为空,如果为空,则直接返回
username
的默认值;如果不为空,继续下一步。 - 不为空,就可以得到 request 对象,它里面存储了 session 信息。
- 使用
Object
类接收预定好的 key 值(在这里是jpaUser
),如果 session 中存储了用户信息,那么拿到的对象类型一定是JpaUser
,进行强制类型转换后,可以拿到存储在其中的name
值。 - 返回带有真实用户名的
username
,接下来会将其交给 JPA 审计来进行注入。
总之,这一节讲述了如何在审计功能中实现从 session 中获取用户信息并将其注入到实体的创建人和最后修改人字段中,以实现自动填充用户信息。
四、增删改查CURD
//接口
public interface JpaUserRepository extends JpaRepository<JpaUser, Long> {
}
JpaUserRepository
接口的作用:
这个接口定义了一个用于操作JpaUser
对象的数据库访问层(Data Access Layer)的接口。它提供了一组方法,用于对JpaUser
对象进行基本的数据库操作,如查询、保存、删除等。
每个参数的含义:
-
JpaUser
: 这是该接口操作的实体类。在这个例子中,JpaUser
是一个Java类,它映射到数据库中的一个表。 -
Long
: 这是该接口操作的实体的主键类型。在这个例子中,JpaUser
表的主键类型是Long
。
在实际操作中,CURD操作是核心,包括插入、查询、更新和删除操作。
1、 插入(新增)操作
插入操作将一个新的实体对象持久化到数据库中。
// 创建一个新的实体对象
JpaUser jpaUser = new JpaUser();
jpaUser.setId(8L);
jpaUser.setName("雪花酥");
jpaUser.setObjectVersion(1L);
// 保存实体对象到数据库,无则添加,有则更新
userRepository.save(jpaUser);
知识点:
-
save()
方法用于将实体对象保存到数据库。如果实体对象的主键已存在,则进行更新操作;如果主键不存在,则进行新增操作。[无则添加,有则更新] - 主键生成策略(如自增长、UUID等)会影响实体对象的插入方式。
2、 查询操作
查询操作允许你从数据库中检索实体对象。
// 查询所有用户
List<JpaUser> userList = userRepository.findAll();
System.out.println("userList = " + userList);
知识点:
-
findAll()
方法用于检索指定实体类的所有记录。
// 根据主键查询用户
//自动拼接查询语句:where jpauser0_.name=?
JpaUser jpa = userRepository.findOneByName("JPA");
if (jpa.isPresent()) {
// 找到用户
System.out.println("jpa = " + jpa);
} else {
// 用户不存在
}
知识点:
-
findById()
方法用于根据主键查找记录。返回值是一个Optional
对象,可以避免空指针异常。
3、 更新操作
更新操作用于修改数据库中的现有实体对象,确保没有数据丢失。
// 查询要更新的用户
JpaUser user = userRepository.findById(userId);
if (user.isPresent()) {
JpaUser existingUser = user.get();
existingUser.setUsername("updatedUsername");
userRepository.save(existingUser); // 执行更新
}
知识点:
- 查询并获取要更新的实体对象。
- 修改实体对象的属性。
- 使用
save()
方法将修改后的实体对象保存到数据库,执行更新操作。
4、 删除操作
删除操作用于从数据库中删除特定的实体对象。
// 根据主键删除用户
userRepository.deleteById(1L);
知识点:
-
deleteById()
方法用于根据主键删除记录。
// 删除实体对象
User userToDelete = new User();
userToDelete.setId(userId);
userRepository.delete(userToDelete);
知识点:
-
delete()
方法可以通过传入实体对象进行删除操作。
以上示例代码展示了基本的增删改查操作使用方法,使用Spring Data JPA可以轻松地处理这些数据库操作。要注意的是,Spring Data JPA提供了更多高级查询和操作的方法,可以根据需要深入学习和应用。
五、方法命名规则
Spring Data JPA 可以根据实体类自动生成常见的 SQL 查询。可以使用方法名约定来定义查询方法。例如,以下会自动生成一个根据用户名查询用户的 SQL:
//接口
public interface JpaUserRepository extends JpaRepository<JpaUser, Long> {
}
这个接口是一个继承自JpaRepository
的自定义接口,专门用于处理JpaUser
对象的数据库操作。
通过继承JpaRepository
,JpaUserRepository
自动继承了JpaRepository
提供的一些基本方法,如findOne()
, findAll()
, save()
, delete()
, 等等。
JPA方法命名规则规定了方法名与数据库查询的对应关系。自定义SQL查询可以使用@Query注解,支持原生SQL和命名参数。
jpa方法命名规则
上面随机列举了几个经常使用用的查询方式,实际上还是有很多的,另外除了find开头的方式,还可以用get开头。看下面面的列表:
关键字 | 方法命名 | sql where字句 |
And | findByNameAndPwd | where name = ? and pwd= ? |
Or | findByNameOrSex | where name = ? or sex =? |
Between | findByIdBetween | where id between ? and ? |
LessThan | findByIdLessThan | where id <? |
LessThanEqual | findByIdLessThanEqual | where id <=? |
GreaterThan | findByIdGreaterThan | where id > ? |
GreaterThanEqual | findByIdGreaterThanEqual | where id >= ? |
After | findByIdAfter | where id > ? |
Before | find ByIdBefore | where id < ? |
IsNull | findByNameIsNull | where name is null |
IsNotNull,NotNull | findByNameIsNotNullfindByNameNotNull | where name is not null |
Like | findByNameLike | where name like ? |
NotLike | findByNameNotLike | where name not like ? |
StartingWith | findByNameStartingWith | where name like ‘?%’ |
EndingWith | findByNameEndingWith | where name like ‘%?’ |
Containing | findByNameContaining | where name like ‘%?%’ |
OrderBy | findByIdOrderByAgeDescAndIdAsc | where id = ? order by age desc,id asc |
Not | findByNameNot | where name <> ? |
In | findByNameIn | where name in (?) |
NotIn | findByIdNotIn | where id not in (?) |
True | findByDelStatusTrue | where delStatus = true |
False | findByDelStatusFalse | where delStatus = false |
IgnoreCase | findByNameIgnoreCase | where UPPER(name) = UPPER(?) |
六、 自定义 SQL查询
除了使用 Spring Data JPA 自动生成的查询方法外,你还可以通过 @Query
注解来自定义 SQL 查询。这使得你可以根据特定需求编写自己的查询语句。
1、@Query注解
@Query注解使用起来很简单,默认的属性是value,就是当前写的SQL语句,有时会用到nativeQuery属性,这个属性是用来标记当前的SQL是本地SQL,还是符合JPA语法规范的SQL。
- 本地SQL,是根据实际使用的数据库类型写的SQL,这种SQL中使用到的一些语法格式不能被JPA解析以及可能不兼容其他数据库,这种SQL称为本地SQL,此时需要将nativeQuery属性设置为true,否则会报错。
- JPA语法规范的SQL,往往这种SQL本身是不适用于任何数据库的,需要JPA将这种SQL转换成真正当前数据库所需要的SQL语法格式。
下面是一些自定义 SQL 查询的示例:
public interface IJpaUserRepository extends JpaRepository<JpaUser, Long> {
List<JpaUser> findByOrderByIdDesc();
List<JpaUser> findByNameLike(String name);//模糊查询。查询条件中需要自己加 %
List<JpaUser> findByNameContaining(String name);//包含
// 查询所有用户
@Query("SELECT u FROM JpaUser u")
List<JpaUser> findAllUsers();
// 按名字查询用户
@Query("SELECT u FROM JpaUser u WHERE u.name = :name")
List<JpaUser> findUsersByName(@Param("name") String name);
// 使用原生 SQL 查询所有用户
@Query(value = "SELECT * FROM jpa_user", nativeQuery = true)
List<JpaUser> findAllUsersNative();
// 自定义更新操作
@Modifying
@Query("UPDATE JpaUser u SET u.name = :newName WHERE u.id = :id")
int updateUserName(@Param("id") Long id, @Param("newName") String newName);
JpaUser findOneByName(String name);
@Query(value = "from JpaUser")
List<JpaUser> queryAll();
//@Query注解使用起来很简单,默认的属性是value,就是当前写的SQL语句。
// 有时会用到nativeQuery属性,这个属性是用来标记当前的SQL是本地SQL,还是符合JPA语法规范的SQL。nativeQuery = true表示为原生sql写法
@Query(value = "select * from jpa_user", nativeQuery = true)
List<JpaUser> queryAll2();//原生sql写法
}
上述示例中,我们使用 @Query
注解来定义了几个不同类型的自定义查询:
findAllUsers()
: 查询所有用户的自定义查询,使用JPQL(Java Persistence Query Language)。findUsersByName()
: 根据名字查询用户的自定义查询,使用JPQL,并使用命名参数。findAllUsersNative()
: 使用原生 SQL 查询所有用户。updateUserName()
: 自定义更新操作,使用@Modifying
注解标注,并通过JPQL更新用户名。
知识点:
-
@Query
注解可以用于在接口中定义自定义查询。 - 通过设置
nativeQuery = true
,可以使用原生 SQL 进行查询。 -
@Modifying
注解用于标识更新操作。 -
@Param
注解用于绑定命名参数。 - 使用JPQL时,要使用实体类名和属性名,而不是数据库表名和列名。
2、带条件的查询
2.1 使用 @Param
语法
在这种查询中,我们使用 :参数名
的语法来传递参数,同时在方法参数上使用 @Param
注解来绑定参数。
@Query("select u from JpaUser u where name=:nm and objectVersion=:ver")
List<JpaUser> queryAllByNameAndVersion(@Param("nm") String name, @Param("ver") Long version);
2.2 使用参数位置
这里我们使用 ?
来表示参数位置,例如 ?1
表示第一个参数,?2
表示第二个参数。
@Query("select u from JpaUser u where name=?1 and objectVersion=?2")
List<JpaUser> queryAllByNameAndVersion2(String name, Long version);
2.3 原生 SQL 查询
同样的参数位置语法也适用于原生 SQL 查询。
@Query(value = "select * from jpa_user where name=?1 and object_version=?2", nativeQuery = true)
List<JpaUser> queryAllByNameAndVersion3(String name, Long version);
2.4 使用属性名称加表前缀
在 JPA SQL 语法中,你可以使用属性名称加上表名称前缀进行查询。
@Query("select u from JpaUser u where u.name=?1 and u.objectVersion = ?2")
List<JpaUser> queryAllByNameAndVersion4(String name, Long version);
3、传对象
使用对象作为参数
这个例子展示了如何传递一个对象作为参数进行查询。
@Query("select u from JpaUser u where u.name=:#{#object.name} and u.objectVersion=:#{#object.objectVersion}")
List<JpaUser> queryAllByObj(@Param("object") JpaUser jpaUser);
4、插入与更新
当使用 Spring Data JPA 进行数据的插入和更新操作时,我们需要确保以下两个方面:
- 事务管理(Transactional): 插入和更新操作都是对数据库进行修改的操作,为了保证数据的完整性和一致性,我们需要将这些操作放在一个事务中进行。事务是一组数据库操作,要么全部成功,要么全部失败,保证了数据的一致性。在Spring中,
@Transactional
注解用于声明一个方法应该在一个事务中运行。 - 修改操作(Modifying): 在 JPA 中,插入和更新操作都被认为是修改操作,需要使用
@Modifying
注解来标识。这个注解告诉 Spring Data JPA 这个方法是一个修改操作,需要特别处理。
4.1 自定义插入语句(原生 SQL)
在这个示例中,我们使用 @Modifying
和 @Transactional
注解来标识插入操作,同时使用原生 SQL 语法。
@Modifying
@Transactional
@Query(value = "insert into jpa_user(id, name, object_version) values (:#{#object.id}, :#{#object.name}, :#{#object.objectVersion})", nativeQuery = true)
int add(@Param("object") JpaUser jpaUser);
在这个示例中,@Modifying
注解告诉 Spring Data JPA 这个方法是一个修改操作,@Transactional
注解指定了这个方法应该在一个事务中运行。然后,我们使用 @Query
注解来指定自定义的原生 SQL 插入语句。
4.2 自定义更新语句
同样地,你可以使用 @Modifying
和 @Transactional
注解标识更新操作。
@Modifying
@Transactional
@Query("update JpaUser u set u.name=:#{#object.name} where u.id = :#{#object.id}")
int updateById(@Param("object") JpaUser jpaUser);
在这些代码片段中,不同的自定义查询方式展示了如何使用 @Query
注解来执行定制化的查询。插入和更新操作也可以通过 @Query
注解来实现,使用 @Modifying
和 @Transactional
注解来标识插入和更新操作是为了保证数据库操作的一致性和正确性,同时还需要注意在使用这些注解时要确保方法运行在合适的事务环境中。
这些自定义查询可以帮助你灵活地执行各种复杂的数据库操作,根据具体需求来优化和精细控制数据的检索和操作。
注意:JPA很好的一个特性就是用JPA语法规范写的SQL,会根据当前系统使用的数据库类型改变生成的SQL语法,兼容数据库类型的切换,如之前使用的是MySQL,现在换成Oracle,由于不同类型的数据库,SQL语法会有区别,如果使用的是mybatis,就需要手动去改SQL兼容Oracle,而JPA就不用啦,无缝对接。
说明:很大的时候使用JPA感觉都是为了兼容后期可能会有数据库切换的问题,所以在使用JPA的时候,不要去使用本地SQL,这就违背了使用JPA的初衷,让nativeQuery属性保持默认值就可以啦!
5、联表查询
在数据库中,关系数据库表之间可能会存在关联关系,有时候我们需要在查询中联合多个表的数据。
JPA允许我们进行联表查询,将多个表的数据组合在一起,从而得到更丰富的信息。联表查询可以结合@Query和@Join来实现。下面是一个使用 Spring Data JPA 完成的连表查询案例:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
@Query(value = "SELECT u.id, u.name, r.role_name FROM user u LEFT JOIN user_role ur ON u.id = ur.user_id LEFT JOIN role r ON ur.role_id = r.id WHERE u.id = :userId", nativeQuery = true)
List<Object[]> findUserAndRoleById(@Param("userId") Long userId);
}
这段代码演示了如何使用 Spring Data JPA 进行联表查询,查询用户及其角色信息。
@Repository
: 这个注解标志这个接口是一个仓库,用于访问数据库。extends JpaRepository<User, Long>
: 这个接口继承了JpaRepository
,这是 Spring Data JPA 提供的一个用于基本数据库操作的仓库接口,User
是实体类,Long
是主键的数据类型。@Query
: 这个注解用于自定义查询,可以使用 JPQL 或者原生 SQL。在这个例子中,使用了原生 SQL 查询。value = "SELECT u.id, u.name, r.role_name FROM user u LEFT JOIN user_role ur ON u.id = ur.user_id LEFT JOIN role r ON ur.role_id = r.id WHERE u.id = :userId"
: 这是实际的 SQL 查询语句。它使用了LEFT JOIN
连接三个表:user
、user_role
、role
,并在查询条件中根据用户ID查询相关信息。nativeQuery = true
: 这个参数指示查询使用原生 SQL。@Param("userId")
: 这个注解将方法参数userId
映射到 SQL 查询中的:userId
参数。List<Object[]>
: 这个方法的返回类型是一个由对象数组组成的列表。查询结果中的每行数据将被映射到一个对象数组中。
在这个案例中,我们使用了原生SQL语句来进行连表查询。通过LEFT JOIN将user表、user_role表和role表连接起来,然后使用WHERE条件过滤出指定用户的信息。最后,使用@Query注解将SQL语句与方法关联起来。
在返回值中,我们使用了Object[]类型来存储查询结果。数组中的第一个元素是用户的id,第二个元素是用户的姓名,第三个元素是用户的角色名。
需要注意的是,在使用原生SQL进行查询时,返回值的类型应该与查询结果的数据类型相匹配。否则,程序可能会抛出类型转换异常等错误。
总之,这个示例展示了如何使用 Spring Data JPA 进行连表查询,获取用户及其角色信息。通过自定义查询语句,我们可以灵活地进行多表关联查询,得到我们所需的数据。
七、总结
1.@Version
注解加上后,更新操作一定要带上注解修饰的字段,且要与数据库中的值一致。
2.@CreatedBy
和@CreatedDate
会在更新时一并更新,需要主动去维护,或者在@Column注解中加上updatable = false,比如这样@Column(name = "CREATED_DATE",updatable = false)
。
JPA为Java开发者提供了便捷的数据持久化解决方案,通过简单的注解和方法命名规则,实现了对数据库的操作。使用Spring Data JPA进一步简化了开发过程,提高了效率,同时具备灵活性和可维护性。综上所述,JPA为Java持久化提供了强大的工具和规范,帮助开发者更轻松地处理数据存储与检索。