和关系型数据库类似,在使用MongoDB的时候最主要还是CRUD,而Spring-data-mongodb封装了MongoTemplate类,可以方便的进行相应的操作。
首先,配置spring
在关系型数据应用程序中,一般都会用到ORM框架,而Spring-data-mongodb本身就已经实现对象到数据的映射。而封装的Query和Update类用来做查询和更新已经非常方便了。但是我们还是需要把实体Bean转换成Query实例。本着将懒惰进行到底的原则,这点也最好不要。
对于查询,一般根据实体bean的某些属性进行查询,最主要的查询有等于,like和in类型。创建一个注解QueryField,代表要查询的字段
/**
*
* 用于实体Bean的属性上的注解,注解有两个属性可以设置,type表示查询类似,默认为equals
* attribute表示要查询的属性,默认为空串,在使用时如果为空串,则默认为实体Bean字段的名称
*
*
* @author: chuanli
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface QueryField {
QueryType type() default QueryType.EQUALS;
String attribute() default "";
}
默认使用相等。
QueryType是个枚举类型,表示查询类型
/**
*
* 查询类型的媒介类
* 目前有支持三种类型:
* 1. equals:相等
* 2. like:mongodb的like查询
* 3. in:用于列表的in类型查询
*
*
* @author: chuanli
*/
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 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{
@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 find(Query query) {
return mongoTemplate.find(query, this.getEntityClass());
}
public List 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 getEntityClass() {
return ((Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
}
public MongoTemplate getMongoTemplate() {
return mongoTemplate;
}
}
这样基本的CRUD就写好了,虽然一些复杂的查询和更新还得自己构造Query和Update,但是大部分情况足以应付了。