目录
  • 前言
  • 1.方法方式
    • 方法说明
    • 例子
  • 2.注解方式
    • 注解说明
    • 例子
    • 多表联查,且多条件、分页查询怎么写?
  • 小结
    • 总结

      前言

      SpringData JPA常用有两种写法,一个是用Jpa自带方法进行CRUD,适合简单查询场景、例如查询全部数据、根据某个字段查询,根据某字段排序等等。另一种是使用注解方式,@Query@Modifying

      1.方法方式

      方法说明

      接口方法如下,方法作用见注释:

      public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
          // 无条件,查询全部记录
          List<T> findAll();
          // 排序查询
          List<T> findAll(Sort var1);
          // 根据主键ID查询
          List<T> findAllById(Iterable<ID> var1);
          // 批量保存集合数据
          <S extends T> List<S> saveAll(Iterable<S> var1);
      
          void flush();
       
          <S extends T> S saveAndFlush(S var1);
          // 批量删除
          void deleteInBatch(Iterable<T> var1);
          // all in 全部删除
          void deleteAllInBatch();
          // 查询一条记录
          T getOne(ID var1);
          // 条件查询
          <S extends T> List<S> findAll(Example<S> var1);
          // 条件查询,带排序
          <S extends T> List<S> findAll(Example<S> var1, Sort var2);
      }

      例子

      一般dao实现JpaRepository接口,直接调用JpaRepository中的方法就可以实现了简单查询,例如查询User实例列表:

      // 构建user的Example对象
      Example<User> example =Example.of(User);
      List<User> users = userRepository.findAll(example);

      2.注解方式

      jpa实现CRUD的主要注解是@Query

      注解说明

      @Query注解主要有以下参数,参数作用如下:

      • value:SQL语句
      • countQuery: 分页查询时统计总数
      • nativeQuery: 使用执行这个方法的时候执行原生sql语句,直接写数据库中的实际表名和表的实际字段名

      @Query的代码如下:

      @Retention(RetentionPolicy.RUNTIME)
      @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
      @QueryAnnotation
      @Documented
      public @interface Query {
          String value() default "";
          String countQuery() default "";
          String countProjection() default "";
          boolean nativeQuery() default false;
          String name() default "";
          String countName() default "";
      }

      例子

      • 使用注解方式分组查询

      跟正常写sql语句相同,将sql写到value中,并且nativeQuery = true。下面例子是根据task_id进行分组查询task集合

      @Query(value = "select task_id from task group by task_id", nativeQuery = true)
      List<Task> queryByGroup();
      • 使用注解方式排序

      根据task_id进行排序查询task集合

      @Query(value = "select task_id,task_date from task order by task_id", nativeQuery = true)
      List<Task> queryOrder();
      • 使用注解方式条件查询

      条件查询时可以使用字段名 操作符 ?;例如:task_date >= ?,使用位置匹配?。也可以使用字段名 操作符 :属性名;例如:task_date >= :startDate,使用属性名匹配,推荐使用后者,如果字段顺序修改,不影响匹配结果。下面是根据任务时间(task_date)段内和未被删除(deleted)的任务

      @Query(value = "select task_id,task_date from task where task_date >=? and task_date <=? and deleted=0 ", nativeQuery = true)
      List<ApptTask> queryDate(@Param("startDate") String startDate, @Param("endDate") String endDate);
      • 使用注解方式修改

      修改一条数据需要加上@Modifying用于标识是修改操作,默认事务等级是只读,所以还需要加上@Transactional,这样覆盖了默认的@Transactional才可以执行修改操作。下面是根据task_id更新task表的备注信息

      @Transactional(rollbackOn = Exception.class)
      @Modifying
      @Query(value = "update task set remark = ? where task_id=?", nativeQuery = true)
      void updateRemark(@Param("remark") String remark, @Param("taskId") String taskId);

      多表联查,且多条件、分页查询怎么写?

      复杂的查询需要注意,以下使用一个Mysql的多表联查的例子来说明复杂的查询要怎么写。下面是usertask表关联查询出任务名称、任务ID、用户名称这些信息,并且根据task_nametask_date进行过滤;根据task_date倒序。

      共有几点需要注意:

      • 多表联查使用正常的JOIN就可以
      • 多条件是常见的情况,需要区别传入的条件是否要去执行,这种情况需要使用where 1=1 and 这种方式来保证条件不传时仍然正常查询。
      • 分页查询需要传入分页参数Pageable,并且写countQuery来统计总数。
      • 多条件查询关键:if(:参数!='',k.字段名 =:参数,1=1),这里是使用了if进行判断,这个写法类似Mybatis xml中的<if>标签。if的含义是代表传入的参数如果不为""(Spring类型空是""而不是null)将参数传入,如果为空时显示1=1 代表参数为真,对查询结果不产生作用。

      代码:

      @Query(value =
            " select a.task_name, a.task_id,u.user_name" +
            " from task a " +
            " LEFT JOIN usert u" +
            " ON a.user_id = u.user_id" +
            " where  a.deleted=0  " +
            " AND if(:taskName!='',a.task_name =:taskName,1=1)" +
            " AND if(:startDate!='',a.task_date >=:startDate,1=1)" +
            " AND if(:endDate!='',a.task_date <=:endDate,1=1)" +
            " order by a.task_date desc"
            , 
            nativeQuery = true
            , 
            countQuery =
            " select count(*)" +
            " from task a " +
            " LEFT JOIN usert u" +
            " ON a.user_id = u.user_id" +
            " where  a.deleted=0  " +
            " AND if(:taskName!='',a.task_name =:taskName,1=1)" +
            " AND if(:startDate!='',a.task_date >=:startDate,1=1)" +
            " AND if(:endDate!='',a.task_date <=:endDate,1=1)" +
            " order by a.task_date desc")
      Page<Map<String,Object>> queryUserTaskPage(@Param("taskName") String taskName, @Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate, @Param("pageable") Pageable pageable);

      小结

      以上列举了两种JPA的crud方式,jpa方法与注解方式,平时写代码时更倾向于使用注解方式去写原生sql来实现业务。对于简单查询可以用JpaRepository里面这些方法就够用了,对于更复杂的场景推荐使用@Query写sql的方式来实现。

      jpa方法可以屏蔽底层的sql,如果有不同数据库实现的服务,用jpa方法可以免于修改sql。但是jpa方法对于分组查询、limit支持、多条件、多表联查这些不太友好。

      总结

      声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。