SpringBoot使用JPA来做数据查询
Spring-Data-JPA在做数据存储方面真的很方便,它的目的就是写更少的代码,更多的事情,但是也有其力有未逮或者说处理起来比较闹心的地方。
1.先来感受一下使用JPA做数据查询时,代码的简化程度
@CacheConfig(cacheNames = "news")
public interface NewsRepository extends PagingAndSortingRepository<NewsEntity, Long> { @Cacheable NewsEntity findOne(Long id); @Cacheable NewsEntity findTop1ByOriginId(String originId); @Transactional long deleteByOriginId(String originId); Page<NewsEntity> findDistinctByTitleStartingWithAndSimilarIdIsNullOrderByPubDateDesc(String title, Pageable pageable); }
单表查询时,只需要根据JPA提供的规范去命名,根本不需要自己去写什么查询语句就可以。
2.当然要自己写SQL语句也没有问题
@Query(value = "select e.* from news_detail e INNER JOIN news_info n on e.news_id = n.id where n.pub_date >= ?1", nativeQuery = true) List<Object> listBypubDateWithEntityDetail(String pubDate); @Query(value = "select n.id,GROUP_CONCAT(e.ent_id) from news_info n INNER JOIN map_news_company e on e.news_id = n.id where n.pub_date>= ?1 and n.id>?2 group by n.id order by n.id limit 10000", nativeQuery = true) List<Object[]> listBypubDateWithEnts(String pubDate, long news_id);
使用原生的SQL也可以,JPA就是这么方便,然而总有需要操心的地方——多条件分组查询。用过Hibernate和Mybatis的,在写业务逻辑的时候,拼接查询条件的时候,一定写过很多if条件不为空的判断,这就是JPA操蛋的地方。
3.看看例子
@Query(value = "select pub_time,count(1) as count from t_weibo where content like %:keyword% and pub_time>=:dateFrom and pub_time<=:dateTo group by pub_time", nativeQuery = true) List<Object[]> getWeibo(@Param("keyword") String keyword, @Param("dateFrom") Date dateFrom, @Param("dateTo") Date dateTo); @Query(value = "select pub_time,count(1) as count from t_weibo where content like %:keyword% and pub_time>=:dateFrom and pub_time<=:dateTo and region in ( select keyname from t_cell where provincename=:provincename) group by pub_time", nativeQuery = true) List<Object[]> getWeiboByProvince(@Param("provincename") String provincename,@Param("keyword") String keyword,@Param("dateFrom") Date dateFrom, @Param("dateTo") Date dateTo);
这个例子两个方法的作用一样,条件个数不一样,这就是冗余了。
4.如果是这样JPA被设计出来的意义是什么,jpa有一套来应对这些的措施,使用Specification这个来来完成条件拼接
User user1 = (User) userRepository.findOne(new Specification<User>() { public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { /*criteriaQuery.where(criteriaBuilder.equal(root.<String>get("name"), user.getName()), criteriaBuilder.equal(root.<String>get("password"), user.getPwd()));*/
Predicate predicate = null;
if(user.getName!=null&&!user.getName().equal){
if(predicate!=null){
predicate = criteriaBuilder.equal(root.<String>get("name"), user.getName())
}else{
predicate = criteriaBuilder.and(predicte,criteriaBuilder.equal(root.<String>get("name"), user.getName()))
}
}
if(predicate!=null){
criteriaQuery.where(predicate);
}
return null; } });
核心就是使用CriteriaBuilder 进行条件拼接
5.还有一种方式就是使用QueryDsl插件来组合Spring Data JPA使用
添加maven依赖 <!--queryDSL--> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-jpa</artifactId> <version>${querydsl.version}</version> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <version>${querydsl.version}</version> <scope>provided</scope> </dependency> 配置querydsl插件 <build> <plugins> <!--该插件可以生成querysdl需要的查询对象,执行mvn compile即可--> <plugin> <groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.1.3</version> <executions> <execution> <goals> <goal>process</goal> </goals> <configuration> <outputDirectory>target/generated-sources/java</outputDirectory> <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
此时编译(compile)一下maven项目,在这个(target/generated-sources/java)文件夹下看到对应于你建的实体类(User)的QueryDsl类(QUser)
@Entity @Table(name = "users") @Data public class UserBean { @Id @GeneratedValue @Column(name = "u_id") private Long id; @Column(name = "u_username") private String name; @Column(name = "u_age") private int age; @Column(name = "u_score") private double socre; }
查询语句
@PersistenceContext EntityManager entityManager; @RequestMapping("query") public List<GoodEntity> list(){ QUserBean userBean = QUserBean.userBean; JPAQuery<UserBean> jpaQuery = new JPAQuery<>(entityManager); return jpaQuery.select(userBean) .from(userBean) .where(userBean.name.eq("haha")) .fetch(); }