🤵♂️ 个人主页:@香菜的个人主页
✍🏻作者简介:csdn 认证博客专家,游戏开发领域优质创作者,华为云享专家,2021年度华为云年度十佳博主
🐋 希望大家多多支持,我们一起进步!😄
如果文章对你有帮助的话,
欢迎评论 💬点赞👍🏻 收藏 📂加关注+
系列文章:Spring Boot学习大纲,可以留言自己想了解的技术点
前言
之前文章已经写过一次spring data,链接:spring-data 一统江湖,玩转多种数据源
主要的内容可以参考官方的:Spring Data JPA - Reference Documentation
今天这篇文章总结下spring data jpa的查询语法,在开发中能够灵活使用,在开始下面的之前我们定义一个entity,以便后面的部分使用
这里定义一个玩家的entity
@Data
@Entity
@Table(name = "player_info")
@ApiModel(description = "玩家信息")
public class PlayerEntity implements Serializable {private static final long serialVersionUID = 1L; @Id @Column(name = "id", nullable = false)@GeneratedValue(strategy = GenerationType.IDENTITY)@ApiModelProperty(value = "主键")@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)private Integer id; @ApiModelProperty(value = "玩家名字")@Column(name = "player_name")private String playerName; @ApiModelProperty(value = "玩家类型")@Column(name = "player_type")private String playerType;}
1、方法命名查询
这种方式是最简单的,也是最方便的,可能也是很多人包括我选择spring data jpa的主要原因之一,通过简单的组合就可以实现查询
1.1 例子几步走
1.1.1 声明一个扩展 Repository 的接口或其子接口之一
这里的entity使用PlayerEntity ,主键是用Integer
interface PlayerRepository extends Repository<PlayerEntity, Integer> { … }
1.1.2 声明查询方法
interface PlayerRepository extends Repository<PlayerEntity, Integer> {List<PlayerEntity> findByPlayerName(String playerName);
}
1.2 查询语法
按照Spring Data JPA 定义的规则,查询方法以findBy开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进行解析。
2、SQL查询
这个是常用的一种方式,直接使用sql查询,使用query注解,并且nativeQuery = true,直白点就是使用sql查询
方法名可以根据自己习惯进行命名
public interface PlayerDao extends JpaRepository<PlayerEntity, Integer>, JpaSpecificationExecutor<PlayerEntity>{@Query(value = "SELECT max(0 + SUBSTR( player_id, 3 )) FROM parking_info WHERE LEFT ( player_id, 2 ) = :zoneId", nativeQuery = true)int getMaxPlayerId(String zoneId);}
这里的问题是没办法在开发的时候做语法检查,只能在调试中进行检查
3、JPQL查询
和原生SQL语句类似,并且完全面向对象,通过类名和属性访问,而不是表名和表的属性。
这个和上面的区别在于nativeQuery = false,当然默认也是false,
@Query(value = "select companyCode from PlayerEntity where playerId = :playerId")
String findCompanyCodeByPlayerId(String playerId);
一定要注意,这里的字段和类名都是类中定义的
4、复杂查询 Specification
4.1 接口说明
需要Spring Data Jpa 支持 Criteria 查询方式,使用这种方式需要继承 JpaSpecificationExecutor 接口,该接口提供了如下一些方法
JpaSpecificationExecutor这个接口基本是围绕着Specification接口来定义的
public interface Specification<T> extends Serializable {long serialVersionUID = 1L;static <T> Specification<T> not(@Nullable Specification<T> spec) {return spec == null ? (root, query, builder) -> {return null;} : (root, query, builder) -> {return builder.not(spec.toPredicate(root, query, builder));};}static <T> Specification<T> where(@Nullable Specification<T> spec) {return spec == null ? (root, query, builder) -> {return null;} : spec;}default Specification<T> and(@Nullable Specification<T> other) {return SpecificationComposition.composed(this, other, CriteriaBuilder::and);}default Specification<T> or(@Nullable Specification<T> other) {return SpecificationComposition.composed(this, other, CriteriaBuilder::or);}@NullablePredicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);
}
通常来说我们一般使用toPredicate方法
Root:查询的根对象(查询的任何属性都可以从更对象获取)
CriteriaQuery:顶层查询对象,自定义查询方式(了解,一般不用)
CriteriaBuilder:查询构造器,封装了很多查询条件
4.2 看个官方的例子
4.2.1 定义查询条件
public class CustomerSpecs {public static Specification<Customer> isLongTermCustomer() {return (root, query, builder) -> {LocalDate date = LocalDate.now().minusYears(2);return builder.lessThan(root.get(Customer_.createdAt), date);};}public static Specification<Customer> hasSalesOfMoreThan(MonetaryAmount value) {return (root, query, builder) -> {// build query here};}
}
4.2.2 调用查询方法
List<Customer> customers = customerRepository.findAll(isLongTermCustomer());
4.2.3 项目中使用方法
Sort sort;if (order == null){sort = Sort.unsorted();}else {sort = Sort.by(order? Sort.Direction.DESC:Sort.Direction.ASC,"totalParkingSpace");}Pageable pageable = PageRequest.of(page, size,sort);Specification<ParkingInfoEntity> sp = (root, query, cb) -> {List<Predicate> condition = new ArrayList<>();if (parkingType != null) {Predicate parkingTypePredicate = cb.equal(root.get("parkingType"), parkingType);condition.add(parkingTypePredicate);}if (StringUtils.hasText(keyword)) {String likeKeyword = "%" + keyword + "%";Predicate parkingId = cb.like(root.get("parkingId"), likeKeyword);Predicate parkingName = cb.like(root.get("parkingName"), likeKeyword);Predicate parkingAddress = cb.like(root.get("parkingAddress"), likeKeyword);Predicate belongCompany = cb.like(root.get("belongCompany"), likeKeyword);Predicate or = cb.or(parkingId, parkingName, parkingAddress, belongCompany);condition.add(or);}return cb.and(condition.toArray(new Predicate[0]));};Page<ParkingInfoEntity> all = parkingInfoDao.findAll(sp, pageable);
注:这种方式主要应用在复杂条件查询时候,可以简单的理解为手动拼接sql,只不过这里更安全,更不容易出错。
用过的都说好
4.3 调用数据库内置函数
CriteriaBuilder还提供了function方法,在function方法里可以直接传方法名进去
/**
name: 方法名
returnType: 返回类型
arguments:表达式
**/
public <T> Expression<T> function(String name, Class<T> returnType, Expression... arguments) {return new ParameterizedFunctionExpression(this, returnType, name, arguments);}
调用代码如下
Expression<BigDecimal> round = cb.function("round", BigDecimal.class, quot);
cb.greaterThanOrEqualTo(round, 80)
5、QueryByExampleExecutor
它允许动态查询创建,并且不需要编写包含字段名称的查询
5.1 用法
Query by Example API 由四部分组成
- 实例,entity的实例
- ExampleMatcher,ExampleMatcher包含有关如何匹配特定字段的详细信息。它可以在多个示例中重复使用。
- Example,查询用的对象,包含查询的匹配方式和字段的值(entity实例)
5.2 实例
它用于创建查询
Person person = new Person();
person.setFirstname("Dave"); ExampleMatcher matcher = ExampleMatcher.matching() .withIgnorePaths("lastname") .withIncludeNullValues() .withStringMatcher(StringMatcher.ENDING);
Example<Person> example = Example.of(person, matcher);
6、总结
简单查询使用 查询方法组合
复杂查询使用SQL查询和JPQL查询
动态查询使用Specification和Example API
在工作中根据自己的需求选择对应的方式