Spring Boot学习笔记(三)——Spring-data-jpa扩展封装

上篇文章我们分享了下Spring-data-jpa的基本使用方法,但在实际使用中可能会有更复杂的用法,现在我们来看看他的更高级玩法,并做了完美的封装,使用起来更加智能简单。Spring-data-jpa为我们提供了JpaSpecificationExecutor接口,只要简单实现toPredicate方法就可以实现复杂的查询,下面就来看看具体如何使用。

首先我们来看看JpaSpecificationExecutor这个类

public interface JpaSpecificationExecutor<T> {
    T findOne(Specification<T> var1);

    List<T> findAll(Specification<T> var1);

    Page<T> findAll(Specification<T> var1, Pageable var2);

    List<T> findAll(Specification<T> var1, Sort var2);

    long count(Specification<T> var1);
}

可以看出他是个接口,里面提供了基本的使用方法,其中重要的一个参数是Specification<T>我们再来看看这个这个参数

public interface Specification<T> {
    Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3);
}

发现他也是一个接口,并且只有一个方法,因此这个方法就是我们查询的关键所在,实际他就是向我们提供的构建查询条件的关键接口,我们只要实现他的方法按照JPA 2.0 criteria api写好查询条件就可以了,接下来看看具体如何来怎么使用

由上一篇我么知道使用Spring-data-jpa我们需要继承JpaRepository接口,为了做复杂查询还需继承JpaSpecificationExecutor接口,因此我们可以建一个BaseRepository类来同时继承这两个类

/**
 * 基类的数据访问接口(继承了CrudRepository,PagingAndSortingRepository,
 * JpaSpecificationExecutor的特性)
 *
 * Created by mj on 2017/12/17.
 */
@NoRepositoryBean
public abstract interface BaseRepository<T, ID extends Serializable> extends JpaRepository<T,ID>, JpaSpecificationExecutor<T> {

    /**
     *
     * 使用QBL进行查询列表
     *
     * @author mj 2016年10月26日
     * @param query
     * @return
     */
    public abstract List<T> findAll(BaseQuery query);

    /**
     *
     * 封装分页查询
     *
     * @author mj 2016年10月26日
     * @param query
     * @param pageable
     * @return
     */
    public abstract Page<T> findAll(BaseQuery query, Pageable pageable);

    /**
     *
     * 封装排序查询
     *
     * @author mj 2016年10月26日
     * @param query
     * @param sort
     * @return
     */
    public abstract List<T> findAll(BaseQuery query, Sort sort);

    /**
     *
     * 使用QBL定位记录
     *
     * @author mj 2016年10月26日
     * @param query
     * @return
     */
    public abstract T findOne(BaseQuery query);

    /**
     *
     * 更新方法
     *
     * @author mj 2016年10月26日
     * @param t
     * @param updateFileds
     * @param where
     * @return
     */
    public abstract int update(T t, BaseQuery where, String... updateFileds);

    /**
     *
     * 根据唯一主键更新方法
     *
     * @author mj 2016年10月26日
     * @param t
     * @param id
     * @param updateFileds
     * @return
     */
    public abstract int updateById(T t, ID id, String... updateFileds);
}

其中里面的一些方法暂时不用关心是对jpa的一些功能扩展加强后面会讲到,然后我们来构建查询条件,通过以上讲述我们只需创建 一个类来实现Specification<T>接口即可

/**
 * 自定义Query语言转Specification
 *
 * @version 
 * @author mj 2016年10月26日 下午3:46:50
 * @param
 * 
 */
public class QueryToSpecification implements Specification {
	private  BaseQuery query;
	
	
	public QueryToSpecification(BaseQuery query) {
		super();
		this.query = query;
	}

	/**
	 * 【请在此输入描述文字】
	 * 
	 * (non-Javadoc)
	 * @see Specification#toPredicate(Root, CriteriaQuery, CriteriaBuilder)
	 */
	@Override
	public Predicate toPredicate(Root root, CriteriaQuery cquery, CriteriaBuilder cb) {
		return BaseQueryPredicateBuilder.getPredicate(root, cb, cquery,this.query);
	}

}

由此可看出我们实现了Specification<T>接口的toPredicate方法,里面的参数Root就是我们的实体类,CriteriaQuery为基本查询,CriteriaBuilder为查询条件构建,其中BaseQueryPredicateBuilder.getPredicate(root, cb, cquery,this.query);为具体的构建条件实现,我们继续来构建这个方法

