前言
Spring Boot是一个用于构建独立的、生产级别的Java应用程序的框架,而MongoDB是一个流行的NoSQL数据库,具有灵活的数据模型和可扩展性。通过将它们结合起来,您可以轻松地开发出高效、可靠的应用程序,并且能够处理大量的数据。
MongoDB特点
非结构化数据存储
MongoDB使用文档模型来存储数据,文档是一种类似于JSON的数据结构,可以存储各种类型的数据,而不需要事先定义数据模式。
高度可扩展性
MongoDB支持水平扩展,可以通过添加更多的服务器来处理大量的数据和高并发请求。它还支持自动分片,可以将数据分布在多个服务器上,以提高性能和可用性。
强大的查询功能
MongoDB提供了丰富的查询功能,包括支持复杂的查询条件、索引和聚合操作。它还支持全文搜索和地理空间查询,可以满足各种查询需求。
高性能
MongoDB使用内存映射文件的方式来存储数据,可以利用操作系统的缓存机制来提高读取性能。此外,它还支持异步写入和复制,可以提高写入性能和数据的可靠性。
灵活的数据模型
MongoDB的文档模型非常灵活,可以存储不同结构的文档,而不需要事先定义数据模式。这使得数据模型的变更变得更加容易和灵活。
支持多种数据类型
MongoDB支持各种数据类型,包括字符串、整数、浮点数、日期、数组、嵌套文档等。它还支持二进制数据和大型文件的存储。
可靠性和可用性
MongoDB支持数据的自动复制和故障恢复,可以提供高可用性和数据的持久性。它还支持数据的备份和恢复,以及数据的安全性和权限控制。
版本依赖
模块 | 版本 |
JDK | 17 |
SpringBoot | 3.1.0 |
Mongo | 6.0.8 |
mybatis-plus | 2.0.2 |
环境准备
MongoDB安装
安装教程请查看:一文搞定(linux+windows+docker)安装MongoDB
导入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.0</version>
</parent>
<groupId>com.example</groupId>
<artifactId>springboot-mongo-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-mongo-demo</name>
<description>springboot-mongo-demo</description>
<properties>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mongo -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.15.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
application.yml配置
server:
port: 8080 # 设置访问端口
spring:
data:
mongodb:
uri: mongodb://47.98.123.147:27017/mongo_db
# 上方为明确指定某个数据的用户进行连接
# 也可以使用admin 数据库中的用户进行连接 统一到admin 数据库进行认证
# admin 用户认证 url 写法: mongodb://账户:密码%40@ip:端口/数据库名?authSource=admin&authMechanism=SCRAM-SHA-1
使用实例
mongo 封装顶级service
public abstract class MongoServiceImpl<T> implements MongoService<T> {
protected abstract Class<T> getEntityClass();
@Autowired
protected MongoTemplate mgt;
@Override
public void save(T entity) {
mgt.save(entity);
}
@Override
public void update(T entity) {
// 反向解析对象
Map<String, Object> map = null;
try {
map = parseEntity(entity);
} catch (Exception e) {
e.printStackTrace();
}
// ID字段
String idName = null;
Object idValue = null;
// 生成参数
Update update = new Update();
if (EmptyUtil.isNotEmpty(map)) {
for (String key : map.keySet()) {
if (key.indexOf("{") != -1) {
// 设置ID
idName = key.substring(key.indexOf("{") + 1, key.indexOf("}"));
idValue = map.get(key);
} else {
update.set(key, map.get(key));
}
}
}
mgt.updateFirst(new Query().addCriteria(where(idName).is(idValue)), update, getEntityClass());
}
@Override
public void delete(Serializable... ids) {
if (EmptyUtil.isNotEmpty(ids)) {
for (Serializable id : ids) {
mgt.remove(mgt.findById(id, getEntityClass()));
}
}
}
@Override
public T find(Serializable id) {
return mgt.findById(id, getEntityClass());
}
@Override
public List<T> findAll() {
return mgt.findAll(getEntityClass());
}
@Override
public List<T> findAll(String order) {
List<Order> orderList = parseOrder(order);
if (EmptyUtil.isEmpty(orderList)) {
return findAll();
}
return mgt.find(new Query().with(Sort.by((orderList))), getEntityClass());
}
@Override
public List<T> findByProp(String propName, Object propValue) {
return findByProp(propName, propValue, null);
}
@Override
public List<T> findByProp(String propName, Object propValue, String order) {
Query query = new Query();
// 参数
query.addCriteria(where(propName).is(propValue));
// 排序
List<Order> orderList = parseOrder(order);
if (EmptyUtil.isNotEmpty(orderList)) {
query.with(Sort.by(orderList));
}
return mgt.find(query, getEntityClass());
}
@Override
public List<T> findByProps(String[] propName, Object[] propValue) {
return findByProps(propName, propValue, null);
}
@Override
public List<T> findByProps(String[] propName, Object[] propValue, String order) {
Query query = createQuery(propName, propValue, order);
return mgt.find(query, getEntityClass());
}
@Override
public T uniqueByProp(String propName, Object propValue) {
return mgt.findOne(new Query(where(propName).is(propValue)), getEntityClass());
}
@Override
public T uniqueByProps(String[] propName, Object[] propValue) {
Query query = createQuery(propName, propValue, null);
return mgt.findOne(query, getEntityClass());
}
@Override
public Page<T> pageAll(int pageNo, int pageSize) {
return pageAll(pageNo, pageSize, null);
}
@Override
public Page<T> pageAll(int pageNo, int pageSize, String order) {
return pageByProp(pageNo, pageSize, null, null, order);
}
@Override
public Page<T> pageByProp(int pageNo, int pageSize, String param, Object value) {
return pageByProp(pageNo, pageSize, param, value, "sendTime");
}
@Override
public Page<T> pageByProp(int pageNo, int pageSize, String param, Object value, String order) {
String[] params = null;
Object[] values = null;
if (EmptyUtil.isNotEmpty(param)) {
params = new String[] { param };
values = new Object[] { value };
}
return pageByProps(pageNo, pageSize, params, values, order);
}
@Override
public Page<T> pageByProps(int pageNo, int pageSize, String[] params, Object[] values) {
return pageByProps(pageNo, pageSize, params, values, null);
}
@Override
public Page<T> pageByProps(int pageNo, int pageSize, String[] params, Object[] values, String order) {
// 创建分页模型对象
Page<T> page = new Page<>(pageNo, pageSize);
// 查询总记录数
int count = countByCondition(params, values);
page.setTotal(count);
// 查询数据列表
Query query = createQuery(params, values, order);
// 设置分页信息
query.skip((page.getCurrent()-1)*page.getSize());
query.limit(page.getSize());
query.with(Sort.by(Direction.DESC,"sendTime"));
// 封装结果数据
page.setRecords(mgt.find(query, getEntityClass()));
return page;
}
@Override
public int countByCondition(String[] params, Object[] values) {
Query query = createQuery(params, values, null);
Long count = mgt.count(query, getEntityClass());
return count.intValue();
}
/**
* 创建带有where条件(只支持等值)和排序的Query对象
*
* @param params
* 参数数组
* @param values
* 参数值数组
* @param order
* 排序
* @return Query对象
*/
protected Query createQuery(String[] params, Object[] values, String order) {
Query query = new Query();
// where 条件
if (EmptyUtil.isNotEmpty(params) && EmptyUtil.isNotEmpty(values)) {
for (int i = 0; i < params.length; i++) {
query.addCriteria(where(params[i]).is(values[i]));
}
}
// 排序
List<Order> orderList = parseOrder(order);
if (EmptyUtil.isNotEmpty(orderList)) {
query.with(Sort.by(orderList));
}
return query;
}
/**
* 解析Order字符串为所需参数
*
* @param order
* 排序参数,如[id]、[id asc]、[id asc,name desc]
* @return Order对象集合
*/
protected List<Order> parseOrder(String order) {
List<Order> list = null;
if (EmptyUtil.isNotEmpty(order)) {
list = new ArrayList<Order>();
// 共有几组排序字段
String[] fields = order.split(",");
Order o = null;
String[] item = null;
for (int i = 0; i < fields.length; i++) {
if (EmptyUtil.isEmpty(fields[i])) {
continue;
}
item = fields[i].split(" ");
if (item.length == 1) {
o = new Order(Direction.ASC, item[0]);
} else if (item.length == 2) {
o = new Order("desc".equalsIgnoreCase(item[1]) ? Direction.DESC : Direction.ASC, item[0]);
} else {
throw new RuntimeException("排序字段参数解析出错");
}
list.add(o);
}
}
return list;
}
/**
* 将对象的字段及值反射解析为Map对象<br>
* 这里使用Java反射机制手动解析,并且可以识别注解为主键的字段,以达到根据id进行更新实体的目的<br>
* key:字段名称,value:字段对应的值
*
* @param t
* 要修改的对象
* @return Map对象,注意:id字段的key封装为“{id字段名称}”,以供后续识别
* @throws Exception
*/
protected Map<String, Object> parseEntity(T t) throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
/*
* 解析ID
*/
String idName = "";
Field[] declaredFields = getEntityClass().getDeclaredFields();
for (Field field : declaredFields) {
if (field.isAnnotationPresent(Id.class)) {
field.setAccessible(true);
map.put("{" + field.getName() + "}", field.get(t));
idName = field.getName();
break;
}
}
/*
* 解析其他属性
*/
Method[] methods = getEntityClass().getDeclaredMethods();
if (EmptyUtil.isNotEmpty(methods)) {
for (Method method : methods) {
if (method.getName().startsWith("get") && method.getModifiers() == Modifier.PUBLIC) {
String fieldName = parse2FieldName(method.getName());
if (!fieldName.equals(idName)) {
map.put(fieldName, method.invoke(t));
}
}
}
}
return map;
}
/**
* 将get方法名转换为对应的字段名称
*
* @param methodName
* 如:getName
* @return 如:name
*/
private String parse2FieldName(String methodName) {
String name = methodName.replace("get", "");
name = name.substring(0, 1).toLowerCase() + name.substring(1);
return name;
}
}
EmptyUtil
public class EmptyUtil {
/**
* 私有构造
*/
private EmptyUtil() {
}
/**
* 判断字符串是否为空
*
* @param str
* @return true or false
*/
public static boolean isEmpty(String str) {
return str == null || str == "" || str.trim() == "" || str.length() <= 0;
}
/**
* 判断List是否为空
*
* @param list
* @return true or false
*/
public static <T> boolean isEmpty(List<T> list) {
return list == null || list.isEmpty();
}
/**
* 判断Map是否为空
*
* @param map
* @return true or false
*/
public static <K, V> boolean isEmpty(Map<K, V> map) {
return map == null || map.isEmpty();
}
/**
* 判断数组是否为空
*
* @param array
* @return true or false
*/
public static <T> boolean isEmpty(T[] array) {
return array == null || array.length == 0;
}
/**
* 判断字符串是否不为空
*
* @param str
* @return true or false
*/
public static boolean isNotEmpty(String str) {
return !isEmpty(str);
}
/**
* 判断List是否不为空
*
* @param list
* @return true or false
*/
public static <T> boolean isNotEmpty(List<T> list) {
return !isEmpty(list);
}
/**
* 判断Map是否不为空
*
* @param map
* @return true or false
*/
public static <K, V> boolean isNotEmpty(Map<K, V> map) {
return !isEmpty(map);
}
/**
* 判断数组是否不为空
*
* @param array
* @return true or false
*/
public static <T> boolean isNotEmpty(T[] array) {
return !isEmpty(array);
}
}
User
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = -4328989516223829865L;
/**
* 用户ID
*/
private String id;
/**
* 用户账号
*/
private String userName;
/**
* 密码
*/
private String password;
}
UserMServiceImpl
public class UserMServiceImpl extends MongoServiceImpl<User> implements UserMService {
@Override
protected Class<User> getEntityClass() {
return User.class;
}
@Override
public void insert(User user) {
save(user);
}
@Override
public User selectById(String id) {
return find(id);
}
}
MongoController
@RestController
@RequestMapping("/mongo")
public class MongoController {
@Autowired
private UserMService userMService;
@ResponseBody
@GetMapping("/add")
public User add(){
String id = UUID.randomUUID().toString();
User user = User.builder().id(id).userName("aa").password("111111").build();
userMService.insert(user);
User userMb= userMService.selectById(id);
return userMb;
}
}
测试结果
总结
希望通过本文,Spring Boot和MongoDB的整合有了更深入的了解。Spring Boot提供了简化开发流程的特性,使得构建应用程序变得更加容易和高效。而MongoDB作为一种NoSQL数据库,具有灵活的数据模型和可扩展性,能够满足各种应用程序的需求。
通过将Spring Boot和MongoDB结合起来,可以轻松地构建出高效、可靠的应用程序,并且能够处理大量的数据。无论是开发Web应用程序、移动应用程序还是企业级应用程序,Spring Boot和MongoDB的整合都能够为您提供强大的支持。