Fork me on GitHub

Spring Data Jpa使用Filter过滤数据

场景

需要根据当前用户的权限过滤需要显示的数据。
比如有一张旅馆表,主键为hotel_code,该值的构成为6位行政区划+4位流水号。杭州用户只能看到3301开头的旅馆信息,而3302开头的只能看到宁波的旅馆信息。
如:

hotel_code hotel_name
3301888999 杭州测试旅馆
3302888999 宁波测试旅馆
3303888999 温州测试旅馆
3304888999 嘉兴测试旅馆

用户表中有个filter_code字段,过滤匹配start_with的数据。如fitler_code=33,则匹配显示整个浙江省的数据;filter_code=3301则显示杭州市的数据。

框架

  • springboot

  • spring data jpa

JPA内嵌Hibernate的Filter功能简介

JPA自身并没有提供Filter功能,而是由hibernate实现的,
@Filter是Entity上的注解,在添加该注解之后,hibernate会在相应查询语句中添加where子句以达到过滤的目的。
还是直接看代码吧。

代码片段实现

Hotel实体类:

@Entity
@Data
@FilterDef(name = "filterByHotelCode", parameters = {
        @ParamDef(name = "filterCode", type = "string")
})
@Filters({
        @Filter(name = "filterByHotelCode", condition = "hotel_code like :filterCode")
})
public class Hotel {
    @Id
    @Column(name = "hotel_code")
    private String hotelCode;

    private String hotelName;
}

DAO:

public interface HotelRepository extends JpaRepository<Hotel, String> {}

HotelService:

    // @EnableFilter为自定义注解以实现切面开启Filter
    // @Transactional 是使用Filter功能的前提、必要条件
    @EnableFilter
    @Transactional
    public PageModel<Hotel> queryByPage(String queryKey, Pageable pageable) {
        Page<Hotel> hotelPage = hotelRepository.findHotelsByKey(queryKey, pageable);
        return PageModel.build(hotelPage, pageable.getOffset());
    }

自定义注解:

public @interface EnableFilter {
}

切面AOP:

@Component
@Aspect
public class DataFilterAdvice {

    @PersistenceContext
    private EntityManager entityManager;

    @Around("@annotation(org.vigack.annotation.EnableFilter)")
    public Object doProcess(ProceedingJoinPoint joinPoint) throws Throwable {
        try{
            ManagerDTO manager =
                    (ManagerDTO) SecurityUtils.getSubject().getSession(false).getAttribute("MANAGER");
            Filter filter = entityManager.unwrap(Session.class).enableFilter("filterByHotelCode");
            filter.setParameter("filterCode", manager.getFilterCode() + "%");
            return joinPoint.proceed();
        }catch (Throwable ex){
            ex.printStackTrace();
            throw ex;
        }finally {
            entityManager.unwrap(Session.class).disableFilter("filterByHotelCode");
        }
    }
}

注意事项

  • Filter的condition比较简单,对于动态的条件支持度不够,比如用户的filter_code如果是一个list,那么实现起来就麻烦了。

  • 这个过滤对于懒加载不起作用

  • 如果通过主键直接查询,那么过滤器将不起作用

posted @ 2017-11-21 13:59  alchimistin  阅读(14345)  评论(3编辑  收藏  举报