/**
 * query转换builder类
 *
 * @version
 * @author mj 2016年10月26日 下午3:54:41
 * 
 */
public class BaseQueryPredicateBuilder {
	private static Logger log = LogManager.getLogger(BaseQueryPredicateBuilder.class);

	public static <T> Predicate getPredicate2(Root<T> root, CriteriaBuilder cb, BaseQuery query) {
		return getPredicate(root, cb, null, query);
	}

	public static <T> Predicate getPredicate(Root<T> root, CriteriaBuilder cb, CriteriaQuery<T> cquery,
			BaseQuery query) {

		List<Predicate> predicatesAnd = new ArrayList<Predicate>();
		try {
			Class<?> entityClass = queryEntity(query);
			if (entityClass == null) {
				// 是否返回NULL,待研究
				return null;
			}
			BeanInfo beanInfo = Introspector.getBeanInfo(query.getClass());
			PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor pd : pds) {
				Method readMethod = pd.getReadMethod();
				if ((pd.getName().indexOf("Page") == 0) || (pd.getName().indexOf("Sort") == 0)) {
					continue;
				}
				if (!pd.getName().equals("class")) {
					Object obj = readMethod.invoke(query);
					if (obj != null) {
						QBindAttrField fieldProp = (QBindAttrField) getBindFieldName(query, pd.getName());
						String bindAttrName = fieldProp.fieldName();
						if (bindAttrName == null) {
							// 查询字段名称默认等于属性名称
							bindAttrName = pd.getName();
						}
						Path<?> from = root;
						Expression expression = from.get(bindAttrName);
						switch (fieldProp.where()) {
						case equal:
							predicatesAnd.add(cb.equal(expression, obj));
							break;
						case greaterThanOrEqualTo:
							predicatesAnd.add(cb.greaterThanOrEqualTo(expression, (Comparable) obj));
							break;
						case lessThanOrEqualTo:
							predicatesAnd.add(cb.lessThanOrEqualTo(expression, (Comparable) obj));
							break;
						case like:
							predicatesAnd.add(cb.like(expression, "%" + (Comparable) obj + "%"));
							break;
						case greaterThan:
							predicatesAnd.add(cb.greaterThan(expression, (Comparable) obj));
							break;
						case lessThan:
							predicatesAnd.add(cb.lessThan(expression, (Comparable) obj));
							break;
						case notEqual:
							predicatesAnd.add(cb.notEqual(expression, (Comparable) obj));
							break;
						case in:
							if (pd.getPropertyType().getName().indexOf("List") > 0) {
								List<?> value = (List<?>) obj;
								if (value.size() == 0) {
									// 防止生成LIST时,没有传入值,而查询条件会做全查处理,此处做特殊处理返回空条件
									((List<?>) obj).add(null);
								}
								if (value.size() > 20) {
									Set<Object> set = new HashSet<Object>(value.size());
									// 如果in超过20个要去重处理
									set.addAll(value);
									value = new ArrayList<Object>(set);
								}
								predicatesAnd.add(expression.in(value));
							} else {
								List vList = new ArrayList<Object>();
								vList.add(obj);
								predicatesAnd.add(expression.in(vList));
							}
							// 特殊处理
							break;
						default:// 默认等于equal
							break;
						}
					}
				}

			}
		} catch (Exception e) {
			log.error(e);
		}

		// 组合条件
		if (predicatesAnd.isEmpty()) {
			return cb.isTrue(cb.literal(true));
		}

		if (predicatesAnd.size() == 1) {
			return predicatesAnd.iterator().next();
		}
		return cb.and(predicatesAnd.toArray(new Predicate[predicatesAnd.size()]));
	}

	/**
	 * 
	 * 获取查询实体类名称
	 * 
	 * @author liuyi 2016年4月16日
	 * @param query
	 * @return
	 */
	public static Class<?> queryEntity(BaseQuery query) {
		Annotation anno = query.getClass().getAnnotation(QBindEntity.class);
		if (anno != null)
			return ((QBindEntity) anno).entityClass();
		return null;
	}

	/**
	 * 
	 * 获取绑定字段属性值
	 * 
	 * @author liuyi 2016年4月16日
	 * @param PropertyName
	 * @return
	 */
	public static Annotation getBindFieldName(BaseQuery query, String PropertyName) {
		try {
			Field field = query.getClass().getDeclaredField(PropertyName);
			Annotation anno = field.getAnnotation(QBindAttrField.class);
			if (anno != null) {
				return ((QBindAttrField) anno);
			}
		} catch (SecurityException e) {
			log.error("[BaseQueryPredicateBuilder.getBindAttrName SecurityException:]" + e.getMessage());
		} catch (NoSuchFieldException e) {
			log.error("[BaseQueryPredicateBuilder.getBindAttrName NoSuchFieldException:]" + e.getMessage());
		}
		return null;
	}

}

