对数据库的操作一定要放在
@Service
类中,而不是放在@Controller
类中;且@Controller
类可以调用@Service
类的方法。
@Service
类主要用于不易变的核心业务逻辑@Controller
类与前端页面紧密配合,调用@Service
服务读写数据,从而响应前端请求
一、CRUD
1. 新增数据
在 Java 中万物皆对象。所以,所谓数据就是实例对象
import org.springframework.data.mongodb.core.MongoTemplate;
@Autowired
private MongoTemplate mongoTemplate;
public void test() {
Song song = new Song();
song.setSubjectId("s001");
song.setLyrics("...");
song.setName("成都");
mongoTemplate.insert(song);
}
使用@Autowired
可以让系统自动注入MongoTemplate
的实例。只需要调用mongoTemplate.isnert()
方法就能把对象存入数据库。
2. 查询数据
mongoTemplate.findById(songId, Song.class)
注意:findById()
方法第 1 个参数是主键 id,第 2 个参数是具体的类,写法是类名.class
@Override
public Song get(String songId) {
// 输入的主键 id 必须有文本,不能为空或全空格
if (!StringUtils.hasText(songId)) {
LOG.error("input songId is blank.");
return null;
}
Song song = mongoTemplate.findById(songId, Song.class);
return song;
}
3. 修改数据
- 修改那条数据?
- 哪个字段改成哪个值?
import com.mongodb.client.result.UpdateResult;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
//修改 id=1 的数据
Query query = new Query(Criteria.where("id").is("1"));
//把歌名修改为 “new name”
Update updateData = new Update();
updateData.set("name", "new name");
//执行修改,修改返回的结果是一个对象
UpdateResult result = mongoTemplate.updateFirst(query, updateData, Song.class);
//修改的记录数大于 0,表示修改成功
System.out.println(result.getModifiedCount());
先使用条件对象Criteria
构建条件对象Query
实例,然后再调用修改对象Update
的方法.set()
设置需要修改的字段。最后调用mongoTemplate.updateFirst(query, updateData, Song.class)
方法完成修改;第 3 个参数是具体的类
4. 删除数据
只需要精确确定需要删除哪些数据即可。
调用mongoTemplate.remove()
方法即可删除数据,参数是对象,表示需要删除哪些数据
import com.mongodb.client.result.DeleteResult;
@Override
public boolean delete(String songId) {
// 输入的主键 id 必须有文本,不能为空或全空格
if (!StringUtils.hasText(songId)) {
LOG.error("input songId is blank.");
return false;
}
Song song = new Song();
song.setId(songId);
//执行删除
DeleteResult result = mongoTemplate.remove(song);
//删除的记录数大于 0,表示删除成功
return result != null && result.getDeletedCount() > 0;
}
创建一个对象并设置好属性值,作为删除的条件,符合条件的数据都将被删除。可以设置更多的属性值来提高精确性,但通过主键来删除数据,是保证不误删的一个比较好的办法
二、Spring Data Query
上面的查询是根据主键来查询的,但这显然是不能满足的。
查询的条件复杂一些,返回的数据很多。
核心方法:
List<Song> songs = mongoTemplate.find(query, Song.class);
第一个参数是查询对象Query
实例,第二个参数就表示查询什么样的对象。
查询操作的复杂性在于条件,需要用构建好的Criteria
条件对象的实例,来构建Query
实例:
Query query = new Query(criteria);
构建Criteria
条件对象,一般有两种情况:
- 单一条件。用
Criteria criteria1 = Criteria.where("条件字段名").is("条件值")
即可返回一个条件对象的实例 - 组合条件。根据 or、and 的关系进行组合,多个子条件对象组合成一个总条件对象:
or 关系:
Criteria criteria = new Criteria();
criteria.orOperator(criteria1, criteria2);
and 关系:
Criteria criteria = new Criteria();
criteria.andOperator(criteria1, criteria2);
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Criteria;
public List<Song> list(Song songParam) {
// 总条件
Criteria criteria = new Criteria();
// 可能有多个子条件
List<Criteria> subCris = new ArrayList();
if (StringUtils.hasText(songParam.getName())) {
subCris.add(Criteria.where("name").is(songParam.getName()));
}
if (StringUtils.hasText(songParam.getLyrics())) {
subCris.add(Criteria.where("lyrics").is(songParam.getLyrics()));
}
if (StringUtils.hasText(songParam.getSubjectId())) {
subCris.add(Criteria.where("subjectId").is(songParam.getSubjectId()));
}
// 必须至少有一个查询条件
if (subCris.isEmpty()) {
LOG.error("input song query param is not correct.");
return null;
}
// 三个子条件以 and 关键词连接成总条件对象,相当于 name='' and lyrics='' and subjectId=''
criteria.andOperator(subCris.toArray(new Criteria[]{}));
// 条件对象构建查询对象
Query query = new Query(criteria);
// 限定输出,以免查询数据量太大
query.limit(10);
List<Song> songs = mongoTemplate.find(query, Song.class);
return songs;
}
三。Spring Data 分页
只需要调用PageRequest.of()
方法构建一个分页对象,然后注入到查询对象即可。
PageRequest.of()
方法第一个参数是页码,从 0 开始计数,第一页的值是 0;第二个参数是每页的数量。
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
Pageable pageable = PageRequest.of(0, 20);
query.with(pageable);
对于分页来说,除了要查询结果意外,还需要查询总数,才能进一步计算出总共多少页,实现完整的分页功能。所以还需要两个步骤:
- 调用
count(query, XXX.class)
方法查询总数。第一个参数是查询条件,第二个参数表示查询什么样的对象。 - 根据结果、分页条件、总数三个数据,构建分页器对象
import org.springframework.data.domain.Page;
import org.springframework.data.repository.support.PageableExecutionUtils;
//总数
long count = mongoTemplate.count(query, Song.class);
//构建分页器
Page<Song> pageResult = PageableExecutionUtils.getPage(songs, pageable, new LongSupplier() {
@Override
public long getAsLong() {
return count;
}
});
PageableExecutionUtils.getPage()
方法第一个参数是查询结果;第二个参数是分页条件对象;第三个参数实现一个LongSupplier
接口的匿名类,在匿名类的getAsLong()
方法中返回结果总数。方法返回值是一个Page
分页器对象。