Spring Boot无缝集成MongoDB

上一节,我们做的那个例子有点太简单了,通常的后台都会涉及一些数据库的操作,然后在暴露的API中提供处理后的数据给客户端使用。那么这一节我们要做的是集成MongoDB ( https://www.mongodb.com )。

MongoDB是什么?

MongoDB是一个NoSQL数据库,是NoSQL中的一个分支:文档数据库。和传统的关系型数据库比如Oracle、SQLServer和MySQL等有很大的不同。传统的关系型数据库(RDBMS)已经成为数据库的代名词超过20多年了。对于大多数开发者来说,关系型数据库是比较好理解的,表这种结构和SQL这种标准化查询语言毕竟是很大一部分开发者已有的技能。那么为什么又搞出来了这个什么劳什子NoSQL,而且看上去NoSQL数据库正在飞快的占领市场。

NoSQL的应用场景是什么?

假设说我们现在要构建一个论坛,用户可以发布帖子(帖子内容包括文本、视频、音频和图片等)。那么我们可以画出一个下图的表关系结构。

论坛的简略ER图

这种情况下我们想一下这样一个帖子的结构怎么在页面中显示,如果我们希望显示帖子的文字,以及关联的图片、音频、视频、用户评论、赞和用户的信息的话,我们需要关联八个表取得自己想要的数据。如果我们有这样的帖子列表,而且是随着用户的滚动动态加载,同时需要监听是否有新内容的产生。这样一个任务我们需要太多这种复杂的查询了。

NoSQL解决这类问题的思路是,干脆抛弃传统的表结构,你不是帖子有一个结构关系吗,那我就直接存储和传输一个这样的数据给你,像下面那样。

{
 "id":"5894a12f-dae1-5ab0-5761-1371ba4f703e",
 "title":"2017年的Spring发展方向",
 "date":"2017-01-21",
 "body":"这篇文章主要探讨如何利用Spring Boot集成NoSQL",
 "createdBy":User,
 "images":["http://dev.local/myfirstimage.png","http://dev.local/mysecondimage.png"],
 "videos":[
  {"url":"http://dev.local/myfirstvideo.mp4", "title":"The first video"},
  {"url":"http://dev.local/mysecondvideo.mp4", "title":"The second video"}
 ],
 "audios":[
  {"url":"http://dev.local/myfirstaudio.mp3", "title":"The first audio"},
  {"url":"http://dev.local/mysecondaudio.mp3", "title":"The second audio"}
 ]
}

NoSQL一般情况下是没有Schema这个概念的,这也给开发带来较大的自由度。因为在关系型数据库中,一旦Schema确定,以后更改Schema,维护Schema是很麻烦的一件事。但反过来说Schema对于维护数据的完整性是非常必要的。

一般来说,如果你在做一个Web、物联网等类型的项目,你应该考虑使用NoSQL。如果你要面对的是一个对数据的完整性、事务处理等有严格要求的环境(比如财务系统),你应该考虑关系型数据库。

在Spring中集成MongoDB

在我们刚刚的项目中集成MongoDB简单到令人发指,只有三个步骤:

在 build.gradle 中更改 compile('org.springframework.boot:spring-boot-starter-web') 为 compile("org.springframework.boot:spring-boot-starter-data-rest")

在 Todo.java 中给 private String id; 之前加一个元数据修饰 @Id 以便让Spring知道这个Id就是数据库中的Id

新建一个如下的 TodoRepository.java

package dev.local.todo;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource(collectionResourceRel = "todo", path = "todo")
public interface TodoRepository extends MongoRepository<Todo, String>{
}
此时我们甚至不需要Controller了,所以暂时注释掉 TodoController.java 中的代码。然后我们 ./gradlew bootRun 启动应用。访问 http://localhost:8080/todo 我们会得到下面的的结果。
{
 _embedded: {
  todo: [ ]
 },
 _links: {
  self: {
   href: "http://localhost:8080/todo"
  },
  profile: {
   href: "http://localhost:8080/profile/todo"
  }
 },
 page: {
  size: 20,
  totalElements: 0,
  totalPages: 0,
  number: 0
 }
}

我勒个去,不光是有数据集的返回结果 todo: [ ] ,还附赠了一个links对象和page对象。如果你了解 Hypermedia 的概念,就会发现这是个符合 Hypermedia REST API返回的数据。

说两句关于 MongoRepository<Todo, String> 这个接口,前一个参数类型是领域对象类型,后一个指定该领域对象的Id类型。

Hypermedia REST

简单说两句Hypermedia是什么。简单来说它是可以让客户端清晰的知道自己可以做什么,而无需依赖服务器端指示你做什么。原理呢,也很简单,通过返回的结果中包括不仅是数据本身,也包括指向相关资源的链接。拿上面的例子来说(虽然这种默认状态生成的东西不是很有代表性):links中有一个profiles,我们看看这个profile的链接 http://localhost:8080/profile/todo 执行的结果是什么:

{
 "alps" : {
 "version" : "1.0",
 "descriptors" : [
  {
   "id" : "todo-representation",
   "href" : "http://localhost:8080/profile/todo",
   "descriptors" : [
    {
    "name" : "desc",
    "type" : "SEMANTIC"
    },
    {
    "name" : "completed",
    "type" : "SEMANTIC"
    }
   ]
  },
  {
   "id" : "create-todo",
   "name" : "todo",
   "type" : "UNSAFE",
   "rt" : "#todo-representation"
  },
  {
   "id" : "get-todo",
   "name" : "todo",
   "type" : "SAFE",
   "rt" : "#todo-representation",
   "descriptors" : [
    {
    "name" : "page",
    "doc" : {
     "value" : "The page to return.",
     "format" : "TEXT"
    },
    "type" : "SEMANTIC"
    },
    {
    "name" : "size",
    "doc" : {
     "value" : "The size of the page to return.",
     "format" : "TEXT"
    },
    "type" : "SEMANTIC"
    },
    {
    "name" : "sort",
    "doc" : {
     "value" : "The sorting criteria to use to calculate the content of the page.",
     "format" : "TEXT"
    },
    "type" : "SEMANTIC"
    }
   ]
  },
  {
   "id" : "patch-todo",
   "name" : "todo",
   "type" : "UNSAFE",
   "rt" : "#todo-representation"
  },
  {
   "id" : "update-todo",
   "name" : "todo",
   "type" : "IDEMPOTENT",
   "rt" : "#todo-representation"
  },
  {
   "id" : "delete-todo",
   "name" : "todo",
   "type" : "IDEMPOTENT",
   "rt" : "#todo-representation"
  },
  {
   "id" : "get-todo",
   "name" : "todo",
   "type" : "SAFE",
   "rt" : "#todo-representation"
  }
 ]
 }
}

这个对象虽然我们暂时不是完全的理解,但大致可以猜出来,这个是todo这个REST API的元数据描述,告诉我们这个API中定义了哪些操作和接受哪些参数等等。我们可以看到todo这个API有增删改查等对应功能。

其实呢,Spring是使用了一个叫 ALPS (http://alps.io/spec/index.html) 的专门描述应用语义的数据格式。摘出下面这一小段来分析一下,这个描述了一个get方法,类型是 SAFE 表明这个操作不会对系统状态产生影响(因为只是查询),而且这个操作返回的结果格式定义在 todo-representation 中了。 todo-representation

{
 "id" : "get-todo",
 "name" : "todo",
 "type" : "SAFE",
 "rt" : "#todo-representation"
}

还是不太理解?没关系,我们再来做一个实验,启动 PostMan (不知道的同学,可以去Chrome应用商店中搜索下载)。我们用Postman构建一个POST请求:

用Postman构建一个POST请求添加一个Todo

执行后的结果如下,我们可以看到返回的links中包括了刚刚新增的Todo的link http://localhost:8080/todo/588a01abc5d0e23873d4c1b8 ( 588a01abc5d0e23873d4c1b8 就是数据库自动为这个Todo生成的Id),这样客户端可以方便的知道指向刚刚生成的Todo的API链接。

执行添加Todo后的返回Json数据

再举一个现实一些的例子,我们在开发一个“我的”页面时,一般情况下除了取得我的某些信息之外,因为在这个页面还会有一些可以链接到更具体信息的页面链接。如果客户端在取得比较概要信息的同时就得到这些详情的链接,那么客户端的开发就比较简单了,而且也更灵活了。

其实这个描述中还告诉我们一些分页的信息,比如每页20条记录(size: 20)、总共几页(totalPages:1)、总共多少个元素(totalElements: 1)、当前第几页(number: 0)。当然你也可以在发送API请求时,指定page、size或sort参数。比如 http://localhost:8080/todos?page=0&size=10 就是指定每页10条,当前页是第一页(从0开始)。

魔法的背后

这么简单就生成一个有数据库支持的REST API,这件事看起来比较魔幻,但一般这么魔幻的事情总感觉不太托底,除非我们知道背后的原理是什么。首先再来回顾一下 TodoRepository 的代码:

@RepositoryRestResource(collectionResourceRel = "todo", path = "todo")
public interface TodoRepository extends MongoRepository<Todo, String>{
}

Spring是最早的几个IoC(控制反转或者叫DI)框架之一,所以最擅长的就是依赖的注入了。这里我们写了一个Interface,可以猜到Spring一定是有一个这个接口的实现在运行时注入了进去。如果我们去 spring-data-mongodb 的源码中看一下就知道是怎么回事了,这里只举一个小例子,大家可以去看一下 SimpleMongoRepository.java ( 源码链接 ),由于源码太长,我只截取一部分:

public class SimpleMongoRepository<T, ID extends Serializable> implements MongoRepository<T, ID> {
 private final MongoOperations mongoOperations;
 private final MongoEntityInformation<T, ID> entityInformation;
 /**
  * Creates a new {@link SimpleMongoRepository} for the given {@link MongoEntityInformation} and {@link MongoTemplate}.
  *
  * @param metadata must not be {@literal null}.
  * @param mongoOperations must not be {@literal null}.
  */
 public SimpleMongoRepository(MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) {
  Assert.notNull(mongoOperations);
  Assert.notNull(metadata);
  this.entityInformation = metadata;
  this.mongoOperations = mongoOperations;
 }
 /*
  * (non-Javadoc)
  * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
  */
 public <S extends T> S save(S entity) {
  Assert.notNull(entity, "Entity must not be null!");
  if (entityInformation.isNew(entity)) {
   mongoOperations.insert(entity, entityInformation.getCollectionName());
  } else {
   mongoOperations.save(entity, entityInformation.getCollectionName());
  }
  return entity;
 }
 ...
 public T findOne(ID id) {
  Assert.notNull(id, "The given id must not be null!");
  return mongoOperations.findById(id, entityInformation.getJavaType(), entityInformation.getCollectionName());
 }
 private Query getIdQuery(Object id) {
  return new Query(getIdCriteria(id));
 }
 private Criteria getIdCriteria(Object id) {
  return where(entityInformation.getIdAttribute()).is(id);
 }
 ...
}

也就是说其实在运行时Spring将这个类或者其他具体接口的实现类注入了应用。这个类中有支持各种数据库的操作。我了解到这步就觉得ok了,有兴趣的同学可以继续深入研究。

虽然不想在具体类上继续研究,但我们还是应该多了解一些关于 MongoRepository 的东西。这个接口继承了 PagingAndSortingRepository (定义了排序和分页) 和 QueryByExampleExecutor。而 PagingAndSortingRepository 又继承了 CrudRepository (定义了增删改查等)。

第二个魔法就是 @RepositoryRestResource(collectionResourceRel = "todo", path = "todo") 这个元数据的修饰了,它直接对MongoDB中的集合(本例中的todo)映射到了一个REST URI(todo)。因此我们连Controller都没写就把API搞出来了,而且还是个Hypermedia REST。

其实呢,这个第二个魔法只在你需要变更映射路径时需要。本例中如果我们不加 @RepositoryRestResource 这个修饰符的话,同样也可以生成API,只不过其路径按照默认的方式变成了 todoes ,大家可以试试把这个元数据修饰去掉,然后重启服务,访问 http://localhost:8080/todoes 看看。

说到这里,顺便说一下REST的一些约定俗成的规矩。一般来说如果我们定义了一个领域对象 (比如我们这里的Todo),那么这个对象的集合(比如Todo的列表)可以使用这个对象的命名的复数方式定义其资源URL,也就是刚刚我们访问的 http://localhost:8080/todoes,对于新增一个对象的操作也是这个URL,但Request的方法是POST。

而这个某个指定的对象(比如指定了某个ID的Todo)可以使用 todoes/:id 来访问,比如本例中 http://localhost:8080/todoes/588a01abc5d0e23873d4c1b8。对于这个对象的修改和删除使用的也是这个URL,只不过HTTP Request的方法变成了PUT(或者PATCH)和DELETE。

这个里面默认采用的这个命名 todoes 是根据英语的语法来的,一般来说复数是加s即可,但这个todo,是辅音+o结尾,所以采用的加es方式。 todo 其实并不是一个真正意义上的单词,所以我认为更合理的命名方式应该是 todos。所以我们还是改成 @RepositoryRestResource(collectionResourceRel = "todos", path = "todos")

无招胜有招

刚才我们提到的都是开箱即用的一些方法,你可能会想,这些东西看上去很炫,但没有毛用,实际开发过程中,我们要使用的肯定不是这么简单的增删改查啊。说的有道理,我们来试试看非默认方法。那么我们就来增加一个需求,我们可以通过查询Todo的描述中的关键字来搜索符合的项目。

显然这个查询不是默认的操作,那么这个需求在Spring Boot中怎么实现呢?非常简单,只需在 TodoRepository 中添加一个方法:

...
public interface TodoRepository extends MongoRepository<Todo, String>{
    List<Todo> findByDescLike(@Param("desc") String desc);
}

太不可思议了,这样就行?不信可以启动服务后,在浏览器中输入 http://localhost:8080/todos/search/findByDescLike?desc=swim 去看看结果。是的,我们甚至都没有写这个方法的实现就已经完成了该需求(题外话,其实 http://localhost:8080/todos?desc=swim 这个URL也起作用)。

你说这里肯定有鬼,我同意。那么我们试试把这个方法改个名字 findDescLike ,果然不好用了。为什么呢?这套神奇的疗法的背后还是那个我们在第一篇时提到的 Convention over configuration,要神奇的疗效就得遵循Spring的配方。这个配方就是方法的命名是有讲究的:Spring提供了一套可以通过命名规则进行查询构建的机制。这套机制会把方法名首先过滤一些关键字,比如 find…By, read…By, query…By, count…By 和 get…By 。系统会根据关键字将命名解析成2个子语句,第一个 By 是区分这两个子语句的关键词。这个 By 之前的子语句是查询子语句(指明返回要查询的对象),后面的部分是条件子语句。如果直接就是 findBy… 返回的就是定义Respository时指定的领域对象集合(本例中的Todo组成的集合)。

一般到这里,有的同学可能会问 find…By, read…By, query…By, get…By 到底有什么区别啊?答案是。。。木有区别,就是别名,从下面的定义可以看到这几个东东其实生成的查询是一样的,这种让你不用查文档都可以写对的方式也比较贴近目前流行的自然语言描述风格(类似各种DSL)。

private static final String QUERY_PATTERN = "find|read|get|query|stream";

刚刚我们实验了模糊查询,那如果要是精确查找怎么做呢,比如我们要筛选出已完成或未完成的Todo,也很简单:

 List<Todo> findByCompleted(@Param("completed") boolean completed);

嵌套对象的查询怎么搞?

看到这里你会问,这都是简单类型,如果复杂类型怎么办?嗯,好的,我们还是增加一个需求看一下:现在需求是要这个API是多用户的,每个用户看到的Todo都是他们自己创建的项目。我们新建一个User领域对象:

package dev.local.user;
import org.springframework.data.annotation.Id;
public class User {
 @Id private String id;
 private String username;
 private String email;
 //此处为节省篇幅省略属性的getter和setter
}

为了可以添加User数据,我们需要一个User的REST API,所以添加一个 UserRepository

package dev.local.user;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface UserRepository extends MongoRepository<User, String> {
}

然后给 Todo 领域对象添加一个User属性。

package dev.local.todo;
//省略import部分
public class Todo {
 //省略其他部分
 private User user;
 public User getUser() {
  return user;
 }
 public void setUser(User user) {
  this.user = user;
 }
}

接下来就可以来把 TodoRepository 添加一个方法定义了,我们先实验一个简单点的,根据用户的email来筛选出这个用户的Todo列表:

public interface TodoRepository extends MongoRepository<Todo, String>{
 List<Todo> findByUserEmail(@Param("userEmail") String userEmail);
}

现在需要构造一些数据了,你可以通过刚刚我们建立的API使用Postman工具来构造:我们这里创建了2个用户,以及一些Todo项目,分别属于这两个用户,而且有部分项目的描述是一样的。接下来就可以实验一下了,我们在浏览器中输入 http://localhost:8080/todos/search/findByUserEmail?userEmail=peng@gmail.com ,我们会发现返回的结果中只有这个用户的Todo项目。

{
 "_embedded" : {
 "todos" : [ {
  "desc" : "go swimming",
  "completed" : false,
  "user" : {
  "username" : "peng",
  "email" : "peng@gmail.com"
  },
  "_links" : {
  "self" : {
   "href" : "http://localhost:8080/todos/58908a92c5d0e2524e24545a"
  },
  "todo" : {
   "href" : "http://localhost:8080/todos/58908a92c5d0e2524e24545a"
  }
  }
 }, {
  "desc" : "go for walk",
  "completed" : false,
  "user" : {
  "username" : "peng",
  "email" : "peng@gmail.com"
  },
  "_links" : {
  "self" : {
   "href" : "http://localhost:8080/todos/58908aa1c5d0e2524e24545b"
  },
  "todo" : {
   "href" : "http://localhost:8080/todos/58908aa1c5d0e2524e24545b"
  }
  }
 }, {
  "desc" : "have lunch",
  "completed" : false,
  "user" : {
  "username" : "peng",
  "email" : "peng@gmail.com"
  },
  "_links" : {
  "self" : {
   "href" : "http://localhost:8080/todos/58908ab6c5d0e2524e24545c"
  },
  "todo" : {
   "href" : "http://localhost:8080/todos/58908ab6c5d0e2524e24545c"
  }
  }
 }, {
  "desc" : "have dinner",
  "completed" : false,
  "user" : {
  "username" : "peng",
  "email" : "peng@gmail.com"
  },
  "_links" : {
  "self" : {
   "href" : "http://localhost:8080/todos/58908abdc5d0e2524e24545d"
  },
  "todo" : {
   "href" : "http://localhost:8080/todos/58908abdc5d0e2524e24545d"
  }
  }
 } ]
 },
 "_links" : {
 "self" : {
  "href" : "http://localhost:8080/todos/search/findByUserEmail?userEmail=peng@gmail.com"
 }
 }
}

看到结果后我们来分析这个 findByUserEmail 是如何解析的:首先在 By 之后,解析器会按照 camel (每个单词首字母大写)的规则来分词。那么第一个词是 User,这个属性在 Todo 中有没有呢?有的,但是这个属性是另一个对象类型,所以紧跟着这个词的 Email 就要在 User 类中去查找是否有 Email 这个属性。聪明如你,肯定会想到,那如果在 Todo 类中如果还有一个属性叫 userEmail 怎么办?是的,这种情况下 userEmail 会被优先匹配,此时请使用 _ 来显性分词处理这种混淆。也就是说如果我们的 Todo 类中同时有 user 和 userEmail 两个属性的情况下,我们如果想要指定的是 user 的 email ,那么需要写成 findByUser_Email。

还有一个问题,我估计很多同学现在已经在想了,那就是我们的这个例子中并没有使用 user 的 id,这不科学啊。是的,之所以没有在上面使用 findByUserId 是因为要引出一个易错的地方,下面我们来试试看,将 TodoRepository 的方法改成

public interface TodoRepository extends MongoRepository<Todo, String>{
 List<Todo> findByUserId(@Param("userId") String userId);
}

你如果打开浏览器输入 http://localhost:8080/todos/search/findByUserId?userId=589089c3c5d0e2524e245458 (这里的Id请改成你自己mongodb中的user的id),你会发现返回的结果是个空数组。原因是虽然我们在类中标识 id 为 String 类型,但对于这种数据库自己生成维护的字段,它在MongoDB中的类型是ObjectId,所以在我们的接口定义的查询函数中应该标识这个参数是 ObjectId。那么我们只需要改动 userId 的类型为 org.bson.types.ObjectId 即可。

package dev.local.todo;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import java.util.List;
@RepositoryRestResource(collectionResourceRel = "todos", path = "todos")
public interface TodoRepository extends MongoRepository<Todo, String>{
 List<Todo> findByUserId(@Param("userId") ObjectId userId);
}

再复杂一些行不行?

好吧,到现在我估计还有一大波攻城狮表示不服,实际开发中需要的查询比上面的要复杂的多,再复杂一些怎么办?还是用例子来说话吧,那么现在我们想要模糊搜索指定用户的Todo中描述的关键字,返回匹配的集合。这个需求我们只需改动一行,这个以命名规则为基础的查询条件是可以加 And、Or 这种关联多个条件的关键字的。

List<Todo> findByUserIdAndDescLike(@Param("userId") ObjectId userId, @Param("desc") String desc);

当然,还有其他操作符:Between (值在两者之间), LessThan (小于), GreaterThan (大于), Like (包含), IgnoreCase (b忽略大小写), AllIgnoreCase (对于多个参数全部忽略大小写), OrderBy (引导排序子语句), Asc (升序,仅在 OrderBy 后有效) 和 Desc (降序,仅在 OrderBy 后有效)。

刚刚我们谈到的都是对于查询条件子语句的构建,其实在 By 之前,对于要查询的对象也可以有限定的修饰词 Distinct (去重,如有重复取一个值)。比如有可能返回的结果有重复的记录,可以使用 findDistinctTodoByUserIdAndDescLike。

我可以直接写查询语句吗?几乎所有码农都会问的问题。当然可以咯,也是同样简单,就是给方法加上一个元数据修饰符 @Query

public interface TodoRepository extends MongoRepository<Todo, String>{
 @Query("{ 'user._id': ?0, 'desc': { '$regex': ?1} }")
 List<Todo> searchTodos(@Param("userId") ObjectId userId, @Param("desc") String desc);
}

采用这种方式我们就不用按照命名规则起方法名了,可以直接使用MongoDB的查询进行。上面的例子中有几个地方需要说明一下

?0 和 ?1 是参数的占位符,?0 表示第一个参数,也就是 userId;?1 表示第二个参数也就是 desc。

使用user._id 而不是 user.id 是因为所有被 @Id 修饰的属性在Spring Data中都会被转换成 _id

MongoDB中没有关系型数据库的Like关键字,需要以正则表达式的方式达成类似的功能。

其实,这种支持的力度已经可以让我们写出相对较复杂的查询了。但肯定还是不够的,对于开发人员来讲,如果不给可以自定义的方式基本没人会用的,因为总有这样那样的原因会导致我们希望能完全掌控我们的查询或存储过程。但这个话题展开感觉就内容更多了,后面再讲吧。

本章代码:https://github.com/wpcfan/spring-boot-tut/tree/chap02

(0)

相关推荐

  • 详解springboot整合mongodb

    这篇文章主要介绍springboot如何整合MongoDB. 准备工作 安装 MongoDB jdk 1.8 maven 3.0 idea 环境依赖 在pom文件引入spring-boot-starter-data-mongodb依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifa

  • spring boot项目中MongoDB的使用方法

    前言 大家都知道MySQL数据库很好用,但数据量到了千万以上了,想增加字段是非常痛苦的,这个在MongoDB里就不存在,字段想怎么加就怎么加,所以也就有了想在spring-boot里用MongoDB的想法了,Github上spring-projects里有关于使用MongoDB的demo,后面会给出链接 依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring

  • Spring boot中mongodb的使用

    MongoDB是最早热门非关系数据库的之一,使用也比较普遍,一般会用做离线数据分析来使用,放到内网的居多.由于很多公司使用了云服务,服务器默认都开放了外网地址,导致前一阵子大批 MongoDB 因配置漏洞被攻击,数据被删,引起了人们的注意,感兴趣的可以看看这篇文章:场屠戮MongoDB的盛宴反思:超33000个数据库遭遇入侵勒索,同时也说明了很多公司生产中大量使用mongodb. mongodb简介 MongoDB(来自于英文单词"Humongous",中文含义为"庞大&qu

  • 利用Spring Boot操作MongoDB的方法教程

    MongoDB MongoDB作为一种NoSQL数据库产品,其实已经非常著名了.去年,由于MongoDB安全认证的薄弱,上万家公司中招.虽然是一则负面新闻,但是也从侧面说明了MongoDB的流行程度. 下图是DB-Engines统计的2017年5月全球数据库引擎使用排名.从图中可以看出,mongoDB位列总榜第五,非关系数据库第一,非常靠前的排名. 我个人对mongoDB并不是非常熟悉,但是经过一段时间的了解,对mongoDB的特性还是有了一些简单的理解,这里记录一二. 首先,mongoDB作为

  • Spring Boot无缝集成MongoDB

    上一节,我们做的那个例子有点太简单了,通常的后台都会涉及一些数据库的操作,然后在暴露的API中提供处理后的数据给客户端使用.那么这一节我们要做的是集成MongoDB ( https://www.mongodb.com ). MongoDB是什么? MongoDB是一个NoSQL数据库,是NoSQL中的一个分支:文档数据库.和传统的关系型数据库比如Oracle.SQLServer和MySQL等有很大的不同.传统的关系型数据库(RDBMS)已经成为数据库的代名词超过20多年了.对于大多数开发者来说,

  • Spring Boot中使用MongoDB数据库的方法

    MongoDB数据库简介 简介 MongoDB是一个高性能,开源,无模式的,基于分布式文件存储的文档型数据库,由C++语言编写,其名称来源取自"humongous",是一种开源的文档数据库──NoSql数据库的一种.NoSql,全称是 Not Only Sql,指的是非关系型的数据库. 特点 MongoDB数据库的特点是高性能.易部署.易使用,存储数据非常方便.主要功能特性有: * 面向集合存储,易存储对象类型的数据. * 模式自由. * 支持动态查询. * 支持完全索引,包含内部对象

  • 详解Spring Boot工程集成全局唯一ID生成器 UidGenerator的操作步骤

    Spring Boot中全局唯一流水号ID生成器集成实验 概述 流水号生成器(全局唯一 ID生成器)是服务化系统的基础设施,其在保障系统的正确运行和高可用方面发挥着重要作用.而关于流水号生成算法首屈一指的当属 Snowflake 雪花算法,然而 Snowflake本身很难在现实项目中直接使用,因此实际应用时需要一种可落地的方案. UidGenerator 由百度开发,是Java实现的, 基于 Snowflake算法的唯一ID生成器.UidGenerator以组件形式工作在应用项目中, 支持自定义

  • Spring Boot如何集成模板引擎FreeMarker

    一.Java模板引擎 模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档. 在java中,主要的模板引擎有JSP.Thymeleaf.FreeMarker.Velocity等. 虽然随着前后端分离的崛起和流行,模板引擎已遭受到冷落,但不少旧项目依然使用java的模板引擎渲染界面,而偶尔自己写一些练手项目,使用模板引擎也比起前后端分离要来的快速. 本系列会分别讲解SpringBoot

  • Spring Boot 快速集成 Redis的方法

    Spring Boot 如何快速集成 Redis?没错,栈长本文教你,让大家少走弯路! 添加依赖 使用像 Redis 这类的 NoSQL 数据库就必须要依赖 spring-data-redis 这样的能力包,开箱即用,Spring Boot 中都封装好了: 引入spring-boot-starter-data-redis: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>

  • Spring Boot项目集成UidGenerato的方法步骤

    前言 UidGenerato 基于snowflake算法实现 UidGenerato 由百度开发,基于SnowFlake算法的唯一ID生成器.UidGenerato 已组件的形式工作在应用项目中,支持自定义workeid位数和初始化策略,从而适用docker等虚拟化环境下实例自动重启等场景. 准备一个maven项目,构建两个模块.分别作为使用方和提供方.(建两个模块主要是为了"造轮子",其他模块或项目可以直接引用,无需关心uid配置,如果没有分模块,可以指忽略构建两个模块) 下载uid

  • Spring Boot 项目集成Redis的方式详解

    集成方式 使用Jedis Jedis是Redis官方推荐的面向Java的操作Redis的客户端,是对服务端直连后进行操作.如果直接使用Jedis进行连接,多线程环境下是非线程安全的,正式生产环境一般使用连接池进行连接. <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> &

  • Spring Boot项目集成Knife4j接口文档的实例代码

    目录 1.在pom.xml引入依赖包 2.创建Knife4j配置文件 3.使用Knife4j注解 4.全局参数 Knife4j就相当于是swagger的升级版,对于我来说,它比swagger要好用得多 1.在pom.xml引入依赖包 <!-- Swagger配置依赖knife4j --> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-b

  • Spring Boot Redis 集成配置详解

    spring Boot 熟悉后,集成一个外部扩展是一件很容易的事,集成Redis也很简单,看下面步骤配置: 一.添加pom依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency> 二.创建 RedisClient.java 注意该类存放的pack

  • Spring Boot中使用MongoDB的连接池配置的方法

    因为今天开发遇到了性能问题,可能与MongoDB的连接有关,所以稍稍深入看了一下,正好搜到原来有人写过这篇相关的内容,所以转载过来.回头有时间可以写个扩展到SpringForAll里,主体思路还是一样的.感谢这位美女程序媛的文章! 说明 Spring Boot中通过依赖 spring-boot-starter-data-mongodb ,来实现 spring-data-mongodb 的自动配置. 但是默认情况下,Spring Boot 中,并没有像使用MySQL或者Redis一样,提供了连接池

随机推荐