这就是我们封装的查询条件逻辑,代码这里就不解释了大家可以自己看看很简单,但需要注意的是这里需要两个自定义注解QBindAttrField,QBindEntity和Where条件枚举类关键字,我们也创建一下,他们的功能就是用来映射后面查询需要的实体类和属性

/**
 * 查询绑定属性
 *
 * @version 
 * @author mj  2016年10月28日 下午6:20:57
 * 
 */
@Target({java.lang.annotation.ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface QBindAttrField {
	public abstract String fieldName();
	public abstract Where where();
}
/**
 * 查询绑定实体类
 *
 * @version 
 * @author mj  2016年10月26日 下午6:18:03
 * 
 */
@Target({java.lang.annotation.ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface QBindEntity {
	public abstract Class<?> entityClass();
}
/**
 * 目前系统支持的where查询条件关键字
 *
 * @version 
 * @author mj  2016年10月26日 下午6:40:05
 * 
 */
public enum Where {
	in,
	like,
	equal,
	notEqual,
	lessThan,
	lessThanOrEqualTo,
	greaterThan,
	greaterThanOrEqualTo
}

接下来我们来实现上面提到的对jpa功能的扩展加强实现里面的方法,创建BaseSimpleJpaRepositoryEx类实现SimpleJpaRepository和BaseRepository接口

/**
 * 扩展对JAP功能加强
 *
 * @version
 * @author liuyi  2016年4月16日 下午2:40:16
 *
 */
@Transactional(readOnly=false,propagation=Propagation.SUPPORTS)
@NoRepositoryBean
public class BaseSimpleJpaRepositoryEx<T, ID extends Serializable>
        extends SimpleJpaRepository<T, ID> implements BaseRepository<T, ID> {

    private EntityManager baseEm;
    private JpaEntityInformation<T, ?> baseEmInfo;

    /**
     * BaseSimpleJpaRepositoryEx 构造器
     * @param domainClass
     * @param entityManager
     */
    public BaseSimpleJpaRepositoryEx(Class<T> domainClass, EntityManager entityManager) {
        super(domainClass, entityManager);
        baseEm = entityManager;
//        baseEmInfo = entityInformation;
    }

    /**
     * 自定义查询条件转换实现
     *
     * (non-Javadoc)
     */
    private Specification<T> getConditonByQuery(BaseQuery query) {
        return new QueryToSpecification(query);
    }

    /**
     * 封装自定义组合查询列表方法
     *
     * (non-Javadoc)
     */
    @SuppressWarnings("unchecked")
    @Override
    public List<T> findAll(BaseQuery query) {
        if(query.getSort()!=null){
            return findAll(getConditonByQuery(query), query.getSort());
        }
        else if(query.getPage()!=null){
            return (List<T>) findAll(getConditonByQuery(query),query.getPage());

        }else{
            return (List<T>) findAll(getConditonByQuery(query));
        }

    }

    /**
     *
     * 自定义组合查询分页方法
     *
     * @author liuyi 2016年4月18日
     * @param query
     * @param pageable
     * @return
     */
    @Override
    public Page<T> findAll(BaseQuery query,Pageable pageable) {
        return findAll(getConditonByQuery(query), pageable);
    }

    /**
     * 查询条件
     *
     * (non-Javadoc)
     */
    @Override
    public T findOne(BaseQuery query) {
        return findOne(getConditonByQuery(query));
    }


    @Override
    public List<T> findAll() {
        return super.findAll();
    }

    @Override
    protected CrudMethodMetadata getRepositoryMethodMetadata() {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.getRepositoryMethodMetadata();
    }

    @Override
    protected Class<T> getDomainClass() {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.getDomainClass();
    }

    @Override
    public T findOne(ID id) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.findOne(id);
    }

    @Override
    protected Map<String, Object> getQueryHints() {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.getQueryHints();
    }

    @Override
    public T getOne(ID id) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.getOne(id);
    }

    @Override
    public boolean exists(ID id) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.exists(id);
    }

    @Override
    public List<T> findAll(Iterable<ID> ids) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.findAll(ids);
    }

    @Override
    public List<T> findAll(Sort sort) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.findAll(sort);
    }

    @Override
    public Page<T> findAll(Pageable pageable) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.findAll(pageable);
    }

    @Override
    public T findOne(Specification<T> spec) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.findOne(spec);
    }

    @Override
    public List<T> findAll(Specification<T> spec) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.findAll(spec);
    }

    @Override
    public Page<T> findAll(Specification<T> spec, Pageable pageable) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.findAll(spec, pageable);
    }

    @Override
    public List<T> findAll(Specification<T> spec, Sort sort) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.findAll(spec, sort);
    }

    @Override
    public <S extends T> S findOne(Example<S> example) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.findOne(example);
    }

    @Override
    public <S extends T> long count(Example<S> example) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.count(example);
    }

    @Override
    public <S extends T> boolean exists(Example<S> example) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.exists(example);
    }

    @Override
    public <S extends T> List<S> findAll(Example<S> example) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.findAll(example);
    }

    @Override
    public <S extends T> List<S> findAll(Example<S> example, Sort sort) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.findAll(example, sort);
    }

    @Override
    public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.findAll(example, pageable);
    }

    @Override
    public long count() {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.count();
    }

    @Override
    public long count(Specification<T> spec) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.count(spec);
    }

    @Override
    protected Page<T> readPage(TypedQuery<T> query, Pageable pageable, Specification<T> spec) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.readPage(query, pageable, spec);
    }

    @Override
    protected <S extends T> Page<S> readPage(TypedQuery<S> query, Class<S> domainClass, Pageable pageable,
                                             Specification<S> spec) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.readPage(query, domainClass, pageable, spec);
    }

    @Override
    protected TypedQuery<T> getQuery(Specification<T> spec, Pageable pageable) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.getQuery(spec, pageable);
    }

    @Override
    protected <S extends T> TypedQuery<S> getQuery(Specification<S> spec, Class<S> domainClass, Pageable pageable) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.getQuery(spec, domainClass, pageable);
    }

    @Override
    protected TypedQuery<T> getQuery(Specification<T> spec, Sort sort) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.getQuery(spec, sort);
    }

    @Override
    protected <S extends T> TypedQuery<S> getQuery(Specification<S> spec, Class<S> domainClass, Sort sort) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.getQuery(spec, domainClass, sort);
    }

    @Override
    protected TypedQuery<Long> getCountQuery(Specification<T> spec) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.getCountQuery(spec);
    }

    @Override
    protected <S extends T> TypedQuery<Long> getCountQuery(Specification<S> spec, Class<S> domainClass) {
        // TODO 这是系统自动生成描述,请在此补完后续代码
        return super.getCountQuery(spec, domainClass);
    }

    /**
     * 封装自定义组合查询排序列表方法
     *
     * (non-Javadoc)
     */
    @Override
    public List<T> findAll(BaseQuery query, Sort sort) {
        return findAll(getConditonByQuery(query), sort);
    }

    @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
    @Override
    public <S extends T> List<S> save(Iterable<S> arg0) {
        return super.save(arg0);
    }

    @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
    @Override
    public <S extends T> S save(S entity) {
        return super.save(entity);
    }

    @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
    @Override
    public <S extends T> S saveAndFlush(S entity) {
        return super.saveAndFlush(entity);
    }

    @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
    @Override
    public void setRepositoryMethodMetadata(CrudMethodMetadata crudMethodMetadata) {
        super.setRepositoryMethodMetadata(crudMethodMetadata);
    }

    @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
    @Override
    public void delete(ID id) {
        super.delete(id);
    }

    @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
    @Override
    public void delete(T entity) {
        super.delete(entity);
    }

    @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
    @Override
    public void delete(Iterable<? extends T> entities) {
        super.delete(entities);
    }

    @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
    @Override
    public void deleteInBatch(Iterable<T> entities) {
        super.deleteInBatch(entities);
    }

    @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
    @Override
    public void deleteAll() {
        super.deleteAll();
    }

    @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
    @Override
    public void deleteAllInBatch() {
        super.deleteAllInBatch();
    }

    @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
    @Override
    public void flush() {
        super.flush();
    }

    /**
     *
     * 自定义更新update方法
     *
     * @author liuyi 2016年7月16日
     * @param
     * @param where
     * @return
     */
    @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
    public int  update(T t,BaseQuery where,String... updateFileds){
        CriteriaBuilder cb =baseEm.getEntityManagerFactory().getCriteriaBuilder();
        CriteriaUpdate<T> update =  (CriteriaUpdate<T>) cb.createCriteriaUpdate(t.getClass());
        Root<T> root = update.from((Class<T>) t.getClass());

        for(String fieldName:updateFileds){
            try {
                Object o = PropertyUtils.getProperty(t, fieldName);
                update.set(fieldName, o);
            } catch (Exception e) {
                GwsLogger.error("update error:"+e);
            }
        }
        update.where(BaseQueryPredicateBuilder.getPredicate2(root, cb,where));
        return baseEm.createQuery(update).executeUpdate();
    }

    /**
     *
     * 根据唯一主键更新相关数据
     *
     * @author liuyi 2016年7月16日
     * @param id
     * @param
     * @return
     */
    @Transactional(readOnly=false,propagation=Propagation.REQUIRED)
    public int updateById(T t,ID id,String... updateFileds){
        CriteriaBuilder cb =baseEm.getEntityManagerFactory().getCriteriaBuilder();
        CriteriaUpdate<T> update =  (CriteriaUpdate<T>) cb.createCriteriaUpdate(t.getClass());
        Root<T> root = update.from((Class<T>) t.getClass());
        for(String fieldName:updateFileds){
            try {
                Object o = PropertyUtils.getProperty(t, fieldName);
                update.set(fieldName, o);
            } catch (Exception e) {
                GwsLogger.error("update error:"+e);
            }
        }
        //定位主键信息
        Iterable<String> idAttributeNames = baseEmInfo.getIdAttributeNames();

        for(String key:idAttributeNames){
            if(key!=null&&key!=""){
                update.where(cb.equal(root.get(key), id));
                break;
            }
        }
        return baseEm.createQuery(update).executeUpdate();

    }


}

