spring boot 使用mongoDB

连接

我们使用Spring data进行MongoDB连接。Spring boot有意简化甚至消除原生Spring框架的xml配置,所以spring boot项目推崇尽量不使用xml配置文件进行bean的管理。

使用properties进行mongoDB的连接配置

在application.properties中进行mongoDB连接字符串配置,org.springframework.boot.autoconfigure.mongo提供了对mongoDB连接字符串的配置支持。我们对指定属性进行配置即可。

注意mongo 2.4以上版本已经不支持如下配置了。
spring.data.mongodb.host=127.0.0.1
spring.data.mongodb.port=27017
spring.data.mongodb.username=root
spring.data.mongodb.password=root
spring.data.mongodb.database=gis

2.4以上版本使用如下连接配置:

spring.data.mongodb.uri=mongodb://root(userName):root(password)@localhost(ip地址):27017(端口号)/gis(collections/数据库)

注解以及使用

spring-data-mongodb主要有以下注解:

@Id

主键,不可重复,自带索引,可以在定义的列名上标注,需要自己生成并维护不重复的约束。如果自己不设置@Id主键,mongo会自动生成一个唯一主键,并且插入时效率远高于自己设置主键。
在实际业务中不建议自己设置主键,应交给mongo自己生成,自己可以设置一个业务id,如int型字段,用自己设置的业务id来维护相关联的表。

@Document

标注在实体类上,类似于hibernate的entity注解,标明由mongo来维护该表。

org.springframework.data.mongodb.core.mapping.Document.class
把一个java类声明为mongodb的文档,可以通过collection参数指定这个类对应的文档。
@Document(collection="mongodb 对应 collection 名")      
// 若未加 @Document ,该 bean save 到 mongo 的 user collection
// 若添加 @Document ,则 save 到 reUser collection
@Document(collection="reUser") 
public class User{
}
@Indexed

声明该字段需要加索引,加索引后以该字段为条件检索将大大提高速度。

@CompoundIndex

复合索引,加复合索引后通过复合索引字段查询将大大提高速度。

@Field

代表一个字段,可以不加,不加的话默认以参数名为列名。

给映射存储到 mongodb 的字段取别名
在 java bean 中字段名为 firstName,存储到 mongo 中 key 为 fName
    @Field("fName")
    private String firstName; 
@Transient

被该注解标注的,将不会被录入到数据库中。只作为普通的javaBean属性。

@DBRef

关联另一个document对象。类似于mysql的表关联,但并不一样,mongo不会做级联的操作。

这是个重点注解,下面用实际例子来说明

@Document
public class FPresult {
    private String userid;                   //用户id
    private String dateRange;                //分析的时间范围
    private List<FenceInfo> fenceInfoList;   //该用户的围栏列表
}
@Document
public class FenceInfo {
    private String Fenceid;
    private String lng;
    private String lat;
}

上面两个实体类是两个文档,类似于mysql的一行数据。不用管两个实体类的内容是什么,只需要知道FPresult文档希望包含多个FenceInfo文档。首先演示一下如果没有使用@DBRef注解时插入的结果。

编写一个测试类

    @Test
    public void test_mongodb3(){
        List<FenceInfo> fenceInfoList = new ArrayList<FenceInfo>();

        FPresult fPresult = new FPresult();
        fPresult.setUserid("husin");
        fPresult.setDateRange("20180106-20180109");

        FenceInfo fenceInfo1 = new FenceInfo();
        fenceInfo1.setFenceid("55");
        fenceInfo1.setLng("113.1567");
        fenceInfo1.setLat("27.0136");

        FenceInfo fenceInfo2 = new FenceInfo();
        fenceInfo2.setFenceid("56");
        fenceInfo2.setLng("113.271236");
        fenceInfo2.setLat("27.898989");

        fenceInfoList.add(fenceInfo1);
        fenceInfoList.add(fenceInfo2);

        fPresult.setFenceInfoList(fenceInfoList);
        userDaoimpl.insertFPresult(fPresult);
    }

这里创建了两条FenceInfo数据加入到FPresult中,在把FenceInfo插入到mongodb的集合中。

结果如下:

{
    "_id" : ObjectId("5b7e5450752c9707ef86eb72"),
    "userid" : "husin",
    "dateRange" : "20180106-20180109",
    "fenceInfoList" : [
        {
            "Fenceid" : "55",
            "lng" : "113.1567",
            "lat" : "27.0136"
        },
        {
            "Fenceid" : "56",
            "lng" : "113.271236",
            "lat" : "27.898989"
        }
    ],
    "_class" : "com.example.demo.mangodb.FPresult"
}

可以看到FPresult的fenceInfoList项对应了一个数组,这个数组包含了2个FenceInfo文档。它并没有生成FenceInfo集合(类似mysql的表),为什么会突然谈到生成集合这件事?
无风不起浪,@DBRef注解正是为了在上述情况下自动生成FenceInfo集合而生的。那么它生成FenceInfo集合的规则是怎么样的?下面举个例子说明一下就都清楚了。
还是是上面的例子:

