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(); }
