springData+mongodb 条件查询+条件分页排序
2017-03-28 17:38
585 查看
背景:
最近项目使用缓存存储离线设备下发的消息. 技术支持是使用springmvc+springData,文档类型数据的存储就没使用mybatis.因为以前没使用过springData,摸索的过程还是挺复杂的,不过springData官网和github上这方面的资料还是很齐全的,把我的小小经验记载下来...
Spring data简介
spring Data 项目的目的是为了简化构建基于 Spring 框架应用的数据访问计数,包括非关系数据库、Map-Reduce框架、云数据服务等等;另外也包含对关系数据库的访问支持。不需要使用SQL语句,用关键子可以直接操作数据库(见后), 对于复杂(如多表,嵌套子查询等)的SQL语句, Spring data还提供了@Query()注解的形式在repository的方法上标注;
应用:
项目pom文件
使用springData+mongodb, 开始使用couchbase存储,但在使用过程中发现存储用时过长,一条很简单的消息缓存达到秒级别<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-mongodb</artifactId> <version>1.7.2.RELEASE</version> </dependency>
配置文件: spring-dao.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mongo="http://www.springframework.org/schema/data/mongo" xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <!-- 引入参数配置文件 --> <context:property-placeholder location="classpath:system.properties" /> <!-- 配置数据源 Mongo DB --> <!-- Default bean name is 'mongo' --> <!-- <mongo:mongo id="mongo" host="127.0.0.1" port="27017" /> --> <mongo:mongo id="mongo" host="172.23.xx.xx" port="27017" /> <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> <constructor-arg ref="mongo" /> <constructor-arg name="databaseName" value="msgCache" /> </bean> <mongo:repositories base-package="com.xxxx.repository"></mongo:repositories> </beans>
简单介绍@Document
使用Spring data时,实体分为两种: entity和对应mongodb中的映射entity; entity是普通的javaBean,不需要加注解,提供给调用缓存接口的消费者使用; mapper entity中必须加上@Document ,主键标识注解@Id, 其他属性可以不加注解,不添加时默认为@Field.实体类加上@Document作为文档存储在mongodb中(实例见后)
还要注意的是:要自定义一个类型转换类(ClassUtil),将entity和映射entity进行转换, 查询时从mongodb中取出来的是文档类映射entity, 接口返回给消费者时应该是普通的entity,所以要进行转换;
Repository接口
Repositry接口**基础的 Repository提供了最基本的数据访问功能,其几个子接口则扩展了一些功能。它们的继承关系如下:
Repository:仅仅是一个标识,表明任何继承它的均为仓库接口类
CrudRepository:继承Repository,实现了一组CRUD相关的方法
PagingAndSortingRepository:继承CrudRepository,实现了一组分页排序相关的方法
JpaRepository:继承PagingAndSortingRepository,实现一组JPA规范相关的方法
自定义的XxxxRepository需要继承 JpaRepository,这样的XxxxRepository接口就具备了通用的数据访问控制层的能力。
JpaSpecificationExecutor:不属于Repository体系,实现一组JPACriteria查询相关的方法
这里只简单介绍下CrudRepository和[b]PagingAndSortingRepository[/b]这两个接口
自定义接口后可以直接使用接口中封装的方法,比如简单的CRUD, 源码如下:
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> { <S extends T> S save(S entity); //保存实体 <S extends T> Iterable<S> save(Iterable<S> entities); //保存多个实体 T findOne(ID id); //安装主键_id查找实体 boolean exists(ID id); Iterable<T> findAll(); Iterable<T> findAll(Iterable<ID> ids); long count(); void delete(ID id); void delete(T entity); void delete(Iterable<? extends T> entities); void deleteAll();
}
PagingAndSortingRepository继承了CrudRepository,添加了分页和排序的两个查询
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> { Iterable<T> findAll(Sort sort); //排序查询 Page<T> findAll(Pageable pageable); //分页查询(Pageable中包含了排序) }
使用接口XXXXRepository继承CrudRepository接口,PagingAndSortingRepository接口;
例如:
public interface MessageRepository extends PagingAndSortingRepository<Message, String> {}
这个XXXXRepository就相当于 DAO,只是这个接口不用你去实现.Spring Data JPA为我们封装了,只要按照Spring Data JPA的规范来定义接口的方法名,就会自动为我们生成一个默认的代理实现类。
接口中定义方法的关键字
在实际应用中,以上spring data JPA提供的方法并不能满足我们的需求, 譬如 条件查询did下所有结果集合,结果你发现官方提供的方法都是使用主键_id查询的, 这时就要使用关键字进行自定义方法了,直接放上Spring Data文档中对方法名关键字的介绍, 这些关键字和SQL语句基本相同,简单查看就能懂spring-data简单的了解知识就介绍到这, 详细文档见spring-data
应用
记载消息的处理过程,设计了消息文档,包含id,消息体,设备did,最新状态,状态集合(记录每次状态更新的时间和改变后的状态),最后一次更新状态的时间@Document public class Message { @Id private String id; /**消息ID*/ private String msgId; /**消息内容*/ private String content; /**消息状态(最后修改后的状态)*/ private int status; /**设备ID*/ private String did; /**消息创建时间*/ private Date createTime; /**消息最后更新时间*/ private Date updateTime; /**消息的历史状态列表*/ private List<MessageStatus> messageStatus;
public class MessageStatus { /**消息状态*/ private int status; /**消息更新时间*/ private Date time;Message文档结构如下图:
重要部分:自定义的Repository
public interface MessageRepository extends PagingAndSortingRepository<Message, String> { /** * 按照设备ID查询分页缓存消息 * @param did 设备ID * @param pageable Pageable分页对象 * @return 返回缓存消息列表 */ Page<Message> findByDid(String did, Pageable pageable); /** * 根据设备ID和消息状态获取缓存消息列表 * @param did 设备ID * @param status 消息状态码 * @return 返回指定消息码和设备ID的缓存消息列表 */ List<Message> findByDidAndStatus(String did, int status); /** * 根据设备ID按照时间升序获取消息列表 * @param did 设备ID * @return 返回指定 a1b1 设备ID的离线消息列表 */ List<Message> findByDidOrderByUpdateTimeAsc(String did); /** * 根据设备ID, 消息状态, 按照时间升序获取缓存消息列表 * @param did 设备ID * @param status 消息状态码 * @return 返回指定设备和消息状态的缓存消息列表 */ List<Message> findByDidAndStatusOrderByUpdateTimeAsc(String did, int status); /** * 根据设备ID, 消息状态, 获取缓存消息的数目 * @param did 设备ID * @param status 消息状态码 * @return 返回指定设备和消息状态的缓存消息数目 */ int countByDidAndStatus(String did, int status); /** * 按照设备ID, 消息状态, 分页查询缓存消息 * @param did 设备ID * @param status 消息状态码 * @param pageable Pageable分页对象 * @return 返回缓存消息列表 */ Page<Message> findByDidAndStatus(String did, int status, Pageable pageable); /** * 按照设备ID, 多个消息状态, 分页查询缓存消息 * @param did 设备ID * @param status 消息状态码 * @param pageable Pageable分页对象 * @return 返回缓存消息列表 */ Page<Message> findByDidAndStatusIn(String did, Collection<Integer> status, Pageable pageable); /** * 根据设备ID, 消息状态集, 获取缓存消息的数目 * @param did 设备ID * @param status 消息状态码 * @return 返回指定设备和消息状态的缓存消息数目 */ int countByDidAndStatusIn(String did, Collection<Integer> status); }
上面自定义的Repository都是使用关键字进行定义方法的,包含了稍微复杂点的排序分页
serviceImpl具体实现见下
/** * 分页获取设备最新的离线消息列表 * * @param count * 返回个数 * @param pageNo * 页码, 用于分页查询 * @param did * 设备ID * @param status * 状态拼接字符串,状态之间逗号隔开 * * @return 分页获取设备最新的离线消息列表 */ public List<Message> getMsgListByPage(int count, int pageNo, String did, String status) { if (logger.isInfoEnabled()) { logger.info( "调用接口:[getMsgListByPage],参数:[count = {}, pageNo = {}, did = {}, status = {}]", count, pageNo, did, status); } if (StringUtils.isBlank(status)) { return getMsgListByPage(count, pageNo, did); } String[] splittedStatus = status.split(","); List<Integer> statusToQuery = new ArrayList<Integer>(splittedStatus.length); for (String s : splittedStatus) { statusToQuery.add(Integer.valueOf(s)); } Sort sort = new Sort(Direction.ASC, "updateTime"); Pageable pageable = new PageRequest(pageNo - 1, count, sort); Page<Message> pageMessages = messageRepository.findByDidAndStatusIn(did, statusToQuery, pageable); List<Message> messageMapperList = pageMessages.getContent(); if (CollectionUtils.isNotEmpty(messageMapperList)) { int size = messageMapperList.size(); List<Message> result = new ArrayList<Message>(size); for (Message messageMapper : messageMapperList) { result.add(ClassUtils.swapBack(messageMapper)); } return result; } return Collections.<Message> emptyList(); }
ClassUtils mongodb中取出对象和需求中返回的对象有几个属性不对应,所有我直接做了个工具类转换封装, 这里命名重复了不是很科学,最好不要重复
/**
* 实现类中entity和api中entity类转化
* @param messageMapper 实现类entity
* @return 返回api中entity
*/
public static Message swapBack(messageMapper) {
Message message = new Message();
message.setMsgId(messageMapper.getMsgId());
message.setDid(messageMapper.getDid());
message.setContent(messageMapper.getContent());
message.setStatus(messageMapper.getStatus());
message.setCreateTime(messageMapper.getCreateTime());
message.setUpdateTime(messageMapper.getUpdateTime());
return message;
}
进一步复杂的查询,多表,嵌套等,就要在自定义的Repository的自定义方法上添加注解@Query("") 进行SQL语句查询操作了, 具体如何操作我没有试过..
相关文章推荐
- spring-data-mongodb的同字段索引多条件查询
- Spring data mongodb ObjectId ,根据id日期条件查询,省略@CreatedDate注解
- spring-data-jpa 多条件查询 学习记录
- Spring Data JPA 多条件查询
- 用spring data 查询 MongoDb 空间坐标 范围距离内所有的点
- Spring MVC和Spring Data JPA之按条件查询和分页(kkpaper分页组件)
- Spring Data MongoDB 三:基本文档查询(Query、BasicQuery)(一)
- Spring Data MongoDB 模糊查询
- 【java】spring-data-jpa 集成hibernate实现多条件分页查询
- spring data jpa 条件分页查询
- spring data mongodb 按小时分组查询
- 【spring data jpa】带有条件的查询后分页和不带条件查询后分页实现
- Spring Data MongoDB 三:基本文档查询(Query、BasicQuery)(一)
- Spring Data MongoDB 基础查询
- Spring Data 与MongoDB 集成四:操作篇(查询)
- spring-data-mongodb查询结果返回指定字段
- spring data elasticsearch的一个复杂查询条件
- Spring Data MongoDB 模糊查询
- Spring Data JPA 复杂/多条件组合查询
- 在SPRING DATA MONGODB中使用聚合统计查询