SpringBoot整合MongoDB
https://docs.spring.io/spring-boot/docs/current/reference/html/data.html#data.nosql.mongodb.repositories
注意关注MongoDB服务端和客户端之间的版本兼容问题:https://docs.spring.io/spring-data/mongodb/reference/preface.html
环境准备
- 引入依赖
<!--spring data mongodb-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
-
配置文件的编写
server: port: 8088 spring: application: name: mongdb-server data: mongodb: uri: mongodb://hushang:123456@192.168.75.100:27017/test?authSource=admin # uri等同于下面的配置 # database: test # host: 192.168.75.100 # port: 27017 # username: hushang # password: 123456 # authentication-database: admin
连接配置参考文档:https://docs.mongodb.com/manual/reference/connection-string/
-
测试
// LearnMongodbApplicationTest为我们在测试包下定义的主启动类 public class MongoTemplateTests extends LearnMongodbApplicationTest{ @Autowired private MongoTemplate mongoTemplate; @Test public void testCollection(){ boolean exists = mongoTemplate.collectionExists("emp"); if (exists){ // 删除集合 mongoTemplate.dropCollection("emp"); } // 如果不存在则创建 mongoTemplate.createCollection("emp"); } }
文档操作
相关注解
-
@Document
- 修饰范围: 用在类上
- 作用: 用来映射这个类的一个对象为mongo中一条文档数据。
- 属性:( value 、collection )用来指定操作的集合名称
-
@Id
- 修饰范围: 用在成员变量、方法上
- 作用: 用来将成员变量的值映射为文档的_id的值
-
@Field
- 修饰范围: 用在成员变量、方法上
- 作用: 用来将成员变量及其值映射为文档中一个key:value对。
- 属性:( name , value )用来指定在文档中 key的名称,默认为成员变量名
-
@Transient
- 修饰范围:用在成员变量、方法上
- 作用:用来指定此成员变量不参与文档的序列化
创建实体
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import java.util.Date;
/**
* @Description: 临时emp 集合中的文档实体对象
* @Author 胡尚
* @Date: 2024/7/27 19:09
*/
@Data
@Document("emp") // 需要和mongodb中的collection名对应上
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
@Id
private Integer id;
@Field("username")
private String name;
@Field
private Integer age;
@Field
private Date cteateTime;
// 指定此成员变量不参与文档的序列化
@Transient
private String script;
}
添加文档
insert方法返回值是新增的Document对象,里面包含了新增后_id
的值。如果集合不存在会自动创建集合。
通过Spring Data MongoDB还会给集合中多加一个_class
的属性,存储新增时Document对应Java中类的全限定路径。
这么做为了查询时能把Document转换为Java类型。
@Test
public void testInsertDocument(){
Employee user = new Employee(1, "hushang", 25, new Date(), "这是一个测试数据");
// 添加文档
// save() 方法 _id存在时进行更新操作
Employee userSave = mongoTemplate.save(user);
System.out.println(userSave);
// insert() _id存在时抛异常 支持批量操作
//Employee userSave = mongoTemplate.insert(user);
}
此时我mongodb中保存的结果为
test> db.emp.find()
[
{
_id: 1,
username: 'hushang',
age: 25,
cteateTime: ISODate("2024-07-27T11:19:15.014Z"),
_class: 'com.hs.learn.entity.Employee'
}
]
我们在试试insert()方法的批量插入
@Test
public void testInsertDocument(){
ArrayList<Employee> employeeArrayList = new ArrayList<>();
employeeArrayList.add(new Employee(2, "hushang", 25, new Date(), ""));
employeeArrayList.add(new Employee(3, "hushang", 25, new Date(), ""));
employeeArrayList.add(new Employee(4, "hushang", 25, new Date(), ""));
// 插入多条记录
mongoTemplate.insert(employeeArrayList, Employee.class);
}
- 插入重复数据时: insert报 DuplicateKeyException提示主键重复; save对已存在的数据进行更新。
- 批处理操作时: insert可以一次性插入所有数据,效率较高;save需遍历所有数据,一次插入或更新,效率较低。
查询文档
Criteria
是标准查询的接口,可以引用静态的Criteria.where
的把多个条件组合在一起,就可以轻松地将多个方法标准和查询连接起来,方便我们操作查询语句。
@Test
public void testFind(){
log.info("==========查询所有文档===========");
List<Employee> employeeList = mongoTemplate.findAll(Employee.class);
employeeList.forEach(System.out::println);
log.info("==========根据_id查询===========");
Employee mongoTemplateById = mongoTemplate.findById(1, Employee.class);
System.out.println(mongoTemplateById);
log.info("==========findOne返回第一个文档===========");
Employee employeeByOne = mongoTemplate.findOne(new Query(), Employee.class);
System.out.println(employeeByOne);
log.info("==========条件查询===========");
//new Query() 表示没有条件
//查询年龄大于等于18的数据
Query query1 = new Query(Criteria.where("age").gt(18));
//查询年龄大于等于18 小于28的数据
Query query2 = new Query(Criteria.where("age").gt(18).lt(28));
// 正则查询(模糊查询) java中正则不需要有//
// 这里用 name 或者是 username 都能查询
Query query3 = new Query(Criteria.where("name").regex("hu"));
log.info("==========and or 多条件查询===========");
Criteria criteria = new Criteria();
// 查询年龄大于18 并且 name为hushang的
// criteria.andOperator(Criteria.where("age").gt(18), Criteria.where("name").is("hushang"));
// 查询name为hushang 或者 年龄小于等于30
criteria.orOperator(Criteria.where("name").is("hushang"), Criteria.where("age").lte(30));
Query query4 = new Query(criteria);
System.out.println(mongoTemplate.find(query4, Employee.class));
log.info("==========sort排序===========");
// 安装年龄进行降序排序
Query query5 = new Query();
query5.with(Sort.by(Sort.Order.desc("age")));
log.info("======skip limit 分页 =======");
// skip用于指定跳过记录数,limit则用于限定返回结果数量。
// 安装年龄进行排序,在进行分页
Query query6 = new Query();
query6.with(Sort.by(Sort.Order.desc("age")))
.skip(1)
.limit(2);
}
@Test
public void testFindByJson(){
// 等值查询 查询username为hushang的文档
String json = "{username: 'hushang'}";
// 多条件查询
String json2 = "{$and: [{age: {$gt: 18}},{age: {$lt: 28}}]}";
BasicQuery basicQuery = new BasicQuery(json2);
List<Employee> employees = mongoTemplate.find(basicQuery, Employee.class);
employees.forEach(System.out::println);
}
更新文档
在Mongodb中无论是使用客户端API还是使用Spring Data,更新返回结果一定是受行数影响。如果更新后的结果和更新前的结果是相同,返回0。
- updateFirst() 只更新满足条件的第一条记录
- updateMulti() 更新所有满足条件的记录
- upsert() 没有符合条件的记录则插入数据
// Update对象是这个包下的 import org.springframework.data.mongodb.core.query.Update;
@Test
public void testUpdate(){
//query设置查询条件
Query query = new Query(Criteria.where("username").is("hushang"));
log.info("==========更新前===========");
List<Employee> employees = mongoTemplate.find(query, Employee.class);
employees.forEach(System.out::println);
Update update = new Update();
update.set("age", 18);
// updateFirst()只更新满足条件的一条文档
// UpdateResult updateResult = mongoTemplate.updateFirst(query, update, Employee.class);
// updateMulti()更新所有满足条件的文档
// UpdateResult updateResult = mongoTemplate.updateMulti(query, update, Employee.class);
// upsert() 没有符合条件的记录则插入数据
// UpdateResult updateResult = mongoTemplate.upsert(query, update, Employee.class);
}
删除文档
@Test
public void testDelete(){
//删除所有文档
//mongoTemplate.remove(new Query(),Employee.class);
//条件删除
Query query = new Query(Criteria.where("age").gte(45));
mongoTemplate.remove(query,Employee.class);
// mongodb还有下面两个移除相关的方法
// mongoTemplate.findAllAndRemove()
// mongoTemplate.findAndRemove()
}
小技巧:如何去掉_class属性
@Configuration
public class TulingMongoConfig {
/**
* 定制TypeMapper去掉_class属性
* @param mongoDatabaseFactory
* @param context
* @param conversions
* @return
*/
@Bean
MappingMongoConverter mappingMongoConverter(
MongoDatabaseFactory mongoDatabaseFactory,
MongoMappingContext context, MongoCustomConversions conversions){
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDatabaseFactory);
MappingMongoConverter mappingMongoConverter =
new MappingMongoConverter(dbRefResolver,context);
mappingMongoConverter.setCustomConversions(conversions);
//构造DefaultMongoTypeMapper,将typeKey设置为空值
mappingMongoConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
return mappingMongoConverter;
}
}