使用Spring-data-mongodb构建通用的MongoDB DAO

和关系型数据库类似,在使用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本身就已经实现对象到数据的映射。而封装的QueryUpdate类用来做查询和更新已经非常方便了。但是我们还是需要把实体Bean转换成Query实例。本着将懒惰进行到底的原则,这点也最好不要。

对于查询,一般根据实体bean的某些属性进行查询,最主要的查询有等于,likein类型。创建一个注解QueryField,代表要查询的字段

 

/**
 * <p>
 * 用于实体Bean的属性上的注解,注解有两个属性可以设置,type表示查询类似,默认为equals<br/>
 * attribute表示要查询的属性,默认为空串,在使用时如果为空串,则默认为实体Bean字段的名称
 * </p>
 *
 * @author: <a href="mailto:[email protected]">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:[email protected]">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就写好了,虽然一些复杂的查询和更新还得自己构造QueryUpdate,但是大部分情况足以应付了。

 

 

猜你喜欢

转载自liuluo129.iteye.com/blog/1994045