MongoDB
http://lib.csdn.net/base/mongodb
http://blog.csdn.net/Flyfish111222/article/details/51891130
Introduction
MongoDB 是一个是一个基于分布式文件存储的数据库,介于关系数据库和非关系数据库之间,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,
是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系
数据库单表查询的绝大部分功能,而且还支持对数据建立索引
安装MongoDB
sudo apt-get install mongodb
mongo -version
启动、关闭数据库(默认设置MongoDB是随Ubuntu启动自动启动的。)
service mongodb start
service mongodb stop
查看是否启动成功:
pgrep mongo -l #注意:-l是英文字母l,不是阿拉伯数字1
卸载MongoDB
sudo apt-get --purge remove mongodb mongodb-clients mongodb-server
使用MongoDB
输入mongo进入shell命令模式,默认连接的数据库是test数据库,在此之前一定要确保你已经启动了MongoDB
mongo
显示shell脚本表示成功,连接test
常用操作命令:
show dbs:显示数据库列表
show collections:显示当前数据库中的集合(类似关系数据库中的表table)
show users:显示所有用户
use DBName:切换当前数据库至DBName
db.help() :显示数据库操作命令
db.CollectionName.help() : 显示集合操作命令,CollectionName是集合名
创建库和集合
MongoDB没有创建数据库的命令,如果你想创建一个“School”的数据库,先运行use School命令,之后做一些操作(如:创建聚集集合db.createCollection('teacher')),
这样就可以创建一个名叫“School”的数据库,并且创建一个集合teacher.
集合也可以不创建,直接添加数据,会自动创建数据, 不需要预先定义 collection
切换数据库
use School #切换到School数据库。MongoDB 无需预创建School数据库,在使用时会自动创建
创建Collection
db.createCollection('teacher') #创建一个聚集集合。MongoDB 其实在插入数据的时候,也会自动创建对应的集合,无需预定义集合
插入数据
插入数据有两种方式:insert和save。
这两种方式,其插入的数据中_id字段均可不写,会自动生成一个唯一的_id来标识本条数据
insert和save不同之处在于:
在手动插入_id字段时,如果_id已经存在,insert不做操作,save做更新操作;如果不加_id字段,两者作用相同都是插入数据
db.student.insert({_id:1, sname: 'zhangsan', sage: 20}) #_id可选
db.student.save({_id:1, sname: 'zhangsan', sage: 22}) #_id可选
数据操作
db.student.insert({_id:1, sname: 'zhangsan', sage: 20})
db.student.find();
{ "_id : 1, "sname" : "zhangsan", "sage" : 20 } 插入成功
db.student.save({_id:1, sname: 'zhangsan', sage: 22})
db.student.find();
{ "_id : 1, "sname" : "zhangsan", "sage" : 22 } id 相同, save更新数据成功
db.student.insert({_id:1, sname: 'zhangsan', sage: 25})
db.student.find();
{ "_id : 1, "sname" : "zhangsan", "sage" : 22 } id 相同, insert更新数据失败,不做更改
查找数据
db.youCollection.find(criteria, filterDisplay)
criteria :查询条件,可选
filterDisplay:筛选显示部分数据,如显示指定列数据,可选(当选择时,第一个参数不可省略,若查询条件为空,可用{}做占位符,如下面第三句
etc:
db.student.find() #查询所有记录。相当于:select * from student
db.student.find({sname: 'lisi'}) #查询sname='lisi'的记录。相当于: select * from student where sname='lisi'
db.student.find({},{sname:1, sage:1}) #查询指定列sname、sage数据。相当于:select sname,sage from student。sname:1
表示返回sname列,默认_id字段也是返回的,可以添加_id:0(意为不返回_id)写成{sname: 1, sage: 1,_id:0},就不会返回默认的_id字段了
db.student.find({sname: 'zhangsan', sage: 22}) #and 与条件查询。相当于:select * from student where sname = 'zhangsan' and sage = 22
db.student.find({$or: [{sage: 22}, {sage: 25}]}) #or 条件查询。相当于:select * from student where sage = 22 or sage = 25
修改数据
db.youCollection.update(criteria, objNew, upsert, multi ), criteria和objNew是必选参数,upsert和multi可选参数
criteria: update的查询条件,类似sql update查询内where后面的
objNew : update的对象和一些更新的操作符(如$set)等,也可以理解为sql update查询内set后面的。
upsert : 如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
multi: mongodb默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。默认false,只修改匹配到的第一条数据。
db.student.update({sname: 'lisi'}, {$set: {sage: 30}}, false, true) #相当于:update student set sage =30 where sname = 'lisi';
删除数据
db.student.remove({sname: 'chenliu'}) #相当于:delete from student where sname='chenliu'
退出shell命令模式
输入exit或者Ctrl+C退出shell命令模式
注意:MongoDB相较安全性更偏向易用性,默认是没有开启用户权限的,如果想开启用户权限,可以参考http://blog.csdn.net/Flyfish111222/article/details/51886840
Java API编程实例
添加jar包
mongo-java-driver-3.2.2.jar
<!--
import java.util.ArrayList;
import java.util.List;
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
public class TestMongoDB {
/**
* @param args
*/
public static void main(String[] args) {
insert();//插入数据。执行插入时,可将其他三句函数调用语句注释掉,下同
// find(); //查找数据
// update();//更新数据
// delete();//删除数据
}
/**
* 返回指定数据库中的指定集合
* @param dbname 数据库名
* @param collectionname 集合名
* @return
*/
//MongoDB无需预定义数据库和集合,在使用的时候会自动创建
public static MongoCollection<Document> getCollection(String dbname,String collectionname){
//实例化一个mongo客户端,服务器地址:localhost(本地),端口号:27017
MongoClient mongoClient=new MongoClient("localhost",27017);
//实例化一个mongo数据库
MongoDatabase mongoDatabase = mongoClient.getDatabase(dbname);
//获取数据库中某个集合
MongoCollection<Document> collection = mongoDatabase.getCollection(collectionname);
return collection;
}
/**
* 插入数据
*/
public static void insert(){
try{
//连接MongoDB,指定连接数据库名,指定连接表名。
MongoCollection<Document> collection= getCollection("test","student");
//实例化一个文档,文档内容为{sname:'Mary',sage:25},如果还有其他字段,可以继续追加append
Document doc1=new Document("sname","Mary").append("sage", 25);
//实例化一个文档,文档内容为{sname:'Bob',sage:20}
Document doc2=new Document("sname","Bob").append("sage", 20);
List<Document> documents = new ArrayList<Document>();
//将doc1、doc2加入到documents列表中
documents.add(doc1);
documents.add(doc2);
//将documents插入集合
collection.insertMany(documents);
System.out.println("插入成功");
}catch(Exception e){
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
}
}
/**
* 查询数据
*/
public static void find(){
try{
MongoCollection<Document> collection = getCollection("test","student");
//通过游标遍历检索出的文档集合
//MongoCursor<Document> cursor= collection.find(new Document("sname","Mary")). projection(new Document("sname",1).append("sage",1).append("_id", 0)).iterator();
//find查询条件:sname='Mary'。projection筛选:显示sname和sage,不显示_id(_id默认会显示)
//查询所有数据
MongoCursor<Document> cursor= collection.find().iterator();
while(cursor.hasNext()){
System.out.println(cursor.next().toJson());
}
}catch(Exception e){
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
}
}
/**
* 更新数据
*/
public static void update(){
try{
MongoCollection<Document> collection = getCollection("test","student");
//更新文档 将文档中sname='Mary'的文档修改为sage=22
collection.updateMany(Filters.eq("sname", "Mary"), new Document("$set",new Document("sage",22)));
System.out.println("更新成功!");
}catch(Exception e){
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
}
}
/**
* 删除数据
*/
public static void delete(){
try{
MongoCollection<Document> collection = getCollection("test","student");
//删除符合条件的第一个文档
collection.deleteOne(Filters.eq("sname", "Bob"));
//删除所有符合条件的文档
//collection.deleteMany (Filters.eq("sname", "Bob"));
System.out.println("删除成功!");
}catch(Exception e){
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
}
}
}
-->
MongoDB 用户权限
Introduction
MongoDB默认是没有开启用户权限的,如果直接在公网服务器上如此搭建mongodb,那么所有人都可以直接访问并修改数据库数据了。
其实MongoDB本身有非常详细的安全配置准则,显然开发者也是想到了,然而他是将安全的任务推给用户去解决,这本身的策略就是偏向易用性的,对于安全性,则得靠边站了。
MongoDB有两种用户,一种是admin用户(这里以root为例),能查看所有数据库;另一种数据库用户(这里以dbuser为例)
首先输入mongo进入MongoDB shell命令模式:
mongo
在admin表中添加root用户,在test表中添加dbuser用户
use admin
db.addUser('root','123') #用户名:root,密码:123
use test
db.addUser('dbuser','123') #用户名:dbuser,密码:123
MongoDB所有的用户信息都是存储在admin数据库中system.users表中,截图如下:
db.system.users.find()
修改/etc/mongodb.conf,设置auth=true
sudo vim /etc/mongodb.conf //取消注释
重启MongoDB
service mongodb stop #停止MongoDB服务(因为MongoDB是随开机是自启动的)
service mongodb start #开启MongoDB服务
测试用户权限是否正常开启
试图查看test数据库的所有集合,发现查看失败;验证test数据库用户dbuser;再次查看test所有集合,发现查看成功.
show collections failed
db.auth('dbuser','123') 1,表示成功
show collections success
试图查看School数据库的所有集合,发现查看失败,因为dbuser是test数据库的用户,无法验证School数据库;
切换到admin数据库,验证root用户名;再切换回School数据库,查看其所有集合,发现查看成功
use school
show collections failed
use admin
db.auth('root', '123') 1,用户验证成功
use School
show collections success
注:
这里需要指出的是,验证用户不是切换用户,只要验证过用户,就拥有了对该数据库的操作权限,如我先验证了root用户,再验证dbuser用户(test数据库的用户),
依然可以操作School数据库,因为验证过root用户,仅对当次shell模式有效,退出shell之后再进入shell需要重新验证。
MongoDB的安装包安装和使用
获取安装包
将安装包解压到/usr/local,并将解压的文件夹重命名为mongodb
sudo tar -zxf ~/Downloads/mongodb-linux-x86_64-ubuntu1604-3.2.7.tgz -C /usr/local
cd /usr/local
sudo mv mongodb-linux-x86_64-ubuntu1604-3.2.7 mongodb
创建文件夹/data/db/journal,创建文件mongodb.log, 拟将/data/db作为数据保存目录,/data/db/journal/mongodb.log作为日志文件
sudo mkdir -p /data/db/journal
cd /data/db/journal
touch mongodb.log
创建并配置文件/etc/mongodb.conf
sudo vim /etc/mongodb.conf #没有/etc/mongodb.conf文件时用本条命令会自动创建/etc/mongodb.conf
<!--
#日志文件位置
logpath=/data/db/journal/mongodb.log
# 以追加方式写入日志
logappend=true
# 是否以守护进程方式运行
fork = true
# 默认27017
#port = 27017
# 数据库文件位置
dbpath=/data/db
# 启用定期记录CPU利用率和 I/O 等待
#cpu = true
# 是否以安全认证方式运行,默认是不认证的非安全方式
#noauth = true
#auth = true
# 详细记录输出
#verbose = true
# Inspect all client data for validity on receipt (useful for
# developing drivers)用于开发驱动程序时验证客户端请求
#objcheck = true
# Enable db quota management
# 启用数据库配额管理
#quota = true
# 设置oplog记录等级
# Set oplogging level where n is
# 0=off (default)
# 1=W
# 2=R
# 3=both
# 7=W+some reads
#diaglog=0
# Diagnostic/debugging option 动态调试项
#nocursors = true
# Ignore query hints 忽略查询提示
#nohints = true
# 禁用http界面,默认为localhost:28017
#nohttpinterface = true
# 关闭服务器端脚本,这将极大的限制功能
# Turns off server-side scripting. This will result in greatly limited
# functionality
#noscripting = true
# 关闭扫描表,任何查询将会是扫描失败
# Turns off table scans. Any query that would do a table scan fails.
#notablescan = true
# 关闭数据文件预分配
# Disable data file preallocation.
#noprealloc = true
# 为新数据库指定.ns文件的大小,单位:MB
# Specify .ns file size for new databases.
# nssize =
# Replication Options 复制选项
# in replicated mongo databases, specify the replica set name here
#replSet=setname
# maximum size in megabytes for replication operation log
#oplogSize=1024
# path to a key file storing authentication info for connections
# between replica set members
#指定存储身份验证信息的密钥文件的路径
#keyFile=/path/to/keyfile
-->
脚本方式启动mongoDb服务
cd /usr/local/mongodb
./bin/mongod -f /etc/mongodb.conf forked process: 3709 success
pgrep mongo -l可以查看是否启动成功
3709 mongod
进入mongoDb shell 模式
cd /usr/local/mongodb
./bin/mongo
关闭mongodb服务
use admin
db.shutdownServer()
pgrep mongo -l 无, 表示成功退出mongoDb服务.
卸载mongodb
sudo apt-get --purge remove mongodb
代码片
代码段1 curd 操作
private static final int PORT = 27017;
public static MongoClient getInstance() {
return new MongoClient(IP, PORT);
}
public static void main(String args[]) {
try {
// 连接到 mongodb 服务
MongoClient mongoClient = MongoUtils.getInstance();
// 连接到数据库
MongoDatabase db = mongoClient.getDatabase("test");
System.out.println("Connect to database successfully");
MongoCollection dbc=db.getCollection("Order");
Document doc=new Document();
//insert key
doc.put("name", "hello");
doc.put("age", "24");
doc.put("time", "2015-01-30");
dbc.insertOne(doc);
//del key
// dbc.deleteOne(doc);
System.out.println("总记录数>>"+dbc.count());
} catch (Exception e) {
System.err.println(e.getClass().getName() + ": " + e.getMessage());
}
}
代码段2 上传文件
Mongo mongo = new Mongo();
DB db = mongo.getDB("mongoDBFile"); //创建数据库连接
GridFS myFS = new GridFS(db,"user1"); //创建GridFS 对mongoDBFile数据库中的user1进行操作,这样文件的读取和删除都是user1中的文件
//保存文件
GridFSFile file = myFS.createFile(new File("D:/image1.jpg"));
file.save();
//输出文件
/*GridFSDBFile file =myFS.findOne("image1.jpg");
file.writeTo(new File("D:/image1.jpg"));*/
//删除文件
/*GridFSDBFile file =myFS.findOne("image1.jpg");
myFS.remove((ObjectId) file2.getId());*/
代码段3 分页优化
* 小于指定日期的所有根据UUID分组的访问记录
* @param 指定日期
* @return 所有访问记录的MAP
*/
public static Multimap<String, Map<String, String>> getOldVisitors(String date){
//每次查询的记录数
int pagesize = 100000;
//mongodb中的"_id"
String objectId = "";
//方法的返回值类型,此处用的google guava
Multimap<String, Map<String, String>> mapless = null;
//查询的条件
BasicDBObject queryless = new BasicDBObject(),fields = new BasicDBObject(),field = new BasicDBObject();
//初始化返回的mongodb集合操作对象,大家可以写个数据连接池
dbCol = init();
//查询指定字段,字段越少,查询越快,当然都是一些不必要字段
field.put("uuid",1);
fields.put("uuid", 1);
fields.put("initTime", 1);
//小于指定日期的条件
String conditionless = TimeCond.getTimeCondless(date);
queryless.put("$where", conditionless);
DBCursor cursorless = dbCol.find(queryless,field);
//MongoDB在小于指定日期条件下,集合总大小
int countless = cursorless.count();
//查询遍历的次数 circleCountless+1
int circleCountless = countless/pagesize;
//取模,这是最后一次循环遍历的次数
int modless = countless%pagesize;
//开始遍历查询
for (int i = 1; i <=circleCountless+1; i++) {
//文档对象
DBObject obj = null;
//将游标中返回的结果记录到list集合中,为什么放到list集合中?这是为后面guava 分组做准备
List<Map<String, String>> listOfMaps = new ArrayList();
//如果条件不为空,则加上此条件,构成多条件查询,这一步是分页的关键
if (!"".equals(objectId)) {
//我们通过文档对象obj.get("_id")返回的是不带ObjectId(),所以要求此步骤
ObjectId id = new ObjectId(objectId);
queryless.append("_id", new BasicDBObject("$gt",id));
}
if (i<circleCountless+1) {
cursorless = dbCol.find(queryless,fields).sort(new BasicDBObject("_id", 1)).limit(pagesize);
}else if(i==circleCountless+1){//最后一次循环
cursorless = dbCol.find(queryless,fields).limit(modless);
}
//将游标中返回的结果记录到list集合中,为什么放到list集合中?这是为后面guava 分组做准备
while (cursorless.hasNext()) {
obj = cursorless.next();
listOfMaps.add((Map<String, String>) obj);
}
//获取一次分页中最后一条记录的"_id",然后作为条件传入到下一个循环中
if (null!=obj) {
objectId = obj.get("_id").toString();
}
//第一次分组,根据uuid分组,分组除今天之外的历史数据
mapless = Multimaps.index(
listOfMaps,new Function<Map<String, String>, String>() {
public String apply(final Map<String, String> from) {
return from.get("uuid");
}
});
}
return mapless;
}