浅谈spring模板化处理风格操纵mongodb的使用_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > 浅谈spring模板化处理风格操纵mongodb的使用

浅谈spring模板化处理风格操纵mongodb的使用

 2016/7/18 5:31:19  worldly  程序员俱乐部  我要评论(0)
  • 摘要:在spring已经成为Javaweb开发者必学技能的今天,适应spring编程风格将有助于快速理解和学习spring相关项目,避开aop和ioc不谈,比如工厂方法模式的身影随处可见,今天讨论的是spring访问mongo的API模板,这种模板化的处理方式,也是spring常见的,以下是几个例子:1.spring-ldap-core中的ldapTemplate2.spring-jdbc中的jdbcTemplate3.mybatis-spring中的sqlSessionTemplate4
  • 标签:模板 使用 Spring 浅谈 MongoDB

在spring已经成为Java web开发者必学技能的今天,适应spring编程风格将有助于快速理解和学习spring相关项目,避开aop和ioc不谈,比如工厂方法模式的身影随处可见,今天讨论的是spring 访问mongo的API模板,这种模板化的处理方式,也是spring常见的,以下是几个例子

1.spring-ldap-core中的ldapTemplate

2.spring-jdbc中的jdbcTemplate

3.mybatis-spring中的sqlSessionTemplate

4.spring-data-mongodb中的MongoTemplate

,等等吧,最近在做关于mongo的项目,所以讲讲MongoTemplate,本文的代码是基于

spring-data-commons-1.10.0.RELEASE.jar

spring-data-mongodb-1.7.0.RELEASE.jar

mongo-java-driver-3.2.2.jar

当然还会有spring环境的包,这不是本文的重点,所以忽略。

一、查看源码了解MongoTemplate

继续查看MongoOperations和ApplicationContextAware发现MongoTemplate中的所有对mongo数据操纵的方法都是来自于接口MongoOperations:



?至此,我们基本了解了MongoTemplate,我们已经迫切需要使用它了,那我们必须实例化它,所以有必要认识一下它的构造子:



?可以看到,有四个构造函数可用,前俩个源码如下

?

class="java">public MongoTemplate(Mongo mongo, String databaseName) {
		this(new SimpleMongoDbFactory(mongo, databaseName), null);
	}
public MongoTemplate(Mongo mongo, String databaseName, UserCredentials userCredentials) {
		this(new SimpleMongoDbFactory(mongo, databaseName, userCredentials));
	}
?但是SimpleMongoDbFactory(mongo, databaseName)已经是被标记为废弃的方法,所以不能用它们去实例化MongoTemplate,再看剩下两个构造子的源码:
public MongoTemplate(MongoDbFactory mongoDbFactory) {
		this(mongoDbFactory, null);
	}
public MongoTemplate(MongoDbFactory mongoDbFactory, MongoConverter mongoConverter) {

		Assert.notNull(mongoDbFactory);

		this.mongoDbFactory = mongoDbFactory;
		this.exceptionTranslator = mongoDbFactory.getExceptionTranslator();
		this.mongoConverter = mongoConverter == null ? getDefaultMongoConverter(mongoDbFactory) : mongoConverter;
		this.queryMapper = new QueryMapper(this.mongoConverter);
		this.updateMapper = new UpdateMapper(this.mongoConverter);

		// We always have a mapping context in the converter, whether it's a simple one or not
		mappingContext = this.mongoConverter.getMappingContext();
		// We create indexes based on mapping events
		if (null != mappingContext && mappingContext instanceof MongoMappingContext) {
			indexCreator = new MongoPersistentEntityIndexCreator((MongoMappingContext) mappingContext, mongoDbFactory);
			eventPublisher = new MongoMappingEventPublisher(indexCreator);
			if (mappingContext instanceof ApplicationEventPublisherAware) {
				((ApplicationEventPublisherAware) mappingContext).setApplicationEventPublisher(eventPublisher);
			}
		}
	}
?通过上述源码可以清晰的看到这两个方法有个共同参数MongoDbFactory,第二个参数MongoConverter作用是:“Central Mongo specific converter interface which combines {@link MongoWriter} and {@link MongoReader}”(源码注释),所以我们决定使用第三个构造子,即通过MongoDbFactory对象生MongoTemplate实例,那又需要我们查看MongoDbFactory的构造子,最终我们可以通过host,port,username,password,dbname构造一个mongo实例,从而通过该实例构造一个MongoDbFactory对象,最终可以使用其成功实例化MongoTemplate,以下是在spring配置文件中的配置:
<!-- 加载mongodb的属性配置文件 -->
	<context:property-placeholder location="classpath:/mongo/mongodb.properties" />
	<!-- 构造MongoDbFactory对象 -->
	<mongo:db-factory id="mongoDbFactory" host="${mongo.host}"
		port="${mongo.port}" dbname="${mongo.dbname}" username="${mongo.username}"
		password="${mongo.password}" />
	<!-- 构造MongoTemplate -->
	<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
		<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
	</bean>