通过getConditonByQuery(BaseQuery query)方法可以看到获取了我们实现的构造条件实例,从而作为复杂条件查询的Specification<T>参数设置达到查询效果,至此我们已经封装好了复杂查询,但此时还不能用,这里有个坑,虽然我们实现了jpa功能加强扩展但并没有指定扩展的具体实现类,所以jpa找不到这个实现bean,因此我们还需要最后一步操作就是指定我们实现的这个扩展类BaseSimpleJpaRepositoryEx,我们创建一个jpa工厂类BaseRepositoryFactoryBean来确定具体的实现类

/**
 * 自定义jpa工厂类 可确定具体的实现类
 * Created by mj on 2017/12/20.
 */
public class BaseRepositoryFactoryBean <R extends JpaRepository<T, I>, T,
        I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> {

    public BaseRepositoryFactoryBean(Class<? extends R> repositoryInterface) {
        super(repositoryInterface);
    }

    @Override
    protected RepositoryFactorySupport createRepositoryFactory(EntityManager em) {
        return new BaseRepositoryFactory(em);
    }

    //创建一个内部类,该类不用在外部访问
    private static class BaseRepositoryFactory<T, I extends Serializable>
            extends JpaRepositoryFactory {

        private final EntityManager em;

        public BaseRepositoryFactory(EntityManager em) {
            super(em);
            this.em = em;
        }

        //设置具体的实现类是BaseRepositoryImpl
        @Override
        protected Object getTargetRepository(RepositoryInformation information) {
            return new BaseSimpleJpaRepositoryEx<T, I>((Class<T>) information.getDomainType(), em);
        }

        //设置具体的实现类的class
        @Override
        protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
            return BaseSimpleJpaRepositoryEx.class;
        }
    }
}

