SpringDataJPA 系列之快速入门
1.1 简介
1.1.1 概述
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套 JPA 应用框架,可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!Spring Data JPA 让我们解脱了 DAO 层的操作,基本上所有 CRUD 都可以依赖于它来实现,在实际的工作工程中,推荐使用 Spring Data JPA + ORM(如:hibernate) 完成操作,这样在切换不同的 ORM 框架时提供了极大的方便,同时也使数据库层操作更加简单,方便解耦
1.1.2 SpringDataJPA 与 JPA 和 hibernate 之间的关系
JPA 是一套规范,内部是有接口和抽象类组成的。hibernate 是一套成熟的 ORM 框架,而且 Hibernate 实现了 JPA 规范,所以也可以称 hibernate 为 JPA 的一种实现方式,我们使用 JPA 的 API 编程,意味着站在更高的角度上看待问题(面向接口编程)。SpringDataJPA是 Spring 提供的一套对 JPA 操作更加高级的封装,是在 JPA 规范下的专门用来进行数据持久化的解决方案。
1.1.3 整合 SpringDataJPA
☞ SpringBoot 整合 SpringDataJPA ☞ Spring 整合 SpringDataJPA
1.2 SpringDataJPA 的使用
1.2.1 使用接中口定义的方法
SpringDataJPA 致力于减少数据访问层的开发量,开发者唯一要做的就是声明持久层的接口,其他都交给SpringDataJPA来帮你完成。一般我们会继承 JpaRepository 和 JpaSpecificationExecutor 接口,我们可以使用接口中定义的方法进行查询。 ♞ CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法 ♞ PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法 ♞ JpaRepository: 继承 PagingAndSortingRepository,实现一组 JPA 规范相关的方法,我们一般继承这个。 ♞ JpaSpecificationExecutor: 不属于 Repository 体系,实现一组 JPA Criteria 查询相关的方法
☞ JpaRepository 中定义的方法
☞ JpaSpecificationExecutor 中定义的方法
☞ 接口方法速查
1.2.2 使用 JPQL
使用 SpringDataJPA 提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条件,这时就可以使用 @Query 注解,结合 JPQL 的语句方式完成查询。@Query 注解的使用非常简单,只需在方法上面标注该注解,同时提供一个 JPQL 查询语句即可。☞ JPQL 详细介绍
/**
* Created with IntelliJ IDEA.
*
* @author Demo_Null
* @date 2020/7/30
* @description student repository 接口
*/
public interface StudentRepository extends JpaRepository<Student, Long> {
@Query("from Student")
public List<Student> findStudent();
}
此外,也可以通过使用 @Query 来执行一个更新操作,为此,我们需要在使用 @Query 的同时,用 @Modifying 来将该操作标识为修改查询,这样框架最终会生成一个更新的操作,而非查询。可以通过自定义的 JPQL 完成 UPDATE 和 DELETE 操作,注意:JPQL 不支持使用 INSERT 操作。方法的返回值是 int,表示更新语句所影响的行数。默认情况下,SpringDataJPA 的每个方法上有事务, 但都是一个只读事务,他们不能完成修改操作,因此需要在调用的地方必须加事务(添加 @Transactional 注解),没有事务不能正常执行。
/**
* Created with IntelliJ IDEA.
*
* @author Demo_Null
* @date 2020/7/30
* @description student repository 接口
*/
public interface StudentRepository extends JpaRepository<Student, Long> {
//**************************************************************************************
// 第一种写法
//**************************************************************************************
@Modifying
// ? 后的数字表示第几个参数,顺序与参数一致可不写
@Query("update Student set sex = ?1, Name = ?2 where id = ?3")
public int updateStudent(Boolean sex, String Name, Long id);
//**************************************************************************************
// 第二种写法
//**************************************************************************************
@Modifying
@Query("update Student set sex = :sex, Name = :Name where id = :id")
public int updateStudent(@Param("sex") Boolean sex, @Param("Name") String Name, @Param("id") Long id);
}
1.2.3 使用 SQL 语句
/**
* Created with IntelliJ IDEA.
*
* @author Demo_Null
* @date 2020/7/30
* @description student repository 接口
*/
public interface StudentRepository extends JpaRepository<Student, Long> {
// nativeQuery true: 使用 sql 查询; false: 使用 jpql 查询,默认就是 false
// 占位符与参数位置对应可不写 ? 后的数字
@Query(value = "select * from student where id = ?1 or sex = ?2", nativeQuery=true)
public List<Student> updateStudent(Long id, Boolean sex);
}
1.2.4 方法命名规则查询
顾名思义,方法命名规则查询就是根据方法的名字,就能创建查询。只需要按照 SpringDataJPA 提供的方法命名规则定义方法的名称,就可以完成查询工作。SpringDataJPA 在程序执行的时候会根据方法名称进行解析,并自动生成查询语句进行查询。按照 SpringDataJPA 定义的规则,查询方法以 findBy 开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进行解析。
☞ 支持的查询关键字
关键字 |
示例 |
JPQL语句 |
---|---|---|
AND |
findByLastnameAndFirstname |
where x.lastname = ?1 and x.firstname = ?2 |
Or |
findByLastnameOrFirstname |
where x.lastname = ?1 or x.firstname = ?2 |
Between |
findByStartDateBetween |
where x.startDate between ?1 and ?2 |
LessThan |
findByAgeLessThan |
where x.age < ?1 |
GreaterThan |
findByAgeGreaterThan |
where x.age > ?1 |
After |
findByStartDateAfter |
where x.startDate > ?1 |
Before |
findByStartDateBefore |
where x.startDate < ?1 |
IsNull |
findByAgeIsNull |
where x.age is null |
IsNotNull,NotNull |
findByAge(Is)NotNull |
where x.age not null |
Like |
findByFirstnameLike |
where x.firstname like ?1 |
NotLike |
findByFirstnameNotLike |
where x.firstname not like ?1 |
StartingWith |
findByFirstnameStartingWith |
where x.firstname like ?1(parameter bound with appended %) |
EndingWith |
findByFirstnameEndingWith |
where x.firstname like ?1(parameter bound with prepended %) |
Containing |
findByFirstnameContaining |
where x.firstname like ?1(parameter bound wrapped in %) |
OrderBy |
findByAgeOrderByLastnameDesc |
where x.age = ?1 order by x.lastname desc |
Not |
findByLastnameNot |
where x.lastname <> ?1 |
In |
findByAgeIn(Collection ages) |
where x.age in ?1 |
NotIn |
findByAgeNotIn(Collection age) |
where x.age not in ?1 |
True |
findByActiveTrue() |
where x.active = true |
False |
findByActiveFalse() |
where x.active = false |
IgnoreCase |
findByFirstnameIgnoreCase |
where UPPER(x.firstame) = UPPER(?1) |
☞ 示例
/**
* Created with IntelliJ IDEA.
*
* @author Demo_Null
* @date 2020/7/30
* @description student repository 接口
*/
public interface StudentRepository extends JpaRepository<Student, Long> {
public List<Student> findByAge(int age);
}
☞ 查询方法流程解析
假如创建如下的查询:findByClassUserAge(),框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析,假设查询实体为 Student,先判断 ClassUserAge(根据 POJO 规范,首字母变为小写)是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性则从右往左截取第一个大写字母开头的字符串(此处为Age),然后检查剩下的字符串(classUser)是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则继续从右往左截取;假设 class 为查询实体的一个属性,则先判断 class 是否有 userAge 属性,有则按照 Student.class.userAge
查询,没有就会报错。
可能会存在一种特殊情况,比如 Student 包含一个 class 的属性,也有一个 classUser 属性,此时会存在混淆。可以明确在属性之间加上 _
以显式表达意图,比如 findByClass_UserAge()
或者 findByClassUser_Age
。还有使用一些特殊的参数来完成某些操作,比如说分页操作:Page<UserModel> findByName(String name, Pageable pageable)
;排序操作:List<UserModel> findByName(String name, Sort sort)
1.2.5 Specifications 动态查询
有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在 SpringDataJPA 中可以通过 JpaSpecificationExecutor 接口查询。相比 JPQL 其优势是类型安全,更加的面向对象。 JpaSpecificationExecutor 这个接口基本是围绕着 Specification 接口来定义的。我们可以简单的理解为,Specification 构造的就是查询条件。Specification 接口中只定义了如下一个方法:public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
参数说明:
♞ root
:Root 接口,代表查询的根对象,可以通过 root 获取实体中的属性;
♞ query
:代表一个顶层查询对象,用来自定义查询;
♞ cb
:用来构建查询,此对象里有很多条件方法。
我们写的 Repository 接口需要继承 JpaSpecificationExecutor 之后才能进行动态查询,如下所示:
/**
* Created with IntelliJ IDEA.
*
* @author Demo_Null
* @date 2020/7/30
* @description student repository 接口
*/
public interface StudentRepository extends JpaRepository<Student, Long>, JpaSpecificationExecutor<Student> {}
☞ 基于 Specifications 完成条件查询
/**
* Created with IntelliJ IDEA.
*
* @author Demo_Null
* @date 2020/7/30
* @description 测试类
*/
@RunWith(SpringRunner.class)
@SpringBootTest()
public class Demo {
@Autowired
private StudentRepository studentRepository;
@Test
public void test() {
// 使用匿名内部类的方式,创建一个 Specification 的实现类,并实现 toPredicate 方法
Specification<Student> spec = new Specification<Student>() {
public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// cb: 构建查询,添加查询方式 like:模糊匹配
// root:从实体 Student 对象中按照 Name 属性进行查询
return cb.like(root.get("Name").as(String.class), "张");
}
};
List<Student> one = studentRepository.findAll(spec);
System.out.println(one);
}
}
☞ 基于 Specifications 的分页查询
对于 SpringDataJPA 中的分页查询,是其内部自动实现的封装过程,返回的是一个 SpringDataJPA 提供的 pageBean 对象。其中的方法有 int getTotalPages()
:获取总页数;long getTotalElements()
:获取总记录数;List<T> getContent()
:获取列表数据
/**
* Created with IntelliJ IDEA.
*
* @author Demo_Null
* @date 2020/7/30
* @description 测试类
*/
@RunWith(SpringRunner.class)
@SpringBootTest()
public class Demo {
@Autowired
private StudentRepository studentRepository;
@Test
public void test() {
// PageRequest 实现了 Pageable接口,调用静态方法 of 第一个参数:页码(从0开始); 第二个参数:每页查询条数
Pageable pageable = PageRequest.of(0, 1);
// 分页查询
Page<Student> page = studentRepository.findAll(pageable);
System.out.println(page.getContent());
}
}
☞ CriteriaBuilder 方法对应关系
方法名称 |
SQL 对应关系 |
---|---|
equle |
filed = value |
gt(greaterThan) |
filed > value |
lt(lessThan) |
filed < value |
ge(greaterThanOrEqualTo ) |
filed >= value |
le(lessThanOrEqualTo) |
filed <= value |
notEqule |
filed != value |
like |
filed like value |
notLike |
filed not like value |
- Python中eval带来的潜在风险,你知道吗?
- React Native自定义导航条
- android混淆那些坑
- 微信小程序开发入门篇
- Support Annotation Library使用详解
- React Native之Navigator
- React Native组件生命周期
- React Native使用原生组件
- Android ViewDragHelper及移动处理总结
- android 仿音悦台页面交互效果
- Webpack+Babel+React开发环境搭建
- Android Studio中 .gitignore配置
- Android系统服务之WindowManager整理
- gulp+webpack工具整合简介
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 在laravel框架中实现封装公共方法全局调用
- laravel通过a标签从视图向控制器实现传值
- 在Laravel 的 Blade 模版中实现定义变量
- 解决Laravel5.5下的toArray问题
- Laravel基础_关于view共享数据的示例讲解
- 解决在laravel中leftjoin带条件查询没有返回右表为NULL的问题
- 解决laravel资源加载路径设置的问题
- laravel实现前后台路由分离的方法
- laravel5.1框架model类查询的实现方法
- laravel-admin 管理平台获取当前登陆用户信息的例子
- Yii框架Session与Cookie使用方法示例
- laravel 实现登陆后返回登陆前的页面方法
- Laravel 登录后清空COOKIE的操作方法
- 使用laravel和ajax实现整个页面无刷新的操作方法
- Laravel 读取 config 下的数据方法