@Document
public class FPresult {
    private String userid;                  //用户id
    private String dateRange;               //fp分析的时间范围
    @DBRef
    private List<FenceInfo> fenceInfoList;  //该用户的围栏列表
}

在FenceInfo列表定义前添加 @DBRef注解

@Document
public class FenceInfo {
    @Id
    private String Fenceid;
    private String lng;
    private String lat;
}

FenceInfo设置唯一id(类似mysql的主键)
注意:如果这里不添加@Id设置主键的话, 插入数据库时会报错, 这个先不管待会儿在说。

测试类不变:

    @Test
    public void test_mongodb3(){
        List<FenceInfo> fenceInfoList = new ArrayList<FenceInfo>();

        FPresult fPresult = new FPresult();
        fPresult.setUserid("husin");
        fPresult.setDateRange("20180106-20180109");

        FenceInfo fenceInfo1 = new FenceInfo();
        fenceInfo1.setFenceid("55");
        fenceInfo1.setLng("113.1567");
        fenceInfo1.setLat("27.0136");

        FenceInfo fenceInfo2 = new FenceInfo();
        fenceInfo2.setFenceid("56");
        fenceInfo2.setLng("113.271236");
        fenceInfo2.setLat("27.898989");

        fenceInfoList.add(fenceInfo1);
        fenceInfoList.add(fenceInfo2);

        fPresult.setFenceInfoList(fenceInfoList);
        userDaoimpl.insertFPresult(fPresult);
    }

结果如下:

> db.fPresult.find().pretty()
{
    "_id" : ObjectId("5b7e59de752c970bfccfc761"),
    "userid" : "husin",
    "dateRange" : "20180106-20180109",
    "fenceInfoList" : [
        DBRef("fenceInfo", "55"),
        DBRef("fenceInfo", "56")
    ],
    "_class" : "com.example.demo.mangodb.FPresult"
}

可以看到fPresult的fenceInfoList项记录的不在是fenceInfoList文档,而是文档的唯一id。
嗯,这时候数据库中肯定会自动生成一个FenceInfo表,里面有两条记录分别是id=55的和id=56的。

> db.FenceInfo.find().pretty()
>

嗯? 怎么输出为空? 是不是哪里出错了?(一般输出为空表示没有这个表)

> show collections
fPresult
system.indexes

结果发现,并没有自动创建FenceInfo表。
奇怪了,难道事情不是像我们想的那样,可是fPresult表的确保存的是FenceInfo表的索引(唯一id)啊。

看看官方怎么说:
The mapping framework does not handle cascading saves. If you change an Account object that is referenced by a Person object, you must save the Account object separately. Calling save on the Person object will not automatically save the Account objects in the property accounts.
意思是不会处理级联保存,你必须单独处理关联的对象。

所以需要我们手动持久化FenceInfo数据,修改测试类:

    @Test
    public void test_mongodb3(){
        List<FenceInfo> fenceInfoList = new ArrayList<FenceInfo>();

        FPresult fPresult = new FPresult();
        fPresult.setUserid("husin");
        fPresult.setDateRange("20180106-20180109");

        FenceInfo fenceInfo1 = new FenceInfo();
        fenceInfo1.setFenceid("55");
        fenceInfo1.setLng("113.1567");
        fenceInfo1.setLat("27.0136");
        userDaoimpl.insertFenceInfo(fenceInfo1);

        FenceInfo fenceInfo2 = new FenceInfo();
        fenceInfo2.setFenceid("56");
        fenceInfo2.setLng("113.271236");
        fenceInfo2.setLat("27.898989");
        userDaoimpl.insertFenceInfo(fenceInfo2);

        fenceInfoList.add(fenceInfo1);
        fenceInfoList.add(fenceInfo2);

        fPresult.setFenceInfoList(fenceInfoList);
        userDaoimpl.insertFPresult(fPresult);
    }

这时候你肯定会想这标签也太不智能了,没错我也是这么想的。
@DBRef标签并不会做联级操作,也就是说你即使删掉了FPresult表中关于FenceInfo的内容,FenceInfo表的内容并不会受到影响。

那这个注解岂不是很鸡肋?

对此官方的说法是:
In short,the best time to use DBRefs are when you’re storing heterogeneous references to documents in different collections.like when you want to take advantage of some additional DBRef-specific functionality in a driver or tool.
实际使用中,感觉貌似作用是在不同的表做划分吧,有点模拟mysql外键的意思。免得数据都落到一个大表的,不便于做关联的表的查询。

补充:这里FenceInfo必须指定主键(唯一id),不能使用自动生成的id,否则会报错:
org.springframework.data.mapping.model.MappingException: No id property found for object of type class…….

猜你喜欢

转载自blog.csdn.net/HUXINY/article/details/81948176