🤵♂️ 个人主页:@香菜的个人主页,加 ischongxin ,备注csdn
✍🏻作者简介:csdn 认证博客专家,游戏开发领域优质创作者,华为云享专家,2021年度华为云年度十佳博主
🐋 希望大家多多支持,我们一起进步!😄
如果文章对你有帮助的话,
欢迎评论 💬点赞👍🏻 收藏 📂加关注+
前言
之前文章已经写过一次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);
}
@Nullable
Predicate 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 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 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
在工作中根据自己的需求选择对应的方式