?这样,我们需要的MongoTemplate实例会随着spring容器的启动而生成。下面我们要讨论的是基于该实例进行mongo的数据操纵(MongoDB的安装等环境准备工作省略)。 二、基于MongoTemplate对象的数据操纵 前面已经讨论过,基于MongoTemplate的数据从操纵实际上是接口MongoOperations中方法的实现和重载。本文只讨论CRUD四中常见操作: 1.查询 mongo中支持对数据的排序和分页,如下是笔者对分页查询的实现:
/**
	 * 查询分页数据
	 * @author young
	 * @param QueryEntity
	 *            查询条件分装实体
	 * @param entityClass
	 *            确定集合名称的实体类
	 */
	public <T> List<T> queryPageData(QueryEntity queryEntity, Class<T> entityClass) {
		Assert.notNull(entityClass, "entityClass must not be null");
		if (!hasCollection(entityClass)) {
			logger.info("collection  determied by entityClass not be found ");
			return null;
		}
		Query query = QueryEntityConverter.getQueryStataments(queryEntity);
		List<T> listResult = getMongoTemplate().find(query, entityClass);
		return listResult;
	}

	/**
	 * 查询分页数据
	 * @author young
	 * @param queryEntity
	 *            查询条件分装实体
	 * @param collectionName
	 *            集合名称
	 * @return
	 */
	public List<? extends Object> queryPageData(QueryEntity queryEntity, String collectionName) {
		Assert.notNull(collectionName, "collection name must not null");
		if (!hasCollection(collectionName)) {
			logger.info("collection named " + collectionName + " not be found");
			return null;
		}
		Query query = QueryEntityConverter.getQueryStataments(queryEntity);
		List<? extends Object> listResult = getMongoTemplate().find(query, Object.class, collectionName);
		return listResult;

	}
?其中,QueryEntity是对分页排序参数的封装,通过它转换成查询对象Query,而entityClass的作用在于确定mongodb中的collection name,即集合名称,还有一个作用就是对query结果集的映射。在第二个方法中我们指定了collection name,用Object对象完成结果集映射。从而同样可以实现功能,而又拜托了实体绑定带来的繁琐。
2.插入 我们知道,mongodb是以一种类似于json的bson格式存放数据到文件中的,它的插入就是基于内存的文件读写操作。所以对数据的插入操作限制很宽泛,几乎任何形式的数据,只要能装换成json的数据或者json字符创的形式数据都可以直接插入,笔者也最喜欢这种简单有用的操作,是nosql基于文件的数据读写而不是结构化数据库技术执行SQL语句来实现数据的读写,在这点上,MongoDB可以对SQL注入攻击天然不感冒。下面是笔者实现的代码:
/**
	 * 批量插入
	 * @author young
	 * @param json
	 *            需要插入的json串文档
	 * @param collectionName
	 *            集合名称
	 */
	public void insertMany(Object json, String collectionName) {
		Assert.notNull(json, "you can not save a null object into a collection");
		Assert.notNull(collectionName, "collection name must be determined by parameter collectionName");
		if (!hasCollection(collectionName)) {
			logger.info("collection named " + collectionName + " not be found b");
		}
		getMongoTemplate().insert(json, collectionName);

	}

	/**
	 * 批量插入
	 * @author young
	 * @param batchToSave
	 *            插入文档列表
	 * @param collectionName
	 *            集合名称
	 */
	public <T> void insertMany(List<T> batchToSave, String collectionName) {
		Assert.notNull(batchToSave, "you can not save a null object into a collection");
		Assert.notNull(collectionName, "collection name must be determined by parameter collectionName");
		if (!hasCollection(collectionName)) {
			logger.info("collection named " + collectionName + " not be found ");
		}
		getMongoTemplate().insert(batchToSave, collectionName);
	}

	/**
	 * 批量插入
	 * @author young
	 * @param batchToSave
	 *            插入文档列表
	 * @param entityClass
	 *            确定集合名称的实体类
	 */
	public <T> void insertMany(List<T> batchToSave, Class<T> entityClass) {
		Assert.notNull(batchToSave, "you can not save a null object into a collection");
		Assert.notNull(entityClass, "entityClass must not be null");
		if (!hasCollection(entityClass)) {
			logger.info("collection  determied by entityClass not be found ");
			return;
		}
		getMongoTemplate().insert(batchToSave, entityClass);
	}
