spring data jpa 提供了方便快捷的查询数据库方式,只要按照它的约定,编写接口和函数定义,即可很方便的从数据库中查询到想用的数据。
https://docs.spring.io/spring-data/jpa/docs/1.11.13.RELEASE/reference/html/#jpa.repositories
但是每个应用业务逻辑的复杂度不同,有时候是必须要自己定义JPQL甚至native sql 来做自己的查询,本文介绍如何创建自定义的查询。
1. 自定义一个接口,该接口用来声明自己额外定义的查询。
public interface CustomizedLogRepository {
public List<LogDTO> searchLogs(String appId, String keyword);
public long searchLogCount(String appId, String keyword);
}
2. 创建一个接口,该接口 extends JpaRepository 或者 CurdRepository, 以及上面自己定义的接口 CustomizedLogRepository
public interface LogRepository extends CrudRepository<LogDTO, Integer>, CustomizedLogRepository {
}
3. 实现LogRepository
注意此处的类名,必须以 2 中创建的接口的名字LogRepository ,后面加上 Impl 来声明,而不是写成 CustomizedLogRepositoryImpl
public class LogRepositoryImpl implements CustomizedLogRepository {
@Autowired
@PersistenceContext
private EntityManager entityManager;
@Override
public List<Ankonlog> searchLogs(String appId, String keyword) {
......
}
@Override
public long searchLogCount(String appId, String keyword) {
......
}
}
至此,自定义JPQL查询完成。
native query
native query 可以用原始的 sql 而不是 jpql 来查询数据库,更加的灵活。
但是Spring Data JPA 对于 native query 分页、排序的处理并不支持,但是可以通过如下方法解决:
@Query(value = "SELECT DISTINCT device_sn FROM motion_record WHERE device_sn LIKE %:deviceSn% \n#pageable\n",
countQuery = "SELECT COUNT(DISTINCT device_sn) FROM motion_record WHERE device_sn LIKE %:deviceSn%",
nativeQuery = true)
public Page<String> searchDevices(@Param("deviceSn") String deviceSn, Pageable pageable);
本例中结合了Page进行分页,需要特别注意的地方有两个点
- 必须使用
\n#pageable\n
来注入分页参数 - 参数的传递必须用
@Param
,试过使用?1
的占位符,但是运行始终报错(Spring Data JPA 1.11.13 版本)
自定义查询结果对象
有时候我们会联表查询多个表的数据,组合成我们需要的查询结果(比如 LEFT JOIN),此时我们需要另外单独定一个实体类来做返回值的映射,比如以下JPQL:
SELECT t1.id, t2.name FROM table1 t1 LEFT JOIN table2 t2 on t1.id = t2.id;
返回值是从不同的表中取出的部分字段组合而成的,那么我们可以定一个实体类:
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class ResultEntity implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
public ResultEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
// 所有属性的 getter/setter
.....
}
此处实体类的定义有几个关键点
- 一定要用 @Entity 注解
- 一定要有一个主键属性,用 @Id 注解
- 一定要有包含全部参数的构造函数
然后,JPQL 需要改写成
SELECT new ResultEntity(t1.id, t2.name) FROM table1 t1 LEFT JOIN table2 t2 on t1.id = t2.id;
如果出现报错找不到实体类,可以在 ResultEntity 前面加上完整的包路径