上一篇介绍了入门基础篇SpringDataJPA访问数据库。本篇介绍SpringDataJPA进一步的定制化查询,使用JPQL或者SQL进行查询、部分字段映射、分页等。本文尽量以简单的建模与代码进行展示操作,文章比较长,包含查询的方方面面。如果能耐心看完这篇文章,你应该能使用SpringDataJPA应对大部分的持久层开发需求。如果你需要使用到动态条件查询,请查看下一篇博客,专题介绍SpringDataJPA的动态查询。

一、入门引导与准备


JPQL(JavaPersistence Query Language)是一种面向对象的查询语言,它在框架中最终会翻译成为sql进行查询,如果不知JPQL请大家自行谷歌了解一下,如果你会SQL,了解这个应该不废吹灰之力。

1.核心注解@Query介绍


使用SpringDataJPA进行JPQL/SQL一般查询的核心是@Query注解,我们先来看看该注解


  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
  3. @QueryAnnotation
  4. @Documented
  5. public @interface Query {
  6. String value() default "";
  7. String countQuery() default "";
  8. String countProjection() default "";
  9. boolean nativeQuery() default false;
  10. String name() default "";
  11. String countName() default "";
  12. }

该注解使用的注解位置为方法、注解类型,一般我们用于注解方法即可。@QueryAnnotation标识这是一个查询注解;

@Query注解中有6个参数,value参数是我们需要填入的JPQL/SQL查询语句;nativeQuery参数是标识该查询是否为原生SQL查询,默认为false;countQuery参数为当你需要使用到分页查询时,可以自己定义(count查询)计数查询的语句,如果该项为空但是如果要用到分页,那么就使用默认的主sql条件来进行计数查询;name参数为命名查询需要使用到的参数,一般配配合@NamedQuery一起使用,这个在后面会说到;countName参数作用与countQuery相似,但是使用的是命名查询的(count查询)计数查询语句;countProjection为涉及到投影部分字段查询时的计数查询(count查询);关于投影查询,待会会说到。

有了@Query基础后,我们就可以小试牛刀一把了,对于jar包依赖,我们用的依旧是上一节的依赖,代码如下:

2.准备实验环境



  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>1.4.1.RELEASE</version>
  5. </parent>
  6. <properties>
  7. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  8. <java.version>1.8</java.version>
  9. <springBoot.groupId>org.springframework.boot</springBoot.groupId>
  10. </properties>
  11. <dependencies>
  12. <!-- SpringBoot Start -->
  13. <dependency>
  14. <groupId>${springBoot.groupId}</groupId>
  15. <artifactId>spring-boot-starter-web</artifactId>
  16. </dependency>
  17. <!-- jpa -->
  18. <dependency>
  19. <groupId>${springBoot.groupId}</groupId>
  20. <artifactId>spring-boot-starter-data-jpa</artifactId>
  21. </dependency>
  22. <dependency>
  23. <groupId>${springBoot.groupId}</groupId>
  24. <artifactId>spring-boot-starter-test</artifactId>
  25. </dependency>
  26. <!-- mysql -->
  27. <dependency>
  28. <groupId>mysql</groupId>
  29. <artifactId>mysql-connector-java</artifactId>
  30. </dependency>
  31. <dependency>
  32. <groupId>junit</groupId>
  33. <artifactId>junit</artifactId>
  34. <version>4.12</version>
  35. </dependency>
  36. </dependencies>

项目结构如下:

JpaConfiguration配置类与上篇的相同:


  1. @Order(Ordered.HIGHEST_PRECEDENCE)
  2. @Configuration
  3. @EnableTransactionManagement(proxyTargetClass=true)
  4. @EnableJpaRepositories(basePackages={"org.fage.**.repository"})
  5. @EntityScan(basePackages={"org.fage.**.entity"})
  6. public class JpaConfiguration {
  7. @Bean
  8. PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor(){
  9. return new PersistenceExceptionTranslationPostProcessor();
  10. }
  11. }

App类:


  1. @SpringBootApplication
  2. @ComponentScan("org.fage.**")
  3. public class App {
  4. public static void main(String[] args) throws Exception {
  5. SpringApplication.run(App.class, args);
  6. }
  7. }

