我们在企业开发中,最常用的都是关系型数据库(oracle、mysql、sqlserver等),这类基于jdbc的数据交互方式,通过在spring中整合mybatis就能实现,本系列文章《Spring系列-实战篇(4)-你有多了解MyBatis 》中就有讲解。
但对于非关系型数据库,就是常说nosql数据库,是没办法直接用mybatis的,因为它们连sql都没有,jdbc的本质就是一种用于执行sql语句的java api。本文就是以nosql数据库中的一员,mongodb为例。讲解mongodb数据库的特点,以及如何在springboot中实现常用的增删改查。
mongodb是面向文档的非关系型数据库,放弃关系模型的主要原因就是为了获得更加方便的扩展性,当然优点不仅如此。
丰富的数据模型:文档的键(对应关系数据库中:表的字段)不会事先定义也不会固定不变,开发灵活。字段值的类型丰富,可以包含其他文档,数组及文档数组,所以用一条记录就可以表示非常复杂的层次关系。
容易扩展:mongodb从最初设计的时候就考虑到了扩展的问题。它所采用的面向文档的数据模型使其可以自动在多台服务器之间分割数据。它还可以平衡集群的数据和负载,自动重排文档。
功能丰富:有很多强大的辅助工具,让mongodb的功能更加强大。(1)可以不用再在数据库写存储过程了,允许在服务端执行脚本,可以用Javascript编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。(2)大数据应用的支撑也是一大特色,支撑mapreduce和其他聚合工具。
对应第一次接触mongodb的朋友,我还是先列出 mysql和mongodb 数据库的基本概念对比表吧。
| mysql概念| mongo概念|
| ---- | ---- |
| table (表)| collection (集合)|
| row (记录行)| document (文档)|
|column (数据字段)|field (域)|
|index (索引)|index (索引)|
|primary key (主键)|自动将_id字段设置为主键|
|table joins (表连接)|不支持|
上节说到,mongodb是为分布式扩展而设计的,在搭建mongo集群时要考虑很多配置。但是这并非本文要介绍的内容,本文还是主要以 springboot+mongodb 的应用为主。我们就易于容器环境,最简单的启动一个mongodb数据库。
docker run --name domain-mongodb -v /u01/mongo/data/db:/data/db -p 27017:27017 -d mongo
按照上面的命令,就启动好了一个mongodb,对外映射的端口是 27017。
就像我们在用oracle数据库,习惯用sql/plsql developer客户端;mysql数据库,习惯用navicat客户端;mongodb,我推荐用 robo 3T 。robo 3T是免费的,在下载页面还有个studio 3T 的软件,那个是商业收费的,当然功能更加丰富。
安装完成并启动后,连接服务器上的mongodb。就像我们在mysql数据库里面,先创建数据库,再创建表一样。我们先创建集合,再创建文档,注意,mongodb不需要预设表结构,所以只要填文档的名字就行。如下图,创建了 portal集合下的三个文档,分别是oauth_client、oauth_config、user。
接下来会以在springboot中,实现对用户信息的操作接口为例,讲解如何实现对mongodb数据最简单的增删改查。
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> <version>2.0.3.RELEASE</version> </dependency>
application.yml
spring: data: mongodb: uri: mongodb://ip:port/集合名
先创建一个pojo类,UserEO.java,对应之前mongodb里面的创建的user文档,这里有几个知识点。
@Document(collection = "user") public class UserEO { private String username; private String password; private String name; /** * 创建时间 */ @JsonFormat(pattern="yyyy.MM.dd HH:mm:ss",timezone = "GMT+8") @Field("creation_date") private Date creationDate; /** * UserBookEO类:name,price,amount */ private List<UserBookEO> bookList; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getCreationDate() { return creationDate; } public void setCreationDate(Date creationDate) { this.creationDate = creationDate; } public List<UserBookEO> getBookList() { return bookList; } public void setBookList(List<UserBookEO> bookList) { this.bookList = bookList; } }
dao 层的操作主要依赖于MongoTemplate 的实现,下列代码中已经列出最常用的增删改成以及分页查询,更多的高级用法,可以去官网看MongoTemplate的源码实现。
@Component public class PaasDao { @Autowired private MongoTemplate mongoTemplate; /** * 分页查询user * * @param key * @return */ public List<UserEO> queryUsers(String key, int page, int pageSize) { Query query = new Query(); Criteria criteriaUsername = Criteria.where("username").regex(key); Criteria criteriaName = Criteria.where("name").regex(key); query.addCriteria(new Criteria().orOperator(criteriaUsername, criteriaName)); //分页 Sort sort=new Sort(Sort.Direction.DESC, "creation_date"); Pageable pageable=PageRequest.of(page-1,pageSize,sort); query.with(pageable); return mongoTemplate.find(query, UserEO.class); } /** * 删除client * * @param username */ public void deleteUser(String username) { Query query = new Query(Criteria.where("username").is(username)); mongoTemplate.remove(query, UserEO.class); } /** * 新增client * * @param user */ public void addUser(UserEO user) { user.setCreationDate(new Date()); mongoTemplate.save(user); } /** * 更新user * * @param user */ public void updateUser(UserEO user) { Query query = new Query(Criteria.where("username").is(user.getUsername())); Update update = new Update().set("password", user.getPassword()) .set("name",user.getName()); mongoTemplate.updateFirst(query, update, UserEO.class); } /** * 查询存在某username的数量 * * @param username * @return */ public long countUsername(String username) { Query query = new Query(Criteria.where("username").is(username)); return mongoTemplate.count(query, UserEO.class); } }
controller层就没有太多可展示的了,我就简单的拿新增和查询两个接口来做个测试吧。
@RestController @RequestMapping(value = "/paas") public class PaasController { @Autowired private PaasDao paasDao; /** * 用户(user)- 查询用户 * * @param request * @return */ @RequestMapping(value = "/user/queryUsers", method = RequestMethod.POST) public Response queryUsers(@RequestBody Map<String, Object> request) { Integer page = (Integer) request.get("page"); Integer pageSize = (Integer) request.get("pageSize"); String key = request.get("key") == null ? "" : (String) request.get("key"); List<UserEO> userEOList=paasDao.queryUsers(key,page,pageSize); long userCount=paasDao.countUsers(key); PageQueryResult<UserEO> pageQueryResult = new PageQueryResult<>(userCount,userEOList); return Response.ok().data(pageQueryResult); } /** * 用户(user)- 新增用户 * * @param userEO * @return */ @RequestMapping(value = "/user/addUser", method = RequestMethod.POST) public Response addUser(@RequestBody UserEO userEO) { paasDao.addUser(userEO); return Response.ok(); }
通过新增接口,我们分别传入两组数据:
//第一组数据 { "username":"kerry", "password":"kerry", "name":"吴晨瑞" } //第二组数据 { "username":"tony", "password":"tony", "name":"托尼", "bookList":[ { "name":"红楼梦", "price":20, "amount":1 }, { "name":"三个火枪手", "price":10, "amount":2 } ] }
插入成功后,我们通过robo 3T 查询一下数据库,验证数据确实插入成功。
接下来再调用查询接口,返回结果如下:
{ "code":"ok", "data":{ "count":2, "result":[ { "username":"tony", "password":"tony", "name":"托尼", "creationDate":"2019.11.17 16:29:34", "bookList":[ { "name":"红楼梦", "price":20, "amount":1 }, { "name":"三个火枪手", "price":10, "amount":2 } ] }, { "username":"kerry", "password":"kerry", "name":"吴晨瑞", "creationDate":"2019.10.30 10:53:55" } ] }, "requestid":"e80b71d50c9e45e9bffa3bd0b2abf446" }
OK,在springboot框架下,基于mongodb的接口开发,看上去并不困难。