通过代码可以很清楚看到是如何指定的,最后在应用程序的启动类上加上@EnableJpaRepositories指定自动启动扫描这个工厂即可

@EnableJpaRepositories(basePackages = {"com.xiaoma"},
		repositoryFactoryBeanClass = BaseRepositoryFactoryBean.class//指定自己的工厂类
)
@SpringBootApplication
public class BlogApplication {

	public static void main(String[] args) {
		SpringApplication.run(BlogApplication.class, args);
		System.out.println("========================================xiaomage blog server is started!");
		GwsLogger.info("xiaomage blog server is started!");
	}
}

至此我们的封装就大功告成,下面就让我们来感受一下效果吧!

首先创建一个实体类Tag

/**
 * Tag 实体类
 * Created by mj on 2017/12/17.
 */
@Entity
@Table(name = "tag")
public class Tag implements Serializable {

    @Id
    @GeneratedValue
    @Column(name = "tag_id")
    private Integer tagId;

    @Column(name = "tag_name")
    private String tagName;

    @Column(name = "create_time")
    private Integer createTime;

    @Column(name = "update_time")
    private Integer updateTime;

    此处省略get()/set()方法
}

创建TagRepository接口继承BaseRepository

/**
 * Created by mj on 2017/12/17.
 */
public interface TagRepository extends BaseRepository<Tag,Integer> {
}