对于实体建模依旧用到上一篇所用的模型Department、User、Role,Department与User为一对多,User与Role为多对多,为了方便后面介绍投影,user多增加几个字段,代码如下:


  1. @Entity
  2. @Table(name = "user")
  3. public class User implements Serializable {
  4. private static final long serialVersionUID = -7237729978037472653L;
  5. @Id
  6. @GeneratedValue(strategy = GenerationType.IDENTITY)
  7. private Long id;
  8. private String name;
  9. private String password;
  10. @Column(name = "create_date")
  11. @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  12. @Temporal(TemporalType.TIMESTAMP)
  13. private Date createDate;
  14. private String email;
  15. // 一对多映射
  16. @ManyToOne
  17. @JoinColumn(name = "department_id")
  18. private Department department;
  19. // 多对多映射
  20. @ManyToMany @JsonBackReference
  21. @JoinTable(name = "user_role", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = {
  22. @JoinColumn(name = "role_id") })
  23. private List<Role> roles;
  24. //getter and setter .....
  25. }

  1. @Entity
  2. @Table(name = "department")
  3. public class Department implements Serializable {
  4. /**
  5. *
  6. */
  7. private static final long serialVersionUID = 3743774627141615707L;
  8. @Id
  9. @GeneratedValue(strategy=GenerationType.IDENTITY)
  10. private Long id;
  11. private String name;
  12. @OneToMany(mappedBy = "department")@JsonBackReference
  13. @JsonBackReferenceprivate List<User> users;
  14. //getter and setter
  15. }

  1. @Entity
  2. @Table(name="role")
  3. public class Role implements Serializable{
  4. /**
  5. *
  6. */
  7. private static final long serialVersionUID = 1366815546093762449L;
  8. @Id
  9. @GeneratedValue(strategy=GenerationType.IDENTITY)
  10. private Long id;
  11. private String name;
  12. //getter and setter
  13. }

建模成功时,生成的表结构如下:

对于Repository:


  1. @Repository
  2. public interface DepartmentRepository extends JpaRepository<Department, Long>{}

  1. @Repository
  2. public interface RoleRepository extends JpaRepository<Role, Long>{}

  1. @Repository
  2. public interface UserRepository extends JpaRepository<User, Long>{
  3. }

如果以上代码有看不懂的地方,请移步到上一篇一览基础篇。至此,我们已经将环境整理好了,至于表中的数据插入,希望各位参考上一篇文章进行基础的crud操作将表中数据进行填充,接下来介绍@Query查询

二、使用JPQL查询


1 .核心查询与测试样例


在UserRepository中增加以下方法:


  1. //--------------JPQL查询展示-------------//
  2. //展示位置参数绑定
  3. @Query(value = "from User u where u.name=?1 and u.password=?2")
  4. User findByNameAndPassword(String name, String password);
  5. //展示名字参数绑定
  6. @Query(value = "from User u where u.name=:name and u.email=:email")
  7. User findByNameAndEmail(@Param("name")String name, @Param("email")String email);
  8. //展示like模糊查询
  9. @Query(value = "from User u where u.name like %:nameLike%")
  10. List<User> findByNameLike(@Param("nameLike")String nameLike);
  11. //展示时间间隔查询
  12. @Query(value = "from User u where u.createDate between :start and :end")
  13. List<User> findByCreateDateBetween(@Param("start")Date start, @Param("end")Date end);
  14. //展示传入集合参数查询
  15. @Query(value = "from User u where u.name in :nameList")
  16. List<User> findByNameIn(@Param("nameList")Collection<String> nameList);
  17. //展示传入Bean进行查询(SPEL表达式查询)
  18. @Query(value = "from User u where u.name=:#{#usr.name} and u.password=:#{#usr.password}")
  19. User findByNameAndPassword(@Param("usr")User usr);
  20. //展示使用Spring自带分页查询
  21. @Query("from User u")
  22. Page<User> findAllPage(Pageable pageable);
  23. //展示带有条件的分页查询
  24. @Query(value = "from User u where u.email like %:emailLike%")
  25. Page<User> findByEmailLike(Pageable pageable, @Param("emailLike")String emailLike);

