和关系型数据库类似,在使用MongoDB的时候最主要还是CRUD,而Spring-data-mongodb封装了MongoTemplate类,可以方便的进行相应的操作。
首先,配置spring
<?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:mongo="http://www.springframework.org/schema/data/mongo" xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.1.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <!-- Default bean name is 'mongo' --> <mongo:mongo host="127.0.0.1" port="27017" /> <mongo:db-factory dbname="database" mongo-ref="mongo" /> <!-- 扫描注解Bean --> <context:component-scan base-package="com.study.mongodb.**.*"> <context:exclude-filter type="annotation" <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> <constructor-arg name="mongoDbFactory" ref="mongoDbFactory" /> </bean> </beans>
在关系型数据应用程序中,一般都会用到ORM框架,而Spring-data-mongodb本身就已经实现对象到数据的映射。而封装的Query和Update类用来做查询和更新已经非常方便了。但是我们还是需要把实体Bean转换成Query实例。本着将懒惰进行到底的原则,这点也最好不要。
对于查询,一般根据实体bean的某些属性进行查询,最主要的查询有等于,like和in类型。创建一个注解QueryField,代表要查询的字段
/** * <p> * 用于实体Bean的属性上的注解,注解有两个属性可以设置,type表示查询类似,默认为equals<br/> * attribute表示要查询的属性,默认为空串,在使用时如果为空串,则默认为实体Bean字段的名称 * </p> * * @author: <a href="mailto:chuanli@sohu-inc.com">chuanli</a> */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface QueryField { QueryType type() default QueryType.EQUALS; String attribute() default ""; }
默认使用相等。
QueryType是个枚举类型,表示查询类型
/** * <p> * 查询类型的媒介类<br/> * 目前有支持三种类型:<br/> * 1. equals:相等 * 2. like:mongodb的like查询 * 3. in:用于列表的in类型查询 * </p> * * @author: <a href="mailto:chuanli@sohu-inc.com">chuanli</a> */ public enum QueryType { EQUALS { @Override public Criteria buildCriteria(QueryField queryFieldAnnotation, Field field, Object value) { if (check(queryFieldAnnotation, field, value)) { String queryField = getQueryFieldName(queryFieldAnnotation, field); return Criteria.where(queryField).is(value.toString()); } return new Criteria(); } }, LIKE { @Override public Criteria buildCriteria(QueryField queryFieldAnnotation, Field field, Object value) { if (check(queryFieldAnnotation, field, value)) { String queryField = getQueryFieldName(queryFieldAnnotation, field); return Criteria.where(queryField).regex(value.toString()); } return new Criteria(); } }, IN { @Override public Criteria buildCriteria(QueryField queryFieldAnnotation, Field field, Object value) { if (check(queryFieldAnnotation, field, value)) { if (value instanceof List) { String queryField = getQueryFieldName(queryFieldAnnotation, field); // 此处必须转型为List,否则会在in外面多一层[] return Criteria.where(queryField).in((List<?>)value); } } return new Criteria(); } }; private static boolean check(QueryField queryField, Field field, Object value) { return !(queryField == null || field == null || value == null); } public abstract Criteria buildCriteria(QueryField queryFieldAnnotation, Field field, Object value); /** * 如果实体bean的字段上QueryField注解没有设置attribute属性时,默认为该字段的名称 * * @param queryFieldAnnotation * @param field * @return */ private static String getQueryFieldName(QueryField queryField, Field field) { String queryFieldValue = queryField.attribute(); if (!StringUtils.hasText(queryFieldValue)) { queryFieldValue = field.getName(); } return queryFieldValue; } }
这样当创建一个实体类时,可以为相应的字段添加注解即可,比如Article类:
public class Article { @QueryField private String id; @QueryField private String title; @QueryField(type = QueryType.LIKE, attribute = "content") private String content; @QueryField(type = QueryType.IN, attribute = "title") private List<String> queryTitles; }
这样一个实例可通过下面方法转换成一个Query实例
private Query buildBaseQuery(T t) { Query query = new Query(); Field[] fields = t.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); try { Object value = field.get(t); if (value != null) { QueryField queryField = field.getAnnotation(QueryField.class); if (queryField != null) { query.addCriteria(queryField.type().buildCriteria(queryField, field, value)); } } } catch (Exception e) { // should not happend } return query; }
增删改查的基本DAO如下:
public abstract class MongodbBaseDao<T>{ @Autowired @Qualifier("mongoTemplate") protected MongoTemplate mongoTemplate; //保存一个对象到mongodb public T save(T bean) { mongoTemplate.save(bean); return bean; } // 根据id删除对象 public void deleteById(T t) { mongoTemplate.remove(t); } // 根据对象的属性删除 public void deleteByCondition(T t) { Query query = buildBaseQuery(t); mongoTemplate.remove(query, getEntityClass()); } // 通过条件查询更新数据 public void update(Query query, Update update) { mongoTemplate.updateMulti(query, update, this.getEntityClass()); } // 根据id进行更新 public void updateById(String id, T t) { Query query = new Query(); query.addCriteria(Criteria.where("id").is(id)); Update update = buildBaseUpdate(t); update(query, update); } // 通过条件查询实体(集合) public List<T> find(Query query) { return mongoTemplate.find(query, this.getEntityClass()); } public List<T> findByCondition(T t) { Query query = buildBaseQuery(t); return mongoTemplate.find(query, getEntityClass()); } // 通过一定的条件查询一个实体 public T findOne(Query query) { return mongoTemplate.findOne(query, this.getEntityClass()); } // 通过ID获取记录 public T get(String id) { return mongoTemplate.findById(id, this.getEntityClass()); } // 通过ID获取记录,并且指定了集合名(表的意思) public T get(String id, String collectionName) { return mongoTemplate.findById(id, this.getEntityClass(), collectionName); } // 根据vo构建查询条件Query private Query buildBaseQuery(T t) { Query query = new Query(); Field[] fields = t.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); try { Object value = field.get(t); if (value != null) { QueryField queryField = field.getAnnotation(QueryField.class); if (queryField != null) { query.addCriteria(queryField.type().buildCriteria(queryField, field, value)); } } } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } return query; } private Update buildBaseUpdate(T t) { Update update = new Update(); Field[] fields = t.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); try { Object value = field.get(t); if (value != null) { update.set(field.getName(), value); } } catch (Exception e) { e.printStackTrace(); } } return update; } // 获取需要操作的实体类class @SuppressWarnings("unchecked") protected Class<T> getEntityClass() { return ((Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]); } public MongoTemplate getMongoTemplate() { return mongoTemplate; } }
这样基本的CRUD就写好了,虽然一些复杂的查询和更新还得自己构造Query和Update,但是大部分情况足以应付了。