spring-boot-starter-data-mongodb 学习使用
原因
最近项目有使用mongodb,现在短暂告一段后,对spring-boot-starter-data-mongodb 的使用进行一些简单总结
安装mongodb
如果使用docker 的话 ,docker-compose.yml 如下,没有映射文件到出来,是简单的学习使用。
version: '3'
services:
mongo:
image: mongo
container_name: mongo1
restart: always
ports:
- 27017:27017
如果是没有使用docker 官网下载
MongoDB Compass MongoDB的GUI ,docker是没有GUI的,如果要看数据的话也要安装这个
MongoDB
简单初始化一个项目
添加start
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
spring 提供两种的start
spring-boot-starter-data-mongodb
Starter for using MongoDB document-oriented database and Spring Data MongoDB
spring-boot-starter-data-mongodb-reactive
Starter for using MongoDB document-oriented database and Spring Data MongoDB Reactive
这里我选择第一个,实际上spring-boot-starter-data-mongodb-reactive 功能更强大,之后用到再说。
配置参数
logging:
level:
org.springframework.data.mongodb.core: debug
spring:
data:
mongodb:
database: xxx
host: 127.0.0.1
注意,如果需要验证的话,需要配置 host,port,username,password,authenticationDatabase
其他还好说, authenticationDatabase 一般是admin, 需要参数如图
使用Querydsl 和 jpa 的原因
因为之前jpa 和 Querydsl 使用比较熟悉了,用的比较顺手,其次,可以很大程度上减少mongodb 的查询语法的使用。
jpa
简单增删改查
entity
public class Person {
@Id
private String id;
private String name;
private Integer age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
添加 Repository
import org.springframework.data.repository.PagingAndSortingRepository;
public interface PersonRepository extends PagingAndSortingRepository<Person, String> {
}
简而言之 ,继承PagingAndSortingRepository, 传入 Person 和 ID 类型 这样其实简单增删改就实现了
简单实现service 层面
public interface PersonService {
void update(String name, String age);
Person findById(String id);
void delete(String id);
Person save(String name);
}
@Service
public class PersonServiceImpl implements PersonService {
@Autowired
private PersonRepository personRepository;
@Override
public void update(String name, String age) {
}
@Override
public Person findById(String id) {
Optional<Person> person = personRepository.findById(id);
return person.orElse(null);
}
@Override
public void delete(String id) {
personRepository.deleteById(id);
}
@Override
public Person save(String name) {
Person person = new Person();
person.setName(name);
return personRepository.save(person);
}
}
@Service 不要忘记,简单的增删改查已经实现了
写个简单test
@SpringBootTest
class MongodbApplicationTests {
@Autowired
private PersonService personService;
@Test
void test1() {
//保存
Person save = personService.save("test1");
System.out.println("save == " + save);
//读取
Person person = personService.findById(save.getId());
System.out.println("findById == " + person);
//删除
personService.delete(person.getId());
System.out.println("delete == ");
//读取
Person result = personService.findById(save.getId());
System.out.println("result == " + result);
}
}
save == Person{id=‘60cdee828f5b721967a17872’, name=‘test1’, age=null}
findById == Person{id=‘60cdee828f5b721967a17872’, name=‘test1’, age=null}
delete ==
result == null
执行结果
打印下查询日志
findOne using query: { “id” : “60cdee828f5b721967a17872”} fields: Document{{}}
Remove using query: { “_id” : { “$oid” : “60cdee828f5b721967a17872”}}
…
PersonRepository 里面可以自己定义方法,如果是使用idea Ultimate 版本 会有提示,社区版的话就没有了,不过spring 上面有语法文档
spring文档
可以实现自定义接口了
public interface PersonRepository extends PagingAndSortingRepository<Person, String> {
Optional<Person> findFirstByName(String name);
}
@Override
public void update(String name, Integer age) {
Optional<Person> first = personRepository.findFirstByName(name);
if(first.isPresent()) {
Person person = first.get();
person.setAge(age);
personRepository.save(person);
}
}
@Test
void test2() {
//保存
Person save = personService.save("test2");
System.out.println("save == " + save);
//更新
personService.update("test2", 18);
System.out.println("update == ");
//读取
Person result = personService.findById(save.getId());
System.out.println("result == " + result);
}
结果
save == Person{id=‘60cdf3edbbe87d40bdaa32de’, name=‘test2’, age=null}
update ==
result == Person{id=‘60cdf3edbbe87d40bdaa32de’, name=‘test2’, age=18}
看下数据
分页
Page<Person> findAllByNameContains(String name, Pageable pageable);
执行语句是
find using query: { “name” : { “$regularExpression” : { “pattern” : “.XXX.”, “options” : “”}}} fields: Document{{}}
@Override
public void saveAll(List<Person> persons) {
personRepository.saveAll(persons);
}
@Override
public Page<Person> page(String name, Pageable pageable) {
return personRepository.findAllByNameContains(name, pageable);
}
@Test
void test3() {
List<Person> list = new ArrayList<>();
String name = "test";
for (int i = 0; i < 10; i++) {
Person person = new Person();
person.setName(name);
list.add(person);
}
personService.saveAll(list);
Page<Person> page = personService.page("es", PageRequest.of(0, 5));
System.out.println("getTotalElements == "+page.getTotalElements());
System.out.println("getSize == "+page.getSize());
List<Person> content = page.getContent();
for (Person person : content) {
System.out.println("person == "+person);
}
}
执行结果
getTotalElements == 10
getSize == 5
person == Person{id=‘60cf1a815244aa1ac87dcbcc’, name=‘test’, age=null}
person == Person{id=‘60cf1a815244aa1ac87dcbcd’, name=‘test’, age=null}
person == Person{id=‘60cf1a815244aa1ac87dcbce’, name=‘test’, age=null}
person == Person{id=‘60cf1a815244aa1ac87dcbcf’, name=‘test’, age=null}
person == Person{id=‘60cf1a815244aa1ac87dcbd0’, name=‘test’, age=null}
关联表
@DBRef 注解关联
新加 entity
@Document
public class Classt {
@Id
private String id;
private String name;
public Classt(String name) {
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Classt{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
'}';
}
}
public interface ClasstRepository extends PagingAndSortingRepository<Classt,String> {
}
@Override
public Person save(Person person, List<Classt> classts) {
person.setClasstes(classts);
return personRepository.save(person);
}
@Override
public void saveAll(List<Person> persons) {
personRepository.saveAll(persons);
}
@Override
public Page<Person> page(String name, Pageable pageable) {
return personRepository.findAllByNameContains(name, pageable);
}
@Override
public Classt saveClasst(String name) {
Classt classt = new Classt(name);
return classtRepository.save(classt);
}
@Test
void test4() {
Classt classt = personService.saveClasst("一班");
Person save = personService.save("test4");
System.out.println("save == " + save);
List<Classt> classts = new ArrayList<>();
classts.add(classt);
save.setClasstes(classts);
personService.save(save, classts);
//读取
Person result = personService.findById(save.getId());
System.out.println("result == " + result);
}
结果
result == Person{id=‘60cf2e22a53a115ac3a96de7’, name=‘test4’, age=null, classtes=[Classt{id=‘60cf2e22a53a115ac3a96de6’, name=‘一班’}]}
如果没有@DBRef
classt就不会生成
GridFs
@Autowired
private GridFsTemplate gridFsTemplate;
保存和提取
@Override
public void storeFileToGridFs(String path) throws FileNotFoundException {
File file = new File(path);
FileInputStream fis = new FileInputStream(file);
gridFsTemplate.store(fis, "filename.txt");
}
@Override
public void findFilesInGridFs(String fileName) throws IOException {
GridFsResource resource = gridFsTemplate.getResource("filename.txt");
InputStream stream = resource.getInputStream();
byte[] bytes = StreamUtils.copyToByteArray(stream);
Path path = Paths.get("E:\\", fileName);
Files.write(path, bytes);
}
@Test
void test8() throws IOException {
personService.findFilesInGridFs("602.JPG");
}
Querydsl
添加依赖
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-mongodb</artifactId>
<version>${querydsl.version}</version>
</dependency>
<dependency>
<groupId>org.mongodb.morphia</groupId>
<artifactId>morphia</artifactId>
<version>1.3.2</version>
</dependency>
在添加plugin
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor><processor>com.querydsl.apt.morphia.MorphiaAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
idea 的话 双击ctrl ,执行 mvn clean package -Dmaven.test.skip=true
结果target/generated-sources/java发现没有生成文件,在实体上添加@Entity,即可
点开com.querydsl.apt.morphia.MorphiaAnnotationProcessor
@SupportedAnnotationTypes({“com.querydsl.core.annotations.","org.mongodb.morphia.annotations.”})
这个是 支持的注解
将之前的PersonRepository进行修改
public interface PersonRepository extends MongoRepository<Person, String>, QuerydslPredicateExecutor<Person> {
Optional<Person> findFirstByName(String name);
Page<Person> findAllByNameContains(String name, Pageable pageable);
}
这里是多添加了继承QuerydslPredicateExecutor,
然后service 添加接口
private QPerson qPerson = QPerson.person;
@Override
public List<Person> findAllQueryDsl(String name, Integer age) {
BooleanExpression expression = qPerson.name.contains(name)
.or(qPerson.age.goe(age));
return Lists.newArrayList(personRepository.findAll(expression));
}
添加测试用例
@Test
void test9() throws IOException {
createBatch();
List<Person> content = personService.findAllQueryDsl("te", 5);
for (Person person : content) {
System.out.println("person == "+person);
}
}
person == Person{id=‘6129e8eeb809463b2ee1c1fc’, name=‘test’, age=26, classtes=null}
person == Person{id=‘6129e8eeb809463b2ee1c1fd’, name=‘test’, age=19, classtes=null}
person == Person{id=‘6129e8eeb809463b2ee1c1fe’, name=‘test’, age=18, classtes=null}
person == Person{id=‘6129e8eeb809463b2ee1c1ff’, name=‘test’, age=40, classtes=null}
person == Person{id=‘6129e8eeb809463b2ee1c200’, name=‘test’, age=46, classtes=null}
person == Person{id=‘6129e8eeb809463b2ee1c201’, name=‘test’, age=30, classtes=null}
person == Person{id=‘6129e8eeb809463b2ee1c202’, name=‘test’, age=4, classtes=null}
person == Person{id=‘6129e8eeb809463b2ee1c203’, name=‘test’, age=26, classtes=null}
person == Person{id=‘6129e8eeb809463b2ee1c204’, name=‘test’, age=15, classtes=null}
person == Person{id=‘6129e8eeb809463b2ee1c205’, name=‘test’, age=26, classtes=null}
对于短时间开发,又对mongodb 的语法不了解,又有使用DSL 来说,是很方便的,基本不用去了解具体的查询构造
mongoTemplate
随着业务对接的表越来越多,Jpa 一个Collections 至少要新建 一个 Entity, 一个Repository,而 dsl 也是如此,就显的很麻烦,这时候就可以使用mongoTemplate
其中mongoTemplate 的find 有两种实现
第一种:
<T> List<T> find(Query query, Class<T> entityClass);
参数是查询语句和类,其中对应的Collections 名字可以类上添加@Document(“t_person”) 来实现
第二种
<T> List<T> find(Query query, Class<T> entityClass, String collectionName);
参数查询语句和类,其中对应的Collections 名字由 collectionName 传入,class 的@Document(“t_person”) 无效
添加代码
@Autowired
private MongoTemplate mongoTemplate;
@Override
public List<Person> findAllByTemplate(String name, Integer age) {
List<Person> people = mongoTemplate.find(Query.query(Criteria.where("name").is(name).and("age").gt(age)),
Person.class);
return people;
}
@Test
void test11() throws IOException {
createBatch();
List<Person> content = personService.findAllByTemplate("test", 15);
for (Person person : content) {
System.out.println("person == "+person);
}
}
person == Person{id=‘6129ef1543275b55d42a533b’, name=‘test’, age=45, classtes=null}
person == Person{id=‘6129ef1543275b55d42a533c’, name=‘test’, age=44, classtes=null}
person == Person{id=‘6129ef1543275b55d42a533f’, name=‘test’, age=24, classtes=null}
person == Person{id=‘6129ef1543275b55d42a5340’, name=‘test’, age=18, classtes=null}
person == Person{id=‘6129ef1543275b55d42a5342’, name=‘test’, age=46, classtes=null}
如果要完全等于findAllQueryDsl 的话
@Override
public List<Person> findAllByTemplate(String name, Integer age) {
List<Person> people = mongoTemplate.find(Query.query(Criteria.where("name").regex(".*\\Qte\\E.*").and("age").gte(age)),
Person.class);
return people;
}
会麻烦一些
但是也有些好处,比如不用在写Repository,如果多个表的字段类似,可以写通用的Query
或者将Entity也忽略掉
如下
@Override
public void saveMap(String str) throws IOException {
Map<String, String> map = new HashMap<>();
map.put("_id", str);
map.put("id", str);
mongoTemplate.save(map, "wjm");
}
@Override
public List<Map> getMap(String str) throws IOException {
Query query = Query.query(Criteria.where("_id").is(str));
return mongoTemplate.find(query, Map.class, "wjm");
}
不过其中"_id" 要做唯一主键
@Test
void test10() throws IOException {
personService.saveMap("date");
List<Map> map = personService.getMap("date");
for (Map m : map) {
System.out.println(" == "+m);
}
}
== {_id=date, id=date}
还是蛮有意思
源码地址
来源
starters
jpa
Mapping Annotation Overview
事务
examples