TestClass的代码如下:


  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest
  3. public class TestClass {
  4. final Logger logger = LoggerFactory.getLogger(TestClass.class);
  5. @Autowired
  6. UserRepository userRepository;
  7. @Test
  8. public void testfindByNameAndPassword(){
  9. userRepository.findByNameAndPassword("王大帅", "123");
  10. }
  11. @Test
  12. public void testFindByNameAndEmail(){
  13. userRepository.findByNameAndEmail("张大仙", "2@qq.com");
  14. }
  15. @Test
  16. public void testFindByNameLike(){
  17. List<User> users = userRepository.findByNameLike("马");
  18. logger.info(users.size() + "----");
  19. }
  20. @Test
  21. public void testFindByCreateDateBetween() throws ParseException{
  22. List<User> users = userRepository.findByCreateDateBetween(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2018-01-01 00:00:00"), new Date(System.currentTimeMillis()));
  23. logger.info(users.size() + "----");
  24. }
  25. @Test
  26. public void testFindByNameIn(){
  27. List<String> list = new ArrayList<String>();
  28. list.add("王大帅");
  29. list.add("李小三");
  30. userRepository.findByNameIn(list);
  31. }
  32. @Test
  33. public void testfindByNameAndPasswordEntity(){
  34. User u = new User();
  35. u.setName("李小三");
  36. u.setPassword("444");
  37. userRepository.findByNameAndPassword(u);
  38. }
  39. @Test
  40. public void testFindAllPage(){
  41. Pageable pageable = new PageRequest(0,5);
  42. Page<User> page = userRepository.findAllPage(pageable);
  43. ObjectMapper mapper = new ObjectMapper();
  44. String json = mapper.writeValueAsString(page); 
  45. logger.info(json);
  46. }
  47. @Test
  48. public void findByEmailLike(){
  49. Pageable pageable = new PageRequest(0,5,new Sort(Direction.ASC,"id"));
  50. userRepository.findByEmailLike(pageable, "@qq.com");
  51. }
  52. }

至此,显示了使用JPQL进行单表查询的绝大多数操作,当你在实体设置了fetch=FetchType.LAZY 或者EAGER时,会有不同的自动连接查询,鼓励大家自行尝试。以上查询语句有必要对其中几个进行解释一下;

对于UserRepository中的第一与第二个方法,目的是为了比较与展示位置绑定与名字绑定的区别,相信根据名称大家就能判别是什么意思与区别了,位置绑定即是方法参数从左到右第123456...所在位置的参数与查询语句中的第123456...进行对应。名字绑定即是查询语句中的参数名称与方法参数名称一一对应;对于第三个与第四个查询例子就不多说了;第五条查询语句展示的是传入集合进行in查询;第六条查询例子展示的是传入bean进行查询,该查询使用的表达式是Spring的SPEL表达式;

2. 分页与排序


最后两条查询语句展示的是进行分页查询、分页并排序查询,使用的计数查询默认使用主查询语句中的条件进行count, 当Repository接口的方法中含有Pageable参数时,那么SpringData认为该查询是需要分页的;org.springframework.data.domain.Pageable是一个接口,接口中定义了分页逻辑操作,它具有一个间接实现类为PageRequest,我们最需要关注的是PageRequest这个实现类的三个构造方法:


  1. public class PageRequest extends AbstractPageRequest {
  2. ....
  3. ....
  4. public PageRequest(int page, int size) {
  5. this(page, size, null);
  6. }
  7. public PageRequest(int page, int size, Direction direction, String... properties) {
  8. this(page, size, new Sort(direction, properties));
  9. }
  10. public PageRequest(int page, int size, Sort sort) {
  11. super(page, size);
  12. this.sort = sort;
  13. }
  14. ....
  15. ....
  16. }

page参数为页码(查第几页,从0页开始),size为每页显示多少条记录数;

Direction则是一个枚举,如果该参数被传入则进行排序,常用的有Direction.ASC/Direction.DESC,即正序排与逆序排,如果排序,需要根据哪个字段排序呢?properties是一个可变长参数,传入相应字段名称即可根据该字段排序。还有最后一个参数Sort,Sort这个类中有一个构造方法:public Sort(Direction direction, String... properties),没错,我不用说相信大家都已经懂了是干什么用的了。

Pageable与PageRequest的关系解释完了,那么就该介绍一下最后两条查询语句的返回值Page<T>是干什么用的了,让我们看看倒数第二个测试方法返回的json串结果:


  1. { "content": [
  2. { "id": 1,"name": "王大帅","password": "123", "createDate": 1515312688000, "email": "1@qq.com","department": { "id": 1, "name": "开发部"}},
  3. { "id": 2, "name": "张大仙", "password": "456", "createDate": 1515139947000, "email": "2@qq.com", "department": {"id": 1, "name": "开发部" }},
  4. {"id": 3, "name": "李小三","password": "789","createDate": 1514794375000, "email": "3@qq.com","department": {"id": 1, "name": "开发部" }},
  5. {"id": 4, "name": "马上来","password": "444", "createDate": 1512116003000, "email": "4@qq.com", "department": { "id": 1,"name": "开发部" } },
  6. { "id": 5, "name": "马德华", "password": "555","createDate": 1515312825000,"email": "5@qq.com","department": { "id": 1, "name": "开发部"} }],
  7. "last": true,
  8. "totalPages": 1,
  9. "totalElements": 5,
  10. "size": 5,
  11. "number": 0,
  12. "sort": null,
  13. "first": true,
  14. "numberOfElements": 5
  15. }

跟踪源码得到结论,Page<T>是一个接口,它的基类接口Slice<T>也是一个接口,而实现类Chunk实现了Slice,实现类PageImpl继承了Chunk并且实现了Page接口。所以实际上Json输出的字符串是PageImpl的拥有的所有属性(包括其父类Chunk)。content属性是分页得出的实体集合,类型为List,也就是上面json串中的content。last属性表示是否为最后一页,totalPages表示总页数,totalElements表示总记录数,size为每页记录数大小,number表示当前为第几页,numberOfElements表示当前页所拥有的记录数,first表示当前是否第一页,sort为排序信息。

到这里,Page与Pageable都了解了。

3. 关联查询与部分字段映射投影


接下来介绍使用JPQL进行关联查询与部分字段映射。现在的查询需求是,查出所有用户的名字、用户所属部门、用户的email、统计用户所拥有的角色有多少个,然后将列表结果进行给前端显示。有的朋友说,那我把关联到的对象都拿出来不就完了。可是,实际开发中一个表下有几十个字段会很常见,如果全部都拿出来是没有必要的,所以我们可以把需要的字段拿出来就可以了,下面介绍两种方法实现这种需求。

3.1 使用VO(view object)做映射与投影


我们在src/main/java中增加一个org.fage.vo包,该包下存放VO对象,我们在该包下创建一个UserOutputVO:


  1. public class UserOutputVO {
  2. private String name; //用户的名字
  3. private String email; //用户的email
  4. private String departmentName; //用户所属的部门
  5. private Long roleNum; //该用户拥有的角色数量
  6. public UserOutputVO(String name, String email, String departmentName, Long roleNum) {
  7. super();
  8. this.name = name;
  9. this.email = email;
  10. this.departmentName = departmentName;
  11. this.roleNum = roleNum;
  12. }
  13. public UserOutputVO() {
  14. super();
  15. }
  16. //getter and setter and toString
  17. ...
  18. }

在UserRepository中创建查询方法:


  1. @Query(value = "select new org.fage.vo.UserOutputVO(u.name, u.email, d.name as departmentName, count(r.id) as roleNum) from User u "
  2. + "left join u.department d left join u.roles r group by u.id")
  3. Page<UserOutputVO> findUserOutputVOAllPage(Pageable pageable);

这里注意一下,VO中的构造方法参数一定要与查询语句中的查询字段类型相匹配(包括数量),如果不匹配就会报错。以下是测试代码:


  1. @Test
  2. public void testFindUserOutputVOAllPage(){
  3. Pageable pageable = new PageRequest(0,5);
  4. Page<UserOutputVO> page = userRepository.findUserOutputVOAllPage(pageable);
  5. List<UserOutputVO> list = page.getContent();
  6. for(UserOutputVO vo : list)
  7. logger.info(vo.toString());
  8. }

输出结果:

对于连接查询,有join、left join 、right join,与sql的类似,但是唯一需要注意的地方就是建模的关系要能连接起来,因为只有这样才能使用“.”进行连接;就像你想的那样,它是类似对象导航的,与sql的表连接有些使用上的不同,但是最终的连接结果是相同的。

3.2 使用projection接口做映射与投影


依然使用的是上面查询VO的需求进行查询,换成projection的方式,在org.fage.vo中创建一个接口:

  1. public interface UserProjection {
  2. String getName();
  3. @Value("#{target.emailColumn}")//当别名与该getXXX名称不一致时,可以使用该注解调整
  4. String getEmail();
  5. String getDepartmentName();
  6. Integer getRoleNum();
  7. }

在UserRepository中创建查询语句:


  1. //故意将email别名为emailColumn,以便讲解@Value的用法
  2. @Query(value = "select u.name as name, u.email as emailColumn, d.name as departmentName, count(r.id) as roleNum from User u "
  3. + "left join u.department d left join u.roles r group by u.id")
  4. Page<UserProjection> findUserProjectionAllPage(Pageable pageable);

在TestClass中添加测试方法:


  1. @Test
  2. public void testFindUserProjectionAllPage(){
  3. Page<UserProjection> page = userRepository.findUserProjectionAllPage(new PageRequest(0,5));
  4. Collection<UserProjection> list = page.getContent();
  5. for(UserProjection up : list){
  6. logger.info(up.getName());
  7. logger.info(up.getEmail());
  8. logger.info(up.getDepartmentName());
  9. logger.info(up.getRoleNum()+"");
  10. }
  11. }

测试结果是成功的。在这里需要注意几点约束,Projection接口中必须以“getXXX”来命名方法,关于“XXX”则是要与查询语句中的别名相对应,注意观察上面的Projection接口与查询语句就发现了。不难发现,有一个别名为emailColumn,与Projection接口中的getEmail方法并不对应,这种时候可以使用@Value{"${target.xxx}"}注解来调整,注意其中的target不能省略,可以把target看成用别名查出来的临时对象,这样就好理解了。

两种方式都可以,对于到底哪种方式好,这取决于你的需求。

4.命名查询

如果以上查询实例都弄懂了,那么命名查询也是类似的,换汤不换药;这里简单的只举两个例子,需求变更时请大家自行尝试。
命名查询的核心注解是@NamedQueries 与 @NamedQuery;@NamedQueries中只有一个value参数,该参数是@NamedQuery的数组。@NamedQuery注解我们需要关注两个参数,query参数也就是需要写入查询语句的地方;name参数则是给该查询命名,命名方式一般约定为  “实体类名.实体对应的repository的查询方法名”,如果看不懂没关系,请看下面的例子。
在Role实体中增加以下代码:

  1. @Entity
  2. @Table(name="role")
  3. @NamedQueries({
  4. @NamedQuery(name = "Role.findById", query = "from Role r where r.id=?1"),
  5. @NamedQuery(name = "Role.findAllPage", query = "from Role r")
  6. //...更多的@NamedQuery
  7. })
  8. public class Role implements Serializable{
  9. private static final long serialVersionUID = 1366815546093762449L;
  10. @Id
  11. @GeneratedValue(strategy=GenerationType.IDENTITY)
  12. private Long id;
  13. private String name;
  14. public Role(){
  15. super();
  16. }
  17. public Role(String name){
  18. this.name = name;
  19. }
  20. //getter and setter
  21. }

对应的RoleRepository代码:


  1. @Repository
  2. public interface RoleRepository extends JpaRepository<Role, Long>{
  3. Role findById(Long id);
  4. Page<Role> findAllPage(Pageable pageable);
  5. }

相应的测试代码:



  1. @Test
  2. public void testFindRoleById(){
  3. roleRepository.findById(1l);
  4. }
  5. @Test
  6. public void testFindRoleAllPage(){
  7. roleRepository.findAll(new PageRequest(0,5));
  8. }

以上就是命名查询的常用方式。

5. JPQL方式总结

还是比较建议使用JPQL方式,因为SpringDataJPA各方面(比如分页排序)、动态查询等等都支持得比较好,Spring的SPEL表达式还可以扩展到SpringSecurity与SpringDataJPA高级的session用户查询方式,后续博客会有对SpringSecurity的介绍,等到那时候在一起讲解。

三、使用原生SQL查询

有些时候,JPQL使用不当会导致转化成的sql并不如理想的简洁与优化,所以在特定场合还是得用到原生SQL查询的,比如当你想优化sql时等等。

1 .一般查询

使用原生查询时用的也是@Query注解,此时nativeQuery参数应该设置为true。我们先来看一些简单的查询


  1. @Query(value = "select * from user u where u.id=:id", nativeQuery = true)
  2. User findByIdNative(@Param("id")Long id);
  3. @Query(value = "select * from user", nativeQuery = true)
  4. List<User> findAllNative();

看看测试代码:


  1. @Test
  2. @Transactional
  3. public void testFindByIdNative(){
  4. User u = userRepository.findByIdNative(1l);
  5. logger.info(u.toString());
  6. logger.info(u.getRoles().toString());
  7. }
  8. @Test
  9. public void testFindAllNative(){
  10. List<User> list = userRepository.findAllNative();
  11. for(User u : list){
  12. logger.info(u.toString());
  13. }
  14. }

结果发现当查所有字段的时候,确实能映射成功,并且fetch快加载、懒加载自动关联也能正常使用。接下来我们换刚才使用JPQL时的查询需求,看看用SQL时该怎么做。

2.投影与映射分页查询

查询列表的需求依旧是刚才介绍使用JPQL时使用的需求(分页查出所有用户的名字、用户所属部门、用户的email、统计用户所拥有的角色有多少个),在UserRepository中创建代码片段:


  1. //展示原生查询
  2. @Query(value = "select u.name as name, u.email as emailColumn, d.name as departmentName, count(ur.role_id) as roleNum from user u "
  3. + "left join department d on d.id=u.department_id left join user_role ur on u.id=ur.user_id group by u.id limit :start,:size",
  4. nativeQuery = true)
  5. List<Object[]> findUserProjectionAllPageNative(@Param("start")int start, @Param("size")int size);
  6. //count语句
  7. @Query(value = "select count(u.id) from user u", nativeQuery = true)
  8. long countById();

在TestClass中创建测试代码:


  1. @Test
  2. public void testFindUserProjectionAllPageNative(){
  3. Pageable pageable = new PageRequest(0,5);
  4. List<Object []> content = userRepository.findUserProjectionAllPageNative(pageable.getOffset(), pageable.getPageSize());
  5. long total = userRepository.countById();
  6. //查看一下查询结果
  7. logger.info(content.size() + "");
  8. for(Object[] o : content){
  9. logger.info("名字:" + o[0].toString());
  10. logger.info("email:" + o[1].toString());
  11. logger.info("所属部门" + o[2].toString());
  12. logger.info("角色数量" + o[3].toString());
  13. }
  14. //如果需要的话,自行封装分页信息
  15. Page<Object[]> page = new PageImpl<Object[]>(content, pageable, total);
  16. System.out.println(page);
  17. }

解释一下上面代码,由于是原生查询不支持动态分页,Page分页我们只能自己做了,但是依旧使用的是Spring的Page;pageable.getOffset()与pageable.getPageSize()分别对应limit ?, ?的第一与第二个问号。原生查询得出来的List是包函一堆被封装成Object的对象数组,每个object数组可以通过数组索引拿出值,也就与需要查的字段一一对应。如果你需要存入VO再带回给前端,那么你可以自行封装。对于PageImpl,我们使用了public
PageImpl(List<T> content, Pageable pageable, long total) 这个构造方法,第一个参数是查询到的结果,第二个就不用说了,第三个参数是对主sql的count查询。当前端需要显示分页时,可以这样进行手动分页。

3.SQL方式总结

当你需要进行sql优化时,可能用原生sql方式会更好。但是一般需求时候用JPQL还是比较方便的,毕竟这样比较省事,拿数据总是需要分页的,有时候只需要拿几个字段也是这样。

四、总结


当你在接到一般需求时,使用JPQL的方式其实已经足够用了。但是如果对sql需要优化的时候,你也可以使用SQL的方式。总而言之,需要根据需求来应变使用的策略。

如果文中有不当的地方欢迎同学们提出建议与修改方案,但是请不要谩骂与辱骂。

下一篇将讲解SpringDataJPA根据动态条件进行查询的方方面面。

SpringData JPA进阶查询—JPQL/原生SQL查询、分页处理、部分字段映射查询的更多相关文章

  1. Django框架08 /聚合查询、分组、F/Q查询、原生sql相关

    Django框架08 /聚合查询.分组.F/Q查询.原生sql相关 目录 Django框架08 /聚合查询.分组.F/Q查询.原生sql相关 1. 聚合查询 2. 分组 3. F查询和Q查询 4. o ...

  2. JPA或Hibernate中使用原生SQL实现分页查询、排序

    发生背景:前端展示的数据需要来自A表和D表拼接,A表和D表根据A表的主键进行关联,D表的非主键字段关联C表的主键,根据条件筛选出符合的数据,并且根据A表的主键关联B表的主键(多主键)的条件,过滤A表中 ...

  3. 08章 分组查询、子查询、原生SQL

    一.分组查询 使用group by关键字对数据分组,使用having关键字对分组数据设定约束条件,从而完成对数据分组和统计 1.1 聚合函数:常被用来实现数据统计功能 ① count() 统计记录条数 ...

  4. orm分组,聚合查询,执行原生sql语句

    from django.db.models import Avg from app01 import models annotate:(聚合查询) ret=models.Article.objects ...

  5. Hibernate 函数 ,子查询 和原生SQL查询

    一. 函数 聚合函数:count(),avg(),sum(),min(),max() 例:(1)查询Dept表中的所有的记录条数. String hql=" select count(*) ...

  6. Spring的jdbcTemplate查询执行原生sql

    在spring与hibernate整合时进行数据库检索,执行原生sql: public AppointmentEvaluateVo searchMyfeedbackDetail(String acco ...

  7. Hibernate使用原生SQL语句进行无关联多表查询

    背景:有两个表:CpCg与CpGg需要进行多表查询 因为CpGg表设计到与另外的表也有联系,因此师兄没有关联此两个表,只是用字段进行逻辑关联,CpGg表的cp字段与CpCg表的id字段逻辑关联

  8. [oldboy-django][2深入django]mysql查询语句--原生sql

    # 增(一共有三种方式) # 插入单条记录 insert into t1(name,...) values('lzp',..); 注意一点:t1(name,...)必须包含所有非空列(除去自增列) # ...

  9. 解决Sql server分页时第二页以上查询结果不正常的问题

    有100个产品,其中最高价格的为200元,而200元的产品共有40个, 现在好了,对每页30进行分页: declare @PageSize int--30 declare @Page int decl ...

随机推荐

  1. jsonp对付同源策略

    当 协议不同或者域名/ip不同或者端口号不同 ,  都不算是同源 这时候 源生的ajax 就不能进行数据请求了 JSONP json with padding 在平时的开发中也发现了  ,当我们请求  ...

  2. Synplify FPGA 逻辑综合

    作为 Synopsys FPGA 设计解决方案的一部分,Synplify FPGA 综合软件是实现高性能.高性价比的 FPGA 设计的行业标准. 其独特的行为提取综合技术 (Behavior Extr ...

  3. PMP项目管理学习笔记(4)——项目整合管理

    六个整合管理过程. 1.制定项目章程 一个新项目要完成的第一件事,就是项目章程的制定.这是授权你开展工作的文档.不过并不总是需要你介入,通常情况下会由赞助人交给你.如果没有项目章程,你就没有权利告诉你 ...

  4. android 代码中及xml中设置透明

    在布局文件的属性中,比如要设置一个LineaerLayout的背景为灰色透明.首先查RGB颜色表灰色是:#9E9E9E,AA代表透明,(透明度从00到FF,00表示完全透明),所以,设置其属性:and ...

  5. codevs 1979 第K个数

     时间限制: 1 s  空间限制: 1000 KB  题目等级 : 黄金 Gold 题目描述 Description 给定一个长度为N(0<n<=10000)的序列,保证每一个序列中的数字 ...

  6. help命令

    help——获得Shell内置命令的帮助信息 命令所在路径:Shell内置命令 示例1: # help cd 使用which或者whereis查找不到路径的命令一般是Shell内置命令,cd就是一个S ...

  7. SpringBoot集成FastDFS+Nginx整合基于Token的防盗链

    为什么要用SpringBoot? SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人 ...

  8. Vue.js系列之vue-router(上) (转载自向朔1992)

    概述 Vue非常适用于实践单页面应用程序也就是平时大家说的比较多的SPA(single page application),这点应该了解过Vue的应该都知道吧.一般的单页面应用是基于路由或页面之间的链 ...

  9. Caused by: java.lang.ClassNotFoundException: Cannot find class: User

    源代码: <select id="selectAll" resultType="User"> select user_id uid,user_nam ...

  10. fossil 使用

    ~$ fossil updateCannot figure out who you are! Consider using the --usercommand line option, setting ...