MongoTemplate的实战
目录
一、Mavn依赖:
<!--spring data jars-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>
<!-- mongodb驱动-->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
</dependency>
二、实例化mongoTemplate
1、client-options配置详解
min-connections-per-host:客户端最小连接数
connections-per-host:客户端最大连接数,超过了将会被阻塞,默认100
threads-allowed-to-block-for-connection-multiplier:可被阻塞的线程数因子,默认值为5,如果connectionsPerHost
配置为10,那么最多能阻塞50个线程,超过50个之后就会收到一个异常
max-wait-time:阻塞线程获取连接的最长等待时间,默认120000 ms
max-connection-idle-time:连接池连接最大空闲时间
max-connection-life-time:连接池连接的最大存活时间
connect-timeout:连接超时时间,默认值是0,就是不超时
socket-timeout:socket超时时间,默认值是0,就是不超时
socket-keep-alive:keep alive标志,默认false
server-selection-timeout:服务器查询超时时间,它定义驱动在抛出异常之前等待服务器查询成功,默认30s,单位milliseconds
read-preference:MongoDB有5种ReadPreference模式:
primary 主节点,默认模式,读操作只在主节点,如果主节点不可用,报错或者抛出异常。
primaryPreferred 首选主节点,大多情况下读操作在主节点,如果主节点不可用,如故障转移,读操作在从节点。
secondary 从节点,读操作只在从节点, 如果从节点不可用,报错或者抛出异常。
secondaryPreferred 首选从节点,大多情况下读操作在从节点,特殊情况(如单主节点架构)读操作在主节点。
nearest 最邻近节点,读操作在最邻近的成员,可能是主节点或者从节点。
write-concern:WriteConcern的7种写入安全机制抛出异常的级别:
NONE:没有异常抛出
NORMAL:仅抛出网络错误异常,没有服务器错误异常,写入到网络就返回
SAFE:抛出网络错误异常、服务器错误异常;并等待服务器完成写操作。
MAJORITY: 抛出网络错误异常、服务器错误异常;并多数主服务器完成写操作。
FSYNC_SAFE: 抛出网络错误异常、服务器错误异常;写操作等待服务器将数据刷新到磁盘。
JOURNAL_SAFE:抛出网络错误异常、服务器错误异常;写操作等待服务器提交到磁盘的日志文件。
REPLICAS_SAFE:抛出网络错误异常、服务器错误异常;等待至少2台服务器完成写操作。
heartbeat-frequency:驱动用来确保集群中服务器状态的心跳频率
min-heartbeat-frequency:驱动重新检查服务器状态最少等待时间
heartbeat-connect-timeout:集群心跳连接的超时时间
heartbeat-socket-timeout:集群心跳连接的socket超时时间
ssl:驱动是否使用ssl进行连接,默认是false
ssl-socket-factory-ref:用来进行ssl连接的SSLSocketFactory,如果配置为none,则使用SSLSocketFactory.getDefault()
2、MappingMongoConverter
调用mongoTemplate的save方法时, spring-data-mongodb的TypeConverter会自动给document添加一个_class属性, 值是你保存的类名. 这种设计并没有什么坏处. spring-data-mongodb是为了在把document转换成Java对象时能够转换到具体的子类. 但有时候我们并不希望出现这个字段, 主要是看上去会比较"烦". 可以通过设置MappingMongoConverter的MongoTypeMapper来解决这个问题.
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd">
<mongo:mongo-client id="mongo" host="127.0.0.1" port="27017" credentials="user:pass@test">
<mongo:client-options write-concern="NORMAL" />
</mongo:mongo-client>
<mongo:db-factory id="mongoDbFactory" dbname="test" mongo-ref="mongo"/>
<!-- 添加这部分可以入库时屏蔽掉_class字段-->
<bean id="mappingContext" class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" />
<bean id="defaultMongoTypeMapper" class="org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper">
<constructor-arg name="typeKey">
<null />
</constructor-arg>
</bean>
<bean id="mappingMongoConverter" class="org.springframework.data.mongodb.core.convert.MappingMongoConverter">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
<constructor-arg name="mappingContext" ref="mappingContext" />
<property name="typeMapper" ref="defaultMongoTypeMapper" />
</bean>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
<constructor-arg name="mongoConverter" ref="mappingMongoConverter" />
</bean>
</beans>
三、语法
1.条件查询:
// 再实例化一个条件对象
Criteria criteria = new Criteria();
// 先实例化一个查询对象,参数为一个Criteria对象
Query query = new Query(criteria );
// 相当于sql:where id = id
criteria = Criteria.where("id").is(id);
// 把query对象和要进行操作的实体类作为参数,find方法为mongoTemplate封装的查询方法
Aaa aaa = this.mongoTemplate.find(query,Aaa.class);
2.模糊查询
// 再实例化一个条件对象
Criteria criteria = new Criteria();
// 先实例化一个查询对象,参数为一个Criteria对象
Query query = new Query(criteria );
// 使用正则表达式实现分词
Pattern pattern = Pattern.compile("^.*" + projectName.toString() + ".*$", Pattern.CASE_INSENSITIVE);
// regex方法相当于sql的like条件
criteria = Criteria.where("project_name").regex(pattern);
// 把query对象和要进行操作的实体类作为参数,find方法为mongoTemplate封装的查询方法
Aaa aaa = this.mongoTemplate.find(query, aaa.class);
3.分页查询
// 再实例化一个条件对象
Criteria criteria = new Criteria();
// 先实例化一个查询对象,参数为一个Criteria对象
Query query = new Query(criteria);
// skip():跳过指定数量的数据;注意需要传入每页的大小和当前页数的参数
criteria.skip(pageSize*(pageNo-1));
//读取的记录条数
criteria.limit(pageSize);
// 把query对象和要进行操作的实体类作为参数,find方法为mongoTemplate封装的查询方法
return this.mongoTemplate.find(query, Aaa.class);
4.对查询进行排序
// 先实例化一个查询对象
Query query = new Query();
// 实例化一个排序对象,参数为排序顺序,和对哪个字段进行排序
Sort sort = new Sort(Sort.Direction.DESC,"id");
// 把sort对象加入query对象中
query.with(sort);
// 把query对象和要进行操作的实体类作为参数,find方法为mongoTemplate封装的查询方法
return this.mongoTemplate.find(query,Aaa.class);
5.查询总数:
// 实例化Criteria,相当于where id = id;
Criteria criteria= Criteria.where("id").is(id);
// 先实例化一个查询对象,参数为一个Criteria对象
Query query=new Query(criteria);
//count():查询记录总数
return this.mongoTemplate.count(query, StarLinkOperateLog.class);
6.新增
下面说一下insert和save的区别:
1)插入重复数据时:
insert: 若新增数据的主键已经存在,则会抛 DuplicateKeyException 异常提示主键重复,不保存当前数据。
save: 若新增数据的主键已经存在,则会对当前已经存在的数据进行修改操作。
2)批操作时:
insert: 可以一次性插入一整个列表,不用进行遍历操作,效率相对较高。
save: 需要遍历列表,进行一个个的插入,效率相对较低。
// 插入一条数据
return mongoTemplate.insert(新增的对象);
// 插入多条数据
return mongoTemplate.insertAll(新增的对象列表);
7.更新操作:
更新操作有以下的方法:
1)updateFirst 更改符合条件的第一个记录;
2)updateMulti 如果根据查询条件找到对应的多条记录,则会全部更新;
3)upsert 相当于 update+insert 如果根据条件没有对应的数据,则会进行插入操作;
4)findAndModify 查询然后更新;
// 根据id进行条件查询
Criteria criteria = Criteria.where("id").is(id);
// 实例化一个Query对象,传入条件对象参数
Query query = new Query(criteria);
// 实例化一个update对象
Update update = new Update();
// 指定需要更改的字段,设置新值
update.set("username","the new username");
A
// 传入需要的参数
return this.mongoTemplate.updateFirst(query,update,Aaa.class);
8.删除操作:
1)根据条件删除:
// 实例化一个条件对象
Criteria criteria = Criteria.where("id").is(id);
// 实例化一个Query对象,传入条件对象参数
Query query = new Query(criteria);
// 调用remove方法传入条件和进行操作的实体类
return this.mongoTemplate.remove(query,aaa.class);
下面补充两种删除方法说明:
1)findAndRemove:查询出符合条件的第一个结果,并将符合条件的数据删除而且只会删除第一条;
2)findAllAndRemove:查询出符合条件的所有结果,并将符合条件的所有数据删除;
9、集合的判断与创建
1) 判断集合是否存在
mongoTemplate.collectionExists(collectionName)
2) 创建集合
mongoTemplate.createCollection(collectionName)
10、--andOperator--方法
1)机制是:新建一个criteria对象放到当前criteria对象的criteriaChain属性中。
关于这个新建的criteria对象:它的key属性是”$and”,它的isValue值比较特别,是一个list集合,集合中是dbObject对象。
2)“$and”如何工作
向good中插入100W数据。
for (var i=0; i<1000000; i++) {
db.good.insert({"x":[i, i+1, i+2, i+3, i+4]});
}
执行查询,查看是否有$and的区别
$and和不加$and的区别在于:
> db.good.find({"x":{"$lt":5,"$gt":1}})//相当于加了"$and"
{ "_id" : ObjectId("4ed061427ecbeebf00e65625"), "x" : [ 0, 1, 2, 3, 4 ] }
{ "_id" : ObjectId("4ed061427ecbeebf00e65626"), "x" : [ 1, 2, 3, 4, 5 ] }
{ "_id" : ObjectId("4ed061427ecbeebf00e65627"), "x" : [ 2, 3, 4, 5, 6 ] }
{ "_id" : ObjectId("4ed061427ecbeebf00e65628"), "x" : [ 3, 4, 5, 6, 7 ] }
{ "_id" : ObjectId("4ed061427ecbeebf00e65629"), "x" : [ 4, 5, 6, 7, 8 ] }
> db.good.find({"x":{"$lt":5}, "x":{"$gt":1}})
{ "_id" : ObjectId("4ed061427ecbeebf00e65625"), "x" : [ 0, 1, 2, 3, 4 ] }
{ "_id" : ObjectId("4ed061427ecbeebf00e65626"), "x" : [ 1, 2, 3, 4, 5 ] }
{ "_id" : ObjectId("4ed061427ecbeebf00e65627"), "x" : [ 2, 3, 4, 5, 6 ] }
{ "_id" : ObjectId("4ed061427ecbeebf00e65628"), "x" : [ 3, 4, 5, 6, 7 ] }
{ "_id" : ObjectId("4ed061427ecbeebf00e65629"), "x" : [ 4, 5, 6, 7, 8 ] }
{ "_id" : ObjectId("4ed061427ecbeebf00e6562a"), "x" : [ 5, 6, 7, 8, 9 ] }
{ "_id" : ObjectId("4ed061427ecbeebf00e6562b"), "x" : [ 6, 7, 8, 9, 10 ] }
{ "_id" : ObjectId("4ed061427ecbeebf00e6562c"), "x" : [ 7, 8, 9, 10, 11 ] }
{ "_id" : ObjectId("4ed061427ecbeebf00e6562d"), "x" : [ 8, 9, 10, 11, 12 ] }
......
11、聚合group
1) 而mongoDB没提供SQL那样通过Group By就轻松实现数据库的分组功能,我们通过接口来实现的
db.collection.group({ key, reduce, initial[, keyf] [, cond] [, finalize] })
key |
作为分组的key |
reduce |
一个聚合函数操作文档的分组操作期间。这些函数可以返回一个sum或count。该函数接受两个参数:当前文档和这个群体聚集的结果文档。 |
initial |
初始化聚合结果文档变量,为空时自动为每列提供初始变量。 |
keyf |
可选。替代的key 字段。指定一个函数创建一个“key object”作为分组的key。使用keyf而是通过group by领域而不是现有的文档域键组。 |
cond |
过滤条件 |
finalize |
在db.collection.group()返回最终结果之前,此功能可以修改的结果文档或替换的结果文档作为一个整体。 |
预备数据:MonogoDB数据库中添加了订单的数据
/* 0 */
{
"_id" : ObjectId("552a330e05c27486b9b9b650"),
"_class" : "com.mongo.model.Orders",
"onumber" : "002",
"date" : ISODate("2014-01-03T16:03:00Z"),
"cname" : "zcy",
"item" : {
"quantity" : 1,
"price" : 4.0,
"pnumber" : "p002"
}
}
/* 1 */
{
"_id" : ObjectId("552a331d05c275d8590a550d"),
"_class" : "com.mongo.model.Orders",
"onumber" : "003",
"date" : ISODate("2014-01-04T16:03:00Z"),
"cname" : "zcy",
"item" : {
"quantity" : 10,
"price" : 2.0,
"pnumber" : "p001"
}
}
/* 2 */
{
"_id" : ObjectId("552a333105c2f28194045a72"),
"_class" : "com.mongo.model.Orders",
"onumber" : "003",
"date" : ISODate("2014-01-04T16:03:00Z"),
"cname" : "zcy",
"item" : {
"quantity" : 30,
"price" : 4.0,
"pnumber" : "p002"
}
}
/* 3 */
{
"_id" : ObjectId("552a333f05c2b62c01cff50e"),
"_class" : "com.mongo.model.Orders",
"onumber" : "004",
"date" : ISODate("2014-01-05T16:03:00Z"),
"cname" : "zcy",
"item" : {
"quantity" : 5,
"price" : 4.0,
"pnumber" : "p002"
}
}
我们要对日期和产品编码进行分组,并计算相同的产品的数量
MongoDB:
db.orders.group({
key: { date:1,'item.pnumber':1},
initial : {"total":0},
reduce : function Reduce(doc, out) {
out.total+=doc.item.quantity
} });
2)Spring Data MongoDB 提供了Group有几个接口
GroupCommand groupCommand=new GroupCommand(inputCollection, keys, condition, initial, reduce, finalize);
我们要对日期和产品编码进行分组,并计算相同的产品的数量
public void getGroupCount(String collectionName) {
BasicDBObject key = new BasicDBObject();
key.put("date", 1);
key.put("item.pnumber", 1);
//条件
BasicDBObject cond = new BasicDBObject();
//初始化
BasicDBObject initial = new BasicDBObject();
initial.append("total", 0);
//reduce
String reduce = "function Reduce(doc, out) { " +
" out.total+=doc.item.quantity;" +
"}";
SimpleDateFormat format=new SimpleDateFormat("yyyy-mm-dd");
BasicDBList groupList=(BasicDBList) mongoTemplate.getCollection(collectionName).group(key, cond, initial, reduce);
if(groupList!=null&&groupList.size()>0){
System.out.println("date item.pnumber total");
for(int i=0;i<groupList.size();i++){
BasicDBObject obj=(BasicDBObject) groupList.get(i);
System.out.println(format.format(obj.getDate("date"))+" "+obj.getString("item.pnumber")+" "+obj.getInt("total"));
}
}
}
来源:oschina
链接:https://my.oschina.net/u/4313158/blog/4480768