最近学习Mongodb,作为一款最像关系型数据库的非关系型数据库(很绕叭hhh),它的功能强大,能够很好地支持记录数据庞大的文档型数据,例如博客、评论这种内容有可能特别长的的数据。Mysql当然也有支持存储长类型文档的数据类型:Blob和Text,但是这并不是最好的选择,现在项目中多种数据库整合使用也是一种趋势,因此,我尝试使用目前最流行的框架+Mongodb实现一次整合以完成对前段时间学习的一次小复习和检验。
PS:只针对简单的增删改查的实现哦~
好的内容整合需要贴切实际的数据,我们就拿常见的博客来实现一次整合,将博客的作者、标题、正文内容、评论、点赞数和发布时间作为我们需要存取和读取的数据.
想好想要存取的数据和数据类型,我们也就可以写出相对应的实体类了
package com.hang.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Blog {
private String id;
private String author;
private String title;
private String content;
private String communication;
private int likeNumbers;
private Date commitTime;
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-mongodbartifactId>
dependency>
#所选取的Mongodb数据库,前提是你的数据库必须创建并且存入内存(没有集合只会在缓存中)
spring.data.mongodb.database = users
#由于我是本地,因此连本地机就行了
spring.data.mongodb.host=127.0.0.1
spring.data.mongodb.port=27017
#没有设置密码,因此为空
spring.data.mongodb.password=
在测试类中测试一下可不可以成功连接Mongodb
package com.hang;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class Springboot09MongodbApplicationTests {
@Test
void contextLoads() {
MongoClient mongoClient=null;
try {
mongoClient = MongoClients.create("mongodb://127.0.0.1:27017");
MongoDatabase users = mongoClient.getDatabase("users");
MongoCollection<Document> test = users.getCollection("test");
test.insertOne(new Document("name","小红"));
System.out.println("数据库连接成功,并成功插入数据");
}catch (Exception e){
e.printStackTrace();
}finally {
mongoClient.close();
}
}
}
本次虽然是小项目,但是也严格按照dao-service-controller三层逻辑进行编写测试,由于实体层在设计之初就已经编写了,因此这里直接从数据层开始编写.
我们可以使用Mongodb.core包下已经编写好的MongoTemplate直接实现对数据层的增删改查,我们可以点进源码具体查看里面的方法:
增的方法有两个,一个是通过集合增加,一个可以通过泛型(对象)增加,我们不如使用save()方法.
删除的方法应该是remove()了,翻遍源码也就找到这一种删除的方法
修改的方法可以分成两种:
①修改第一个符合条件的文档
②修改所有符合条件的文档
查询的方法也有多种,这里只讲解两种;一种是find()方法,找符合条件的值,另一种是findAll()方法,可以查询集合中的所有文档.(还有findById等方法,可以自行读源码进行理解)
其实这些方法与我们之前接触的JdbcTemplate相类似,都是已经封装好的工具,可以直接进行使用,接下来我们就根据这些方法实现对Mongodb的数据写入和读取.
如果你熟悉了上面我所说的方法,那么现在直接"开箱"即用即可.
package com.hang.dao;
import com.hang.pojo.Blog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class BlogMapper {
@Autowired
private MongoTemplate mongoTemplate;
public void saveBlog(Blog blog){
mongoTemplate.save(blog);
}
public List<Blog> getAllBlog(){
return mongoTemplate.findAll(Blog.class);
}
public Blog getBlogById(Query query){ //根据查询语句来查询指定内容的博客
return mongoTemplate.findOne(query,Blog.class);
}
public void updateBlog(Query query, Update update){
mongoTemplate.updateMulti(query,update,Blog.class);
}
public void deleteBlog(Query query){
mongoTemplate.remove(query,Blog.class);
}
}
我们依旧按照规范,使用实现类去实现接口中的方法来编写Service层的代码
package com.hang.service;
import com.hang.pojo.Blog;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import java.util.List;
import java.util.Map;
public interface BlogService {
//获得所有的博客
public List<Blog> getAllBlog();
//获取指定内容的博客
public Blog getBlogById(String id);
//新增一篇博客
public void addBlog(Blog blog);
//修改一篇博客
public void updateBlog(String id, Map<String,Object> map);
//删除一篇博客
public void deleteBlog(String id);
}
数据该如何通过前端返回的数据进行查询、修改、增加等,具体的逻辑需要在这一层实现,我们很难从前端返回一个Query类型或者Update类型的数据,因此我们不妨返回需要修改哪条数据(_id)的哪些值(Map),然后在服务层封装Query和Update,再进行数据层的修改,这也是三层模型的优势所在.
package com.hang.service;
import com.hang.dao.BlogMapper;
import com.hang.pojo.Blog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service("blogService")
public class BlogServiceImpl implements BlogService{
@Autowired
BlogMapper mapper;
@Override
public List<Blog> getAllBlog() {
return mapper.getAllBlog();
}
@Override
public Blog getBlogById(String id) {
//传入ID然后获取指定的博客
Criteria criteriaId=Criteria.where("id").is(id);
Query query=new Query(criteriaId);
return mapper.getBlogById(query);
}
@Override
public void addBlog(Blog blog) {
mapper.saveBlog(blog);
}
@Override
public void updateBlog(String id, Map<String,Object> map) {
Criteria criteriaId=Criteria.where("id").is(id);
Query query=new Query(criteriaId);
Update update=new Update();
update.set("title",map.get("title"));
update.set("author",map.get("author"));
update.set("content",map.get("content"));
update.set("commitTime",map.get("commitTime"));
mapper.updateBlog(query,update);
}
@Override
public void deleteBlog(String id) {
Criteria criteriaId=Criteria.where("id").is(id);
Query query=new Query(criteriaId);
mapper.deleteBlog(query);
}
}
这一层没有啥特别介绍的,就是接收前端的数据并且实现页面跳转.
package com.hang.controller;
import com.hang.pojo.Blog;
import com.hang.service.BlogService;
import com.hang.util.BlogIDUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
public class BlogController {
@Autowired
private BlogService service;
@RequestMapping("/toBlog")
public String blogList(Model model){
List<Blog> blogs = service.getAllBlog();
model.addAttribute("blogs",blogs);
return "html/blog";
}
@GetMapping("/toAddBlog")
public String toAddPage(){
return "html/addBlog";
}
@PostMapping("/add")
public String addBlog(Blog blog){
/**添加操作
*
*/
String randomBlogId = new BlogIDUtils().getRandomBlogId();
blog.setId(randomBlogId);
blog.setLikeNumbers(0); //一开始点赞数为0
blog.setCommunication(""); //评论也为空
service.addBlog(blog);
return "redirect:/toBlog";
}
@GetMapping("/toUpdate/{id}")
public String toUpdatePage(Model model, @PathVariable("id") String id){
//根据前台传回的id添加到指定的模型
Blog blog=service.getBlogById(id);
model.addAttribute("blog",blog);
return "html/updateBlog";
}
@PostMapping("/update")
public String updateBlog(Map<String,Object> map,Blog blog){
map.put("author",blog.getAuthor());
map.put("title",blog.getTitle());
map.put("content",blog.getContent());
map.put("commitTime",blog.getCommitTime());
service.updateBlog(blog.getId(),map);
return "redirect:/toBlog";
}
@GetMapping("/toInfo/{id}")
public String toInfoPage(Model model,@PathVariable("id") String id){
Blog blog=service.getBlogById(id);
model.addAttribute("blog",blog);
return "html/info";
}
@RequestMapping("/delete/{id}")
public String deleteBlog(@PathVariable("id") String id){
service.deleteBlog(id);
return "redirect:/toBlog";
}
}
这里就只放最重要的两个前端页面,分别是博客管理页,新增博客页,这里的前端模板使用了b站狂神说的前端页面模板,并附上资源,需要的自取.
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Dashboard Template for Bootstraptitle>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/dashboard.css}" rel="stylesheet">
<style type="text/css">
/* Chart.js */
@-webkit-keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
@keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
.chartjs-render-monitor {
-webkit-animation: chartjs-render-animation 0.001s;
animation: chartjs-render-animation 0.001s;
}
style>
head>
<body>
<div th:insert="~{/common/commons::topbar}">div>
<div class="container-fluid">
<div class="row">
<div th:insert="~{/common/commons::sidebar(active='blog.html')}">div>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<h2>博客信息h2>
<a class="btn btn-sm btn-success" th:href="@{/toAddBlog}">添加a>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>博客作者th>
<th>博客标题th>
<th>博客内容th>
<th>点赞数th>
<th>发布时间th>
<th>操作th>
tr>
thead>
<tbody>
<tr th:each="blog:${blogs}">
<td th:text="${blog.getAuthor()}">td>
<td th:text="${blog.getTitle()}">td>
<td th:text="${blog.getContent().substring(0,10)}">td>
<td th:text="${blog.getLikeNumbers()}">td>
<td th:text="${#dates.format(blog.getCommitTime(),'yyyy-MM-dd HH:mm:ss')}">td>
<td>
<a class="btn btn-sm btn-success" th:href="@{/toInfo/{id}/(id=${blog.getId()})}">查看a>
<a class="btn btn-sm btn-primary" th:href="@{/toUpdate/{id}/(id=${blog.getId()})}">编辑a>
<a class="btn btn-sm btn-danger" th:href="@{/delete/{id}/(id=${blog.getId()})}" onclick="return confirm('确认删除');" title="删除">删除a>
td>
tr>
tbody>
table>
div>
main>
div>
div>
<script type="text/javascript" src="asserts/js/jquery-3.2.1.slim.min.js">script>
<script type="text/javascript" src="asserts/js/popper.min.js">script>
<script type="text/javascript" src="asserts/js/bootstrap.min.js">script>
<script type="text/javascript" src="asserts/js/feather.min.js">script>
<script>
feather.replace()
script>
<script type="text/javascript" src="asserts/js/Chart.min.js">script>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
datasets: [{
data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
lineTension: 0,
backgroundColor: 'transparent',
borderColor: '#007bff',
borderWidth: 4,
pointBackgroundColor: '#007bff'
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: false
}
}]
},
legend: {
display: false,
}
}
});
script>
body>
html>
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Dashboard Template for Bootstraptitle>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/dashboard.css}" rel="stylesheet">
<style type="text/css">
/* Chart.js */
@-webkit-keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
@keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
.chartjs-render-monitor {
-webkit-animation: chartjs-render-animation 0.001s;
animation: chartjs-render-animation 0.001s;
}
style>
head>
<body>
<div th:insert="~{/common/commons::topbar}">div>
<div class="container-fluid">
<div class="row">
<div th:replace="~{/common/commons::sidebar(active='blog.html')}">div>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<form th:action="@{/add}" method="post">
<div class="form-group">
<label>博客作者label>
<input type="text" name="author" class="form-control" placeholder="请输入作者名">
div>
<div class="form-group">
<label>博客标题label>
<input type="text" name="title" class="form-control" placeholder="请输入博客标题">
div>
<div class="form-group">
<label>博客内容label>
<textarea name="content" class="form-control" rows="20">textarea>
div>
<div class="form-group">
<label>发布时间label>
<input type="text" name="commitTime" class="form-control" placeholder="请输入发布日期">
div>
<button type="submit" class="btn btn-primary">发布button>
<button type="reset" class="btn btn-danger">清空button>
form>
main>
div>
div>
<script type="text/javascript" src="asserts/js/jquery-3.2.1.slim.min.js">script>
<script type="text/javascript" src="asserts/js/popper.min.js">script>
<script type="text/javascript" src="asserts/js/bootstrap.min.js">script>
<script type="text/javascript" src="asserts/js/feather.min.js">script>
<script>
feather.replace()
script>
<script type="text/javascript" src="asserts/js/Chart.min.js">script>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
datasets: [{
data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
lineTension: 0,
backgroundColor: 'transparent',
borderColor: '#007bff',
borderWidth: 4,
pointBackgroundColor: '#007bff'
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: false
}
}]
},
legend: {
display: false,
}
}
});
script>
body>
html>
其它页面就不放了,文章最后我会放项目文件链接,需要的可以自取~
在全局配置文件中配置指定数据库后,使用其Template创建文档时,会自动判断该集合(对应实体类名字)是否已存在,若不存在会自动创建这个集合~
我们点击添加按钮,模拟发布一篇博客
点击后,信息被成功添加到Mongodb并能读取
点击查看按钮即可查看已经发布的博客,页面简陋,且点赞功能暂时没有添加hh.
返回博客管理页面,点击编辑按钮即可实现对博客内容的修改
我们对部分信息进行修改.
这里就不予演示了,删除无非就是将id传入然后调用deleteBlog方法进行删除,UU们可以自行去尝试~
public void deleteBlog(String id) {
Criteria criteriaId=Criteria.where("id").is(id);
Query query=new Query(criteriaId);
mapper.deleteBlog(query);
}
细心的朋友注意到,我从来没有编写博客ID,但是实际中又有这个ID存在,这是因为我编写了一个博客ID的自动生成工具类,新增博客时随机生成一个ID赋予它即可~
package com.hang.util;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Random;
/***实现博客ID的自增,并且保证其不会重复出现
*
*/
public class BlogIDUtils {
public String getRandomBlogId(){
StringBuffer sb=new StringBuffer("lyh");
LocalDateTime ldt=LocalDateTime.now(); //获取当前的时间
DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
sb.append(dtf.format(ldt)); //连接当前时间
//如果同一时刻同一地点同一用户注册了账号,那么还会有一层随机数
sb.append("-");
sb.append(random());
return sb.toString();
}
public String random(){
Random random=new Random();
int randomInt = random.nextInt(100008);
return String.valueOf(randomInt);
}
}
项目中我也遇到了进入指定页面难的问题,因此重写视图配置页面的跳转,方便在进入项目根目录时就能跳转到我想要的页面
package com.hang.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/***拓展SpringMVC
*
*/
@Configuration
public class MvcConfig implements WebMvcConfigurer {
/***配置页面的基本跳转
*
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("dashboard");
registry.addViewController("/dashboard.html").setViewName("dashboard");
}
}
本次通过SpringBoot整合Mongodb并对其场景进行一次较为实际的应用,虽然只是实现较为简单的增删改查,却也是充满艰辛,希望能够有新的提升叭~
项目链接:
链接:https://pan.baidu.com/s/1mO4iZLwLeaXsYOwUYqsZkQ
提取码:java