?3.更新 更新同插入、删除一样也是一种写操作。一般而言,对于数据库操作,性能瓶颈往往是是插入和查询,尤其是查询,尽管如此,更新数据也是必不可少的。MongoDB提供了丰富的更新操作,如更新某些字段的值的setcaozuofu.html" target="_blank">操作符、给指定键的值增加值的inc操作符、修改某些collection的键值的rename操作符、删除集合中的某些键的unset操作符、给指定的键追加值的pull操作符、给某个数组键增加值的addToSet操作符、删除基于方向的第一个或者最后一个键的pop操作。如下是笔者实现的对指定键的值进行的批量更新操作:
/**
	 * @author young
	 * @param queryEntity
	 *            查询条件分装实体
	 * @param updateEntity
	 *            更新实体
	 * @param collectionName
	 *            集合名称
	 * @return
	 */
public int updateMany(QueryEntity queryEntity, Map<String, Object> updateEntity, String collectionName) {
		Assert.notNull(collectionName, "collection name must not be null ");
		if (!hasCollection(collectionName)) {
			logger.info("collection named " + collectionName + " not be found ");
			return -1;
		}
		Query query = QueryEntityConverter.getQueryStataments(queryEntity);
		Update update = UpdateEntityConverter.getKeysUpdateStatment(updateEntity);
		return getMongoTemplate().updateMulti(query, update, collectionName).getN();
	}

	/**
	 * @author young
	 * @param queryEntity
	 *            查询条件分装实体
	 * @param updateEntity
	 *            更新实体
	 * @param entityClass
	 *            确定集合名称的实体类
	 * @return
	 */
	public <T> int updateMany(QueryEntity queryEntity, Map<String, Object> updateEntity, Class<T> entityClass) {
		Assert.notNull(entityClass, "entityClass must not be null");
		if (!hasCollection(entityClass)) {
			logger.info("collection  determied by entityClass not be found ");
			return -1;
		}
		Query query = QueryEntityConverter.getQueryStataments(queryEntity);
		Update update = UpdateEntityConverter.getKeysUpdateStatment(updateEntity);
		return getMongoTemplate().updateMulti(query, update, entityClass).getN();
	}
?4.删除 删除操作对于一个开发人员而言是必须谨慎的操作,以至于好些公司的所谓删除操作是假删除,防止操作失误带来的损失。MongoDB数据库的删除操作也是很谨慎的,如在MongoDB中插入数据,只需满足格式要求,可以反复插入除主键字段外其他部分都相同的数据;但是对于删除,MongoDB的态度就是“在某些模糊描述的情况下,宁可不删”。这本身就是MongoDB的一种特性。其实,任何数据库的删除操作,都可以分为查询和删除两部分,前者是为了标记需要删除的数据,后者才是对标记数据的清理操作,所以笔者以为,删除操作的性能很大程度上是依赖于查询,它们都依赖于数据组织方式,如索引的建立,当然了更新操作也一样。如下是笔者对删除的实现:
/**
	 * 删除指定集合中的指定文档
	 * @author young
	 * @param collectionName
	 *            集合名称
	 * @param queryEntity
	 *            查询条件封装实体类,确定了需要删除的文档
	 * @return
	 */
	public int removeMany(QueryEntity queryEntity, String collectionName) {
		Assert.notNull(collectionName, "collection name must not be null ");
		if (!hasCollection(collectionName)) {
			logger.info("collection named " + collectionName + " not be found ");
			return -1;
		}
		Query query = QueryEntityConverter.getQueryStataments(queryEntity);
		return getMongoTemplate().remove(query, collectionName).getN();
	}

	/**
	 * 删除指定集合中的指定文档
	 * @author 
	 * @param queryEntity
	 *            查询条件封装实体类,确定了需要删除的文档
	 * @param entityClass
	 *            确定集合名称的实体类
	 * @return
	 */
	public <T> int removeMany(QueryEntity queryEntity, Class<T> entityClass) {
		Assert.notNull(entityClass, "entityClass must not be null");
		if (!hasCollection(entityClass)) {
			logger.info("collection  determied by entityClass not be found ");
			return -1;
		}
		Query query = QueryEntityConverter.getQueryStataments(queryEntity);
		return getMongoTemplate().remove(query, entityClass).getN();
	}
?到现在为止,我们已经完成了对MongoDB中数据库的增删查改等基本操作,但是一款优秀的产品,高级特性才是闪光的地方。MongoDB而言,数据读写的高性能,在背后支持的是基于内存操作,以及丰富的索引支持,淡然了查询优化器的作用不可小觑,在这点上索引和数据归类划分是我们开发人员讨论的重点,既要保证数据划分有利于业务逻辑,又要保证读写性能;在本文的最后,需要指出的是MongoDB作为一种nosql数据库,也支持丰富的数据聚合和map-reduce操作,这也是笔者最有兴趣的地方,不过鉴于性能和数据库io负荷的考虑,后台执行应该是一种经过实践检验的不错方式。
?

?

?

?

?

?

?

?

?


  • 大小: 20.9 KB
  • 大小: 319.3 KB
  • 大小: 107.8 KB
  • 查看图片附件
发表评论
用户名: 匿名