创建TagQueryFilter查询条件过滤类

/**
 * Created by mj on 2017/12/24.
 */
@QBindEntity(entityClass = Tag.class)
public class TagQueryFilter extends BaseQuery {

    @QBindAttrField(fieldName = "tagId", where = Where.equal)
    private Integer tagId;

    @QBindAttrField(fieldName = "tagName", where = Where.equal)
    private String tagName;

    public Integer getTagId() {
        return tagId;
    }

    public void setTagId(Integer tagId) {
        this.tagId = tagId;
    }

    public String getTagName() {
        return tagName;
    }

    public void setTagName(String tagName) {
        this.tagName = tagName;
    }
}

创建TagServiceImpl业务实现类实现findTags()方法,TagService此处省略

/**
 * Created by mj on 2017/12/24.
 */
@Service
public class TagServiceImpl implements TagService {

    @Autowired
    private TagRepository tagRepository;

    /**
     * 分页查询文章所有标签 返回标签list
     * @param tag
     * @return
     */
    @Override
    public Page<Tag> findTags(Tag tag, Integer pageNo, Integer pageSize, String sortField) {

        //组合查询语句
        TagQueryFilter query = new TagQueryFilter();
        if (null != tag){
            if (StringUtils.isNotBlank(tag.getTagName())){
                query.setTagName(tag.getTagName());
            }
        }
        Sort sort = new Sort(Sort.Direction.DESC,sortField);
        Pageable pageable = new PageRequest(pageNo,pageSize,sort);
        Page<Tag> pageList = tagRepository.findAll(query,pageable);
        
        return pageList;
    }
}

这样我们就实现了通过tagName作为条件的分页排序查询,如需其他条件只需set其他的属性即可,怎么样很简单吧

创建TagController

/**
 * Created by mj on 2017/12/24.
 */
@Controller
@RequestMapping(value = "admin")
public class TagController {
    @Autowired
    private TagService tagService;

    /**
     * 分页查询文章所有标签
     * @param tag
     * @return
     */
    @RequestMapping(value = "/findTags")
    @ResponseBody
    public RetResult findTags(Tag tag, Integer pageNo, Integer pageSize) {

        String code = CommConstant.GWSCOD0000;
        String message = CommConstant.GWSMSG0000;
        GwsLogger.info("博客文章标签开始:code={},message={},tag={}",code,message,tag);

        Page<Tag> tags = null;
        try {
            if (null == pageNo){
                pageNo = 0;
            }else {
                pageNo = pageNo-1;
            }
            tags = tagService.findTags(tag,pageNo,pageSize,CommConstant.SORT_FIELD_UTIME);
        }catch (Exception e){
            code = CommConstant.GWSCOD0001;
            message = CommConstant.GWSMSG0001;
            GwsLogger.error("博客文章标签异常:code={},message={},e={}",code,message,e);
        }
        GwsLogger.info("博客文章标签结束,code={},message={},tags={}",code,message,tags);
        return RetResult.setRetDate(code,message,tags);
    }
}

然后通过请求测试http://127.0.0.1:8081/admin/findTags?tagName=SpringBoot&pageNo=1&pageSize=3即可看到结果

{
    "code": "000",
    "data": {
        "content": [
            {
                "createTime": 1513934861,
                "createTimeStr": "2017-12-22",
                "tagId": 2,
                "tagName": "SpringBoot",
                "updateTime": 1513934861,
                "updateTimeStr": "2017-12-22"
            }
        ],
        "first": true,
        "last": true,
        "number": 0,
        "numberOfElements": 1,
        "size": 3,
        "sort": [
            {
                "ascending": false,
                "descending": true,
                "direction": "DESC",
                "ignoreCase": false,
                "nullHandling": "NATIVE",
                "property": "updateTime"
            }
        ],
        "totalElements": 1,
        "totalPages": 1
    },
    "extraData": null,
    "message": "success"
}
好了至此我们已经使用Spring-data-jpa进行简单操作以及复杂操作的封装,如需其他操作方式可以在这基础上对其构建条件进行修改,上面实例是我个人搭建博客的代码,基本可以满足大部分需求,可以直接拿来使用,相信大家也会有更好的玩法可以一起分享交流。


猜你喜欢

转载自blog.csdn.net/mjwwjcoder/article/details/80949863