Redis是一个能极大的提高互联网系统的性能,但是他有一些缺陷,其中一个就是计算功能十分有限,虽然可以通过Lua脚本去完善,但是这样对于开发者的工作量就大大增加了。对于需要缓存而且经常需要统计、分析和查询的数据,对于Redis这样简单的NoSQL就不是那么便捷了。而MongoDB对于那些需要统计、按条件查询和分析的数据,他提供了支持,它可以说是一个最接近关系数据库的NoSQL。
MongoDB是有C++语言编写的一种NoSQL,是一个基于分布式文件存储的开源数据库系统。在负载高时可以添加更多的节点,以保证服务器性能,MongoDB的目的是为web应用提供可扩展的高性能数据存储解决方案。MongoDB将数据存储为一个文档,数据结构由键值对组成,这里的MongoDB文档类似于JSON数据集,所以很容易转化为Java POJO对象或者JS对象,这些字段值还可以包含其他文档、数组以及文档数组
与Redis一样,Spring Boot的配置文件也提供了许多关于MongoDB的配置,首先引入SpringBoot关于MongoDB的starter以及fastjson的开发包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.39</version>
</dependency>
配置MongoDB
springboot关于MongoDB的默认配置:
spring.data.mongodb.authentication-database= #用于签名的MongoDB数据库
spring.data.mongodb.database= #数据库名称
spring.data.mongodb.field-naming-strategy= #使用字段名策略
spring.data.mongodb.grid-fs-database= #GridFs(网格文件)数据库名称
spring.data.mongodb.host= #MongoDB服务器,不能设置为uri
spring.data.mongodb.password= #MongoDB服务器密码,不能设置为URI
spring.data.mongodb.port= #MongoDB服务器端口,不能设置为URI
spring.data.mongodb.repositories.type= #是否启用MongoDB关于JPA规范的编程
spring.data.mongodb.uri= #MongoDB默认URI
spring.data.mongodb.username= #MongoDB服务器用户名,不能设置为URI
本人在测试时使用的配置:
spring.data.mongodb.host=192.168.1.103
spring.data.mongodb.port=27017
spring.data.mongodb.username=springboot
spring.data.mongodb.password=springboot
spring.data.mongodb.database=springboot
为了能够进行开发,还需要了解一些关于MongoDB的Bean(springboot会自动创建):
- MongoProperties:springboot关于MongoDB的自动配置属性
- MongoTemplate:MongoDB的操作模板,在spring中主要通过它对MongoDB进行操作
使用MongoTemplate实例
spring data MongoDB主要是通过mongoTemplate进行操作数据的。springboot会根据配置自动生成这个对象,我们只需拿来就行。
下面通过MongoTemplate来操作数据。
- 搭建开发环境
创建一个用户pojo:
package com.springboot.chapter08.pojo;
/**
* 用户
* @author Administrator
*/
@Document//标识MongoDB文档
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id //MongoDB文档编号,主键
private Integer id;
@Field("user_name") //在MongoDB中使用user_name保存属性
private String userName;
private String note;
private List<Role> roles; //角色列表
//get和set方法
@Override
public String toString() {
return "User [id=" + id + ", userName=" + userName + ", note=" + note + ", roles=" + roles + "]";
}
}
@Document标识为MongoDB文档存在,@id将对应的字段设置为主键,@Filed将属性与数据库字段对应起来。
package com.springboot.chapter08.pojo;
/**
* 角色
* @author Administrator
*/
@Document
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
@Field("role_name")
private String roleName;
private String note;
//get和set方法
@Override
public String toString() {
return "Role [id=" + id + ", roleName=" + roleName + ", note=" + note + "]";
}
}
用户控制器
package com.springboot.chapter08.controller;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
/**
* 获取用户
* @param id
* @return
*/
@RequestMapping("/get")
@ResponseBody
public User getUser(Integer id) {
User user = userService.getUser(id);
return user;
}
/**
* 保存(新增或跟新)用户
* @param user
* @return
*/
@RequestMapping("/save")
@ResponseBody
public User saveUser(@RequestBody User user) {
userService.saveUser(user);
return user;
}
/**
* 查询用户
* @param userName 用户名称
* @param note 备注
* @param skip 跳过用户个数
* @param limit 限制返回用户个数
* @return
*/
@RequestMapping("/find")
@ResponseBody
public List<User> findUser(String userName,String note,Integer skip,Integer limit){
List<User> userlist = userService.findUser(userName, note, skip, limit);
return userlist;
}
/**
* 更新用户部分属性
* @param id 用户编号
* @param userName 用户名称
* @param note 备注
* @return
*/
@RequestMapping("/update")
@ResponseBody
public UpdateResult updateUser(Integer id,String userName,String note) {
return userService.updateUser(id, userName, note);
}
/**
* 删除用户
* @param id 用户主键
* @return
*/
@RequestMapping("/delete")
@ResponseBody
public DeleteResult deleteUser(Integer id) {
return userService.deleteUser(id);
}
/**
* 测试插入一条数据
* @return
*/
@RequestMapping("/insert")
@ResponseBody
public User insert() {
return userService.insert();
}
/**
* 测试jpa接口,根据用户名模糊查询
* @param userName
* @return
*/
@RequestMapping("byname")
@ResponseBody
public List<User> findByUserNameLike(String userName){
return userService.findByUserNameLike(userName);
}
/*
* 测试自定义方法,根据ID或用户名查询
*/
@RequestMapping("/findOr")
@ResponseBody
public User findByUserByIdOrUserName(Integer id,String userName) {
return userService.findByUserByIdOrUserName(id, userName);
}
}
- 使用MongoTemplate操作文档
这里使用最为常用的方法,包括增删改查和分页等较为常用的功能。
用户服务接口:
package com.springboot.chapter08.service;
public interface UserService {
public void saveUser(User user);
public DeleteResult deleteUser(Integer id);
public List<User> findUser(String userName,String note,int skip,int limit);
public UpdateResult updateUser(Integer id,String userName,String note);
public User getUser(Integer id);
public User insert();
public List<User> findByUserNameLike(String userName);
public User findByUserByIdOrUserName(Integer id,String userName);
}
用户服务接口的实现
package com.springboot.chapter08.service.impl;
@Service
public class UserServiceImpl implements UserService{
//注入mongotemplate对象
@Autowired
private MongoTemplate mongoTemplate;
@Autowired
private UserDao userDao;
@Override
public void saveUser(User user) {
// TODO Auto-generated method stub
//使用名称为user文档保存用户信息
mongoTemplate.save(user, "user");
//如果文档采用类名首字母小写,则可以这样保存
//mongoTemplate.save(user);
}
@Override
public DeleteResult deleteUser(Integer id) {
// TODO Auto-generated method stub
//构件ID相等的条件
Criteria criteriaId = Criteria.where("id").is(id);
//查询对象
Query query = Query.query(criteriaId);
//删除用户
DeleteResult result = mongoTemplate.remove(query, User.class);
return result;
}
@Override
public List<User> findUser(String userName, String note, int skip, int limit) {
// TODO Auto-generated method stub
//将用户名称和备注设置为模糊查询准则
Criteria criteria = Criteria.where("user_name").regex(userName).and("note").regex(note);
//构建查询条件,并设置分页跳过前skip个,至多返回limit个
Query query = Query.query(criteria).limit(limit).skip(skip);
//执行
List<User> userList = mongoTemplate.find(query, User.class);
return userList;
}
@Override
public UpdateResult updateUser(Integer id, String userName, String note) {
// TODO Auto-generated method stub
//确定要跟新的对象
Criteria criterId = Criteria.where("id").is(id);
Query query = Query.query(criterId);
//定义跟新对象,后续可变化的字符串代表排除在外的属性
Update update = Update.update("user_name", userName);
update.set("note", note);
//更新单个对象
UpdateResult result = mongoTemplate.updateFirst(query, update, User.class);
//更新多个对象
//UpdateResult result2 = mongoTemplate.updateMulti(query, update, User.class);
return result;
}
@Override
public User getUser(Integer id) {
// TODO Auto-generated method stub
User user = mongoTemplate.findById(id, User.class);
return user;
//如果只需要获取第一个也可以采用如下查询方法
// Criteria criterId = Criteria.where("id").is(id);
// Query query = Query.query(criterId);
// return mongoTemplate.findOne(query, User.class);
}
@Override
public User insert() {
// TODO Auto-generated method stub
Role roles1 = new Role();
roles1.setId(2);
roles1.setRoleName("11");
roles1.setNote("111");
Role roles2 = new Role();
roles2.setId(3);
roles2.setRoleName("22");
roles2.setNote("222");
List<Role> roles = new ArrayList<Role>();
roles.add(roles1);
roles.add(roles2);
User user = new User();
user.setId(12);
user.setUserName("12");
user.setNote("12");
user.setRoles(roles);
User user2 = mongoTemplate.insert(user);
return user2;
}
@Override
public List<User> findByUserNameLike(String userName) {
// TODO Auto-generated method stub
return userDao.findByUserNameLike(userName);
}
@Override
public User findByUserByIdOrUserName(Integer id, String userName) {
// TODO Auto-generated method stub
return userDao.findByUserByIdOrUserName(id, userName);
}
}
在上述代码的getUser方法中有一句
Criteria criterId = Criteria.where("id").is(id);
表示构件一个用户主键为变量ID的查询准则,然后通过
Query query = Query.query(criterId);
构件查询条件,然后就通过findOne查询出唯一的用户信息。
再看findUser方法,这里构建了一个查询准则:
Criteria criteria = Criteria.where("user_name").regex(userName).and("note").regex(note);
这里的where方法的参数设置为“userName”,这个字符串代表的是类User的属性userName;regex方法代表正则式匹配,即执行模糊查询;and方法代表连接字,代表同时满足。然后通过
Query query = Query.query(criteria).limit(limit).skip(skip);
构建查询条件,这里的limit代表限制至多返回limit条记录,而skip则代表跳过多少条记录。最后使用find方法,将结果查询为一个列表,返回给调用者。
测试插入一条数据
@Override
public User insert() {
// TODO Auto-generated method stub
Role roles1 = new Role();
roles1.setId(2);
roles1.setRoleName("11");
roles1.setNote("111");
Role roles2 = new Role();
roles2.setId(3);
roles2.setRoleName("22");
roles2.setNote("222");
List<Role> roles = new ArrayList<Role>();
roles.add(roles1);
roles.add(roles2);
User user = new User();
user.setId(12);
user.setUserName("12");
user.setNote("12");
user.setRoles(roles);
User user2 = mongoTemplate.insert(user);
return user2;
}
@RequestMapping("/insert")
@ResponseBody
public User insert() {
return userService.insert();
}
插入成功将会以json数据格式返回到页面
查看MongoDB中的数据:
插入成功。
测试修改数据
@Override
public UpdateResult updateUser(Integer id, String userName, String note) {
// TODO Auto-generated method stub
//确定要跟新的对象
Criteria criterId = Criteria.where("id").is(id);
Query query = Query.query(criterId);
//定义跟新对象,后续可变化的字符串代表排除在外的属性
Update update = Update.update("user_name", userName);
update.set("note", note);
//更新单个对象
UpdateResult result = mongoTemplate.updateFirst(query, update, User.class);
//更新多个对象
//UpdateResult result2 = mongoTemplate.updateMulti(query, update, User.class);
return result;
}
@RequestMapping("/update")
@ResponseBody
public UpdateResult updateUser(Integer id,String userName,String note) {
return userService.updateUser(id, userName, note);
}
测试结果
验证
修改成功
使用JPA
MongoDB是个十分接近于关系数据库的NoSQL数据库,它还可以使用JPA编程,只是于关系数据库不同的是提供给我们的接口不是JpaRepository<T,ID>,而是MongoRepository<T,ID>
- 基本用法
使用Jpa只需要定义其接口,按照其名称就能够进行扩展,而无需实现接口的方法
首先创建一个接口
package com.springboot.chapter08.dao;
import java.util.List;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import com.springboot.chapter08.pojo.User;
//表标识为dao层
@Repository
//扩展MongoRepository接口,T为实体类型,ID为主键的类型
public interface UserDao extends MongoRepository<User, Integer> {
/**
* 符合jpa规范命名方法,则不需要再实现该方法也可用
* 在满足条件的文档按照用户名称进行模糊查询
* @param userName
* @return
*/
List<User> findByUserNameLike(String userName);
/**
* 根据编号或者用户名查找用户
* @param id
* @param userName
* @return
*/
User findByUserByIdOrUserName(Integer id,String userName);
}
接下来的问题是如何将一个接口转化为一个spring bean。为此spring data bean提供了一个注解**@EnableMongoRepositories**,通过它便可以指定扫描对应的接口。
package com.springboot.chapter08.main;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@SpringBootApplication(scanBasePackages = "com.springboot.chapter08",exclude = DataSourceAutoConfiguration.class)
//指定扫描的包,用于继承了MongoRepository接口
@EnableMongoRepositories(basePackages = "com.springboot.chapter08.dao")
public class Chapter08Application {
public static void main(String[] args) {
SpringApplication.run(Chapter08Application.class, args);
}
}
在UserService添加代码:
@Override
public List<User> findByUserNameLike(String userName) {
// TODO Auto-generated method stub
return userDao.findByUserNameLike(userName);
}
在UserController添加如下代码:
/**
* 测试jpa接口
* @param userName
* @return
*/
@RequestMapping("byname")
@ResponseBody
public List<User> findByUserNameLike(String userName){
return userService.findByUserNameLike(userName);
}
测试运行结果(查询userName中含有“1”的用户)
- 使用自定义查询
当查询需要多个字段,或者需要进行较为复杂灵活的查询,JPA规范并不能满足这样的需求。
自定义查询方法,在UserDao中加入如下方法:
/**
* 根据编号或者用户名查找用户
* @param id
* @param userName
* @return
*/
User findByUserByIdOrUserName(Integer id,String userName);
接下来实现这个方法,只是这里的UserDao接口扩展了MongoRepository,如果实现这个接口就要实现其定义的方法,会给使用者带来许多不便,而JPA自动生成方法逻辑的形式就荡然无存了。这时候spring给了我们新的约定,在spring中只要定义一个“接口名称+Impl”的类并且提供与接口定义相同的方法,spring就会自动找到这个类对应的方法作为JPA接口定义的实现,如下:
package com.springboot.chapter08.dao.impl;
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private MongoTemplate mongoTemplate;
@Override
public User findByUserByIdOrUserName(Integer id, String userName) {
// TODO Auto-generated method stub
//构造ID查询准则
Criteria criteriaId = Criteria.where("id").is(id);
//构造username查询准则
Criteria criteriaUserName = Criteria.where("userName").is(userName);
Criteria criteria = new Criteria();
//使用$or操作符关联两个条件,形成或关系
criteria.orOperator(criteriaId,criteriaUserName);
Query query = Query.query(criteria);
User resultUser = mongoTemplate.findOne(query, User.class);
return resultUser;
}
}
@EnableMongoRepositories(
basePackages = "com.springboot.chapter08.dao"
//使用自定义后缀,其默认为Impl
//此时需要修改类名,UserDaoImpl-->UserDaoStuff
//repositoryImplementationPostfix = "Stuff"
)
测试
/*
* 测试自定义方法
*/
@RequestMapping("/findOr")
@ResponseBody
public User findByUserByIdOrUserName(Integer id,String userName) {
return userService.findByUserByIdOrUserName(id, userName);
}
测试结果
id=5是不存在的
userName=1100000十不存在的