最近利用了spring data jpa来完成数据会见层的实现。感受较量强大,也较量巨大,中间尚有不少限制。
话说数据库sql的应用有点像打怪进级,一点一点不绝增加难度。
1. 对付一般应用中种种简朴的增删查改,spring data提供了按照名字直接查询的署理要领,啥都不需要做,独一需要编写接口,定名要领,这部门实在是太利便了,并且简朴查询办理了差不多80%的问题。这部门相对简朴,不再赘述,参考用法大全部门。
2. 对付巨大但又相对牢靠的查询,可以利用JPQL和Native Sql,即@Query直接写JPQL语句的方法。这部门也不难,简朴给个例子,需要留意的是返回功效假如是多个值而且返回多组,那应该以Object[][]暗示
@Query(value = "SELECT su.id, su.name_CN, avg(s.rate), count(b.id), count(concat(b.key, '@', s.admin)) " + "FROM " + CommonConstants.SCHEMA_PREFIX + "Submarket su, " + CommonConstants.SCHEMA_PREFIX + "Building b, " + CommonConstants.SCHEMA_PREFIX + "Space s, " + CommonConstants.SCHEMA_PREFIX + "Market m, " + CommonConstants.SCHEMA_PREFIX + "City c " + "where b.submarket_id = su.id and s.building_id = b.id and su.market_id = m.id and m.city_id = c.id and c.country_id = ?1 group by su.id", nativeQuery=true) Object[][] findAreaInfoByCountryId(int parentId);
3. 对付巨大且动态的查询,利用Criteria。对付criteria的用法,那就有相当多的内容了。需要利用到criteria的场景凡是是界面上有各类过滤和条件选项。
Criteria由于需要利用类的方法将整个巨大sql语句组织起来,因此有不少类,我们先来领略下这些类的寄义。
3.1大大都环境下,搜索返回的功效是一个数据库中的实体工具,对付这种环境,实际上可以直接利用spring data jpa提供的toPredicate要领,软件开发,该要领界说如下
public interface Specification<T> { Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb); }
实际利用时只需要把预界说好的Repository工具担任JpaSpecificationExecutor工具即可
@Repository public interface CityDao extends JpaSpecificationExecutor<City>{ }
真正挪用时只需要通报如下回调要领,spring会自动帮你完身分页
Page<City> page = cityDao.findAll(new Specification<City>() { @Override public Predicate toPredicate(Root<City> root, CriteriaQuery<?> query, CriteriaBuilder cb) { root = query.from(City.class); Path<String> nameExp = root.get("name"); return cb.like(nameExp, "%北京%"); } }, new PageRequest(1, 5, new Sort(Direction.DESC, new String[] { "id" })));
对付这种环境,固然动态查询较量巨大,可是要名誉是个中相当简朴的范例了。
3.2 我们来看boss进级难度今后的环境。如果此时你的查询中不是一个简朴实体范例,而是一个巨大的聚合工具,有一堆聚合查询,有一堆a工具的属性,一堆b工具的属性。大概你还试图用toPredicate要领继承,但实际上Page只答允你通报已界说好的数据库中的实体工具,因为其root中界说的泛型实际上限制了后续的行为,好比想在root上join,假如root不是一个数据库实体则编译就报错了。别的由此激发的query.multiselect自界说查询功效无效,因为功效默认就是你界说好的谁人实体。这时候的办理步伐就是自界说dao实现类。首先,界说一个自界说实现接口
@NoRepositoryBean public interface SearchJpaRepositoryCustom { public Page<Tuple> searchListing(final ListingSearchContext searchContext, Pageable pageable); }
其次,dao接口得担任该自界说接口
public interface BuildingRepository extends PagingAndSortingRepository<Building, Integer>, SearchJpaRepositoryCustom
然后,真正的dao模子实现类如下,需要留意,自界说的实现类必需实现自界说接口,而且,名字是BuildingRepository+impl,留意这里踩过坑
public class BuildingRepositoryImpl extends PagableRepository implements SearchJpaRepositoryCustom { @PersistenceContext private EntityManager em; private Join<Space, ?> getSearchExpression(final CriteriaBuilder cb, final ListingSearchContext searchContext, final Root<Space> root, final Predicate predicate) { List<Expression<Boolean>> expressions = predicate.getExpressions(); // 只搜索版本为0的(即当前版本) expressions.add(cb.equal(root.<String> get("ver"), 0)); if (searchContext.getSpaceId() > 0) { expressions.add(cb.equal(root.<Integer> get("id"), searchContext.getSpaceId())); // id } if (null != searchContext.getMinRate()) { expressions.add(cb.greaterThanOrEqualTo(root.<BigDecimal> get("rate"), searchContext.getMinRate())); // 价值 } if (null != searchContext.getMaxRate()) { expressions.add(cb.lessThanOrEqualTo(root.<BigDecimal> get("rate"), searchContext.getMaxRate())); // 价值 } if (null != searchContext.getLCD()) { expressions.add(cb.lessThanOrEqualTo(root.<Date> get("dateAvailable"), searchContext.getLCD())); // 可用日期 } // spaceTypeId if (searchContext.getSpaceTypeId() > 0) { expressions.add(cb.equal(root.<String> get("spaceType").get("id"), searchContext.getSpaceTypeId())); } // buildingGrade&submarket Join<Space, ?> buildingJoin = root.join(root.getModel().getSingularAttribute("building"), JoinType.INNER); if (searchContext.getBuildingGradeId() > 0) { expressions.add(cb.equal(buildingJoin.get("buildingGrade").get("id"), searchContext.getBuildingGradeId())); } if (searchContext.getSubmarketId() > 0) { expressions.add(cb.equal(buildingJoin.get("submarket").get("id"), searchContext.getSubmarketId())); } if (StringUtils.isNotEmpty(searchContext.getKeyword())) { Predicate like1 = cb.like(buildingJoin.<String> get("buildingNameCn"), "%" + searchContext.getKeyword() + "%"); Predicate like2 = cb.like(buildingJoin.<String> get("addressCn"), "%" + searchContext.getKeyword() + "%"); expressions.add(cb.or(like1, like2)); // 要害字 } return buildingJoin; } @Override public Page<Tuple> searchListing(final ListingSearchContext searchContext, Pageable pageable) { final CriteriaBuilder cb = em.getCriteriaBuilder(); final CriteriaQuery<Tuple> query = cb.createTupleQuery(); final Root<Space> root = query.from(Space.class); Predicate predicate = cb.conjunction(); Join<Space, ?> buildingJoin = getSearchExpression(cb, searchContext, root, predicate); Join<Space, ?> spaceTypeJoin = root.join(root.getModel().getSingularAttribute("spaceType"), JoinType.INNER); Join<Space, ?> contiguousJoin = root.join(root.getModel().getSingularAttribute("contiguous"), JoinType.INNER); Join<Building, ?> assetJoin = buildingJoin.join("asset", JoinType.INNER); Join<BuildingGrade, ?> buildingGradeJoin = buildingJoin.join("buildingGrade", JoinType.INNER); SetJoin<Asset, ?> mediaJoin = assetJoin.joinSet("medias"); mediaJoin.on(cb.and(cb.equal(mediaJoin.get("type"), "photo"), cb.equal(mediaJoin.get("subtype"), "main"))); Expression<BigDecimal> maxConExp = cb.max(contiguousJoin.<BigDecimal> get("maxContiguous")); Expression<BigDecimal> totalConExp = cb.sum(root.<BigDecimal> get("size")); query.multiselect(cb.count(root.<Integer> get("id")), root.<Integer> get("userByAdmin").get("id"), totalConExp, maxConExp, cb.min(root.<BigDecimal> get("minDivisible")), root.<Integer> get("building"), cb.max(root.<Integer> get("stage")), cb.min(root.<Integer> get("lcd")), cb.min(root.<Integer> get("led")), cb.min(root.<Integer> get("floor")), cb.max(root.<Integer> get("floor")), mediaJoin.get("path"), spaceTypeJoin.get("nameEn"), buildingGradeJoin.get("nameEn")); query.where(predicate); query.orderBy(cb.desc(root.get("gmtCreate").as(Date.class))); query.groupBy(root.<Integer> get("building").get("id"), root.<String> get("userByAdmin").get("id")); Predicate minExp = null; Predicate maxExp = null; Predicate minMaxResultExp = null; if (null != searchContext.getMinSize()) { minExp = cb.greaterThanOrEqualTo(cb.min(root.<BigDecimal> get("minDivisible")), searchContext.getMinSize()); // 最小面积 minMaxResultExp = minExp; } if (null != searchContext.getMaxSize()) { maxExp = cb.lessThanOrEqualTo(searchContext.isContiguous() ? maxConExp : totalConExp, searchContext.getMaxSize()); // 最大面积 minMaxResultExp = maxExp; } if (null != searchContext.getMinSize() && null != searchContext.getMaxSize()) { minMaxResultExp = cb.or(minExp, maxExp); } if (null != minMaxResultExp) { query.having(minMaxResultExp); } TypedQuery<Tuple> pagableQuery = em.createQuery(query); return pageable == null ? new PageImpl<Tuple>(pagableQuery.getResultList()) : readPage(pagableQuery, pageable); } }