前言
Spring Data JPA 是基于 Hibernate 的。
Hibernate 是一个广泛使用的 Java ORM(对象关系映射)框架,它提供了对关系型数据库的映射和操作功能,使开发者能够以面向对象的方式来处理数据库操作,而不用直接编写 SQL 语句。
与 MyBatis 比较
Spring Data JPA:优点是代码简单、易于维护,集成 Spring 框架更方便;
缺点是灵活性不如 MyBatis,性能也可能不如 MyBatis。
MyBatis:优点是灵活性强,可以执行复杂的 SQL 语句;
缺点是需要手动编写 SQL 语句,难以维护。
环境
1 |
|
添加依赖
在项目的 pom.xml 文件中添加如下 Spring Data JPA 相关依赖:
1 | <dependency> |
连接配置
application.properties配置文件中增加数据库参数,信息内容如下:
1 | spring.datasource.url=jdbc:mysql://127.0.0.1:3306/zbi_source?useUnicode=true&characterEncoding=utf8 |
实体类
在项目中创建实体类,用于映射数据库表和列。
表实体
实体类需要使用@Entity
注解进行标记,并且需要指定主键和自动生成策略。
例如:
1 | package cn.psvmc.zapijpa.entity; |
主键生成
1 |
|
自动建表
默认JPA是不会自动建表的,但是如果想自动建表,可以添加配置。
1 | spring.jpa.hibernate.ddl-auto=update |
设置 spring.jpa.hibernate.ddl-auto
属性为 create
或 update
。
create
表示每次启动应用时都会删除现有表并重新创建。
update
表示每次启动应用时会根据实体类的定义,更新已存在的表结构(增加或修改列),但不会删除数据。如果表不存在也会创建。
一般来说使用 update
,如果不想自动建表可以设置为none
。
关系映射
关系映射通常包括一对一、一对多和多对多等关系。
在 Spring Data JPA 中,可以使用 @OneToOne
、@OneToMany
和 @ManyToMany
注解来标注关系映射。
这些注解通常与 @JoinColumn
注解一起使用,用于指定关联的外键列。
示例代码:
1 |
|
在上例中,User
和 Address
之间是一对多的关系,所以在 User
实体类中使用了 @OneToMany
注解,在 Address
实体类中使用了 @ManyToOne
注解。mappedBy
属性用于指定关联的属性名称,这里是 user
,表示 Address
实体类中的 user
属性与 User
实体类中的 addresses
属性相对应。
cascade
属性表示级联操作,这里使用 CascadeType.ALL
表示在删除 User
实体时同时删除其关联的所有 Address
实体。
@JoinColumn
注解用于指定外键名称,这里是 user_id
,表示 Address
表中的 user_id
列与 User
表中的主键相对应。
Repository
UserRepository.java
1 | package cn.psvmc.zapijpa.repository; |
内置方法
在继承 Repository
接口后,会默认提供基本的增删改查方法,无需额外的代码实现即可使用。
常用的方法如下:
方法名 | 描述 |
---|---|
T save(T entity) | 保存实体对象 |
Iterable saveAll(Iterable entities) | 批量保存实体对象 |
Optional findById(ID id) | 根据主键获取实体对象 |
boolean existsById(ID id) | 判断是否存在特定主键的实体对象 |
Iterable findAll() | 获取所有实体对象 |
Iterable findAllById(Iterable ids) | 根据主键批量获取实体对象 |
long count() | 获取实体对象的数量 |
void deleteById(ID id) | 根据主键删除实体对象 |
void delete(T entity) | 删除实体对象 |
void deleteAll(Iterable<? extends T> entities) | 批量删除实体对象 |
方法名称查询
方法名称查询是 Spring Data JPA 中最简单的一种自定义查询方法,并且不需要额外的注解或 XML 配置。
它通过方法名来推断出查询的条件,
例如以 findBy
开头的方法表示按照某些条件查询,以 deleteBy
开头的方法表示按照某些条件删除数据。
示例代码:
1 | public interface UserRepository extends Repository<UserEntity, Long> { |
规则
关键字 | 方法命名 | sql where字句 |
---|---|---|
And | findByNameAndPwd | where name= ? and pwd =? |
Or | findByNameOrSex | where name= ? or sex=? |
Is,Equals | findById,findByIdEquals | where id= ? |
Between | findByIdBetween | where id between ? and ? |
LessThan | findByIdLessThan | where id < ? |
LessThanEquals | findByIdLessThanEquals | where id <= ? |
GreaterThan | findByIdGreaterThan | where id > ? |
GreaterThanEquals | findByIdGreaterThanEquals | where id > = ? |
After | findByIdAfter | where id > ? |
Before | findByIdBefore | where id < ? |
IsNull | findByNameIsNull | where name is null |
isNotNull,NotNull | findByNameNotNull | 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 | findByIdOrderByXDesc | where id=? order by x desc |
Not | findByNameNot | where name <> ? |
In | findByIdIn(Collection<?> c) | where id in (?) |
NotIn | findByIdNotIn(Collection<?> c) | where id not in (?) |
TRUE | findByStateTue | where state= true |
FALSE | findByStateFalse | where state= false |
IgnoreCase | findByNameIgnoreCase | where UPPER(name)=UPPER(?) |
查询参数设置
除了方法名称查询外,还可以使用参数设置方式进行自定义查询。
它通过在方法上使用 @Query
注解来指定查询语句,然后使用 @Param
注解来指定方法参数与查询语句中的参数对应关系。
示例代码:
1 | public interface UserRepository extends Repository<UserEntity, Long> { |
使用 Native SQL 查询
在某些情况下,需要执行原生的 SQL 查询语句。
Spring Data JPA 提供了 @Query
注解来支持使用原生 SQL 查询数据。
在 @Query
注解中设置 nativeQuery=true
即可执行原生 SQL 语句。
以下示例代码演示了如何使用原生 SQL 查询 age
大于等于 18
的用户。
1 | public interface UserRepository extends JpaRepository<UserEntity, Long> { |
使用
1 | userRepository.findByAgeGreaterThanEqual(18); |
这两种是一样的
1 | import cn.psvmc.zapijpa.entity.UserEntity; |
让人难受的是下面的写法是对的,但是IDEA工具会提示错误。
排序和分页
在查询数据时,经常需要对结果进行排序和分页操作。
Spring Data JPA 提供了 Sort
和 Pageable
两个类来实现排序和分页功能。
Sort
类表示排序规则,可以使用 Sort.by()
静态方法创建实例,并指定排序属性和排序方向。
常用方法如下:
方法名 | 描述 |
---|---|
static Sort by(Sort.Order… orders) | 根据排序规则创建 Sort 实例 |
static Sort.Order by(String property) | 根据属性升序排序 |
static Sort.Order by(String property, Sort.Direction direction) | 根据属性排序 |
示例代码:
1 | public interface UserRepository extends Repository<UserEntity, Long> { |
Pageable
类表示分页信息,可以使用 PageRequest.of()
静态方法创建实例,并指定页码、每页数据量和排序规则。
常用方法如下:
方法名 | 描述 |
---|---|
static PageRequest of(int page, int size, Sort sort) | 创建分页信息实例 |
static PageRequest of(int page, int size, Sort.Direction direction, String… properties) | 创建分页信息实例 |
示例代码:
1 | public interface UserRepository extends Repository<UserEntity, Long> { |
使用
1 | Pageable pageable = PageRequest.of(0, 10, Sort.by("age").descending()); |
更新和删除
在 Spring Data JPA 中,使用 update
和 delete
语句需要使用 @Modifying
注解标注,并且需要添加 @Transactional
注解开启事务。
需要注意的是,@Modifying
注解只支持 DML 语句。
示例代码:
1 | public interface UserRepository extends Repository<UserEntity, Long> { |
Service
UserService.java
1 | package cn.psvmc.zapijpa.service; |
Controller
1 | package cn.psvmc.zapijpa.controller; |
访问
http://localhost:8080/user/list
多数据源
在实际应用中,有时需要使用多个数据源。
Spring Boot 提供了 @ConfigurationProperties
、@Primary
、@Qualifier
等注解来支持多数据源配置。
以下示例代码演示了如何在 Spring Boot 应用程序中配置多数据源。
在 application.properties
文件中配置两个数据源的连接信息
1 | # 数据源一 |
创建两个数据源的配置类
DataSourceOneConfig
1 |
|
DataSourceTwoConfig
1 |
|
在 Service 或 Repository 中指定要使用的数据源
1 |
|
在上述示例代码中,使用 @ConfigurationProperties
注解将数据源的连接信息和配置类绑定。
使用 @Qualifier
和 @Autowired
注解指定要使用的数据源。
在 Service 或 Repository 中通过 DataSource.getConnection()
获取连接,手动执行 SQL 语句。
开启批量操作
Mysql的话,开启批量操作需要在jdbc的url后面加上参数rewriteBatchedStatements=true
,Oracle无需此操作。
默认批量操作是关闭的,要想开启设置如下参数
1 | spring.jpa.properties.hibernate.jdbc.batch_size=2 |
当batch_size
设置值等于1的时候也是不生效的,必须大于1。
这里是为了测试才设置为2。实际使用可以大一点
1 | spring.jpa.properties.hibernate.jdbc.batch_size=100 |
为了方便验证我们可以添加打印配置
1 | spring.jpa.properties.hibernate.generate_statistics=true |
这样当没有批量处理的时候会看到
spent executing 0 JDBC batches;
有批量的时候值是大于0的。
1 | import cn.psvmc.zapijpa.entity.UserEntity; |
注意
批处理必须开启事务。
Controller中
1 | import cn.psvmc.zapijpa.entity.UserEntity; |
当我们调用以下地址的时候会插入4条数据
http://localhost:8080/user/add_users
开启批量设置为2的时候,没两条就进行一次批处理,就会看到显示2次批处理了。
spent executing 2 JDBC batches;
有人说是通过打印SQL查看
1 | spring.jpa.properties.hibernate.show_sql=true |
但是实测发现就算是批处理,SQL也是一次一条,只不过是多条后批量提交。
打印执行的SQL
1 | spring.jpa.properties.hibernate.show_sql=true |