Spring Data

//安装MongoDB
sudo docker run -itd --name mongo -p 27017:27017 registry.cn-hangzhou.aliyuncs.com/ykd_project/mongo:7.0.12//验证是否启动成功
sudo docker exec -it mongo mongosh admin//创建数据库
use practice//退出登录
exit

Spring Data CRUD

在配置完毕 MongoDB 以后,就可以编程操作了。

对数据库的操作一定要放在 @Service 类中,而不是放在 @Controller 类中;且 @Controller 类可以调用 @Service 类的方法,反之则不行。这是 SpringMVC 的经典架构设计理念。

  • @Service 类主要用于不易变的核心业务逻辑。
  • @Controller 类与前端页面紧密配合,调用 @Service 服务读写数据,从而响应前端请求,

这个设计大家可能目前没有体会,但是没关系,希望在学习、实战中持续理解和思考。

1. 新增数据

新增数据就是向数据库中插入一条数据。在 Java 世界中万物皆对象,所以,所谓数据就是实例对象。

import org.springframework.data.mongodb.core.MongoTemplate;@Autowiredprivate MongoTemplate mongoTemplate;public void test() {Song song = new Song();song.setSubjectId("s001");song.setLyrics("...");song.setName("成都");mongoTemplate.insert(song);}

在本课程第 2 章已经学过,使用 @Autowired 可以让系统自动注入 MongoTemplate 的实例。

只需要调用 mongoTemplate.insert() 方法就能把对象存入数据库。非常简单。

页面上打印出了新增的结果歌曲,系统会自动为 id 属性(主键)赋值:

{"songResult":{"id":"5e55db3a461b9b3c3e1d6a56","name":"成都","lyrics":"...","subjectId":"s001"}}

2. 查询数据

一条语句就可以了:

mongoTemplate.findById(songId, Song.class)

注意:findById() 方法第 1 个参数就是主键 id,第 2 个参数是具体的类,写法是 类名.class

3. 修改数据

修改数据的语句就略复杂一些了。因为修改的操作包括两个部分:

  1. 修改哪条数据?
  2. 哪个字段修改成什么值?

所以,修改操作也分为部分:修改条件和修改的字段。

// 修改 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 个参数是具体的类。

本修改数据的演示中, 使用了约定:主键不能修改;且其它字段值为 null 表示不修改,值为长度为 0 的字符串 "" 表示清空此字段。

这样,我们就可以使用一个 Song 对象区分出“哪些字段需要修改”、“哪些字段不需要修改”、“哪些字段需要清除值”等多种情况。服务接口只需要 Song 对象一个参数即可,使用起来也比较简便。

大家需要思考和体会这段逻辑,在今后的工作中能够根据实际情况灵活运用哦。

4. 删除数据

删除数据也比较简单,只需要精确确定需要删除哪什么数据即可。

调用 mongoTemplate.remove() 方法即可删除数据,参数是对象,表示需要删除哪些数据。

示例代码:

Song song = new Song();
song.setId(songId);// 执行删除
DeleteResult result = mongoTemplate.remove(song);
// 删除的记录数大于 0 ,表示删除成功
System.out.println("删除的数据记录数量:" + result.getDeletedCount());

创建一个对象并设置好属性值,作为删除的条件,符合条件的数据都将被删除。可以设置更多的属性值来提高精确性,但通过主键来删除数据,是保证不误删的一个比较好的办法。

删除数据一定要谨慎哦

由于第一步执行新增数据, ID 是成动态生的,所以一步不知道具体该删除哪一条数据,所以会失败。但是没关系,接下来的作业中自己实现并验证。

数据库操作中,用的最少的操作是删除,用的最多的操作就是查询了,除了根据主键查询,更多的是需要根据条件查询。

.imp-->im.java-->control

package fm.douban.app.control;import com.alibaba.fastjson.JSON;
import fm.douban.model.Song;
import fm.douban.service.SongService;
import fm.douban.service.SubjectService;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class SongListControl {private static final Logger LOG = LoggerFactory.getLogger(SongListControl.class);@Autowiredprivate SubjectService subjectService;@Autowiredprivate SongService songService;@PostConstructpublic void init() {LOG.error("SongListControl 启动啦");if (subjectService != null) {LOG.info("subjectService 实例注入成功。");} else {LOG.info("subjectService 实例注入失败。");}}@RequestMapping("/songadd")public Map add() {Map returnData = new HashMap();Song song=new Song();song.setLyrics("...");song.setName("ykd");song.setSubjectId("001");Song songResult = songService.add(song);returnData.put("songResult", songResult);return returnData;}@RequestMapping("/songget")public Map get(@RequestParam String id) {Map returnData = new HashMap();Song songResult = songService.get(id);returnData.put("songResult", songResult);return returnData;}@RequestMapping("/songmodify")public Map modify(@RequestParam String id) {Map returnData = new HashMap();Song song = new Song();// 必须指定修改哪一条数据song.setId(id);// 仅演示:用一个随机数代替歌词song.setLyrics(String.valueOf(Math.random()));boolean result = songService.modify(song);returnData.put("modify_success", result);return returnData;}@RequestMapping("/songdelete")public Map delete(@RequestParam String id) {Map returnData = new HashMap();boolean result = songService.delete(id);returnData.put("delete_success", result);return returnData;}private Song buildSong() {Song song = new Song();// song 对象的 id 主键不要赋值,MongoDB 会自动填入唯一的随机字符串song.setSubjectId("s001");song.setLyrics("...");song.setName("成都");song.setId("001");Song songResult = songService.add(song);return songResult;}@RequestMapping("/songcheck")public Map check() {Map returnData = new HashMap();// 新增一条数据,失败则中断Song addedSong = buildSong();String id = addedSong.getId();returnData.put("add_song_id", id);if (id != null) {returnData.put("add_result", true);} else {returnData.put("add_result", false);return returnData;}// 读取刚刚新增的数据Song getResult = songService.get(id);if (getResult != null) {returnData.put("get_result", true);} else {returnData.put("get_result", false);return returnData;}getResult.setName("成都-2");getResult.setLyrics("......");boolean modifyResult = songService.modify(getResult);Song modifiedSong = songService.get(id);if (modifyResult && modifiedSong != null && "成都-2".equals(modifiedSong.getName())&& "......".equals(modifiedSong.getLyrics())) {returnData.put("modify_result", true);} else {returnData.put("modify_result", false);return returnData;}boolean delResult = songService.delete(id);Song deletedSong = songService.get(id);if (delResult && deletedSong == null) {returnData.put("delete_result", true);} else {returnData.put("delete_result", false);}LOG.info("check song CRUD service result");LOG.info(JSON.toJSONString(returnData));return returnData;}}package fm.douban.service.impl;import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import fm.douban.model.Song;
import fm.douban.service.SongService;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;@Service
public class SongServiceImpl implements SongService {private static final Logger LOG = LoggerFactory.getLogger(SongServiceImpl.class);@Autowiredprivate MongoTemplate mongoTemplate;//实例注入@Overridepublic Song add(Song song) {return mongoTemplate.insert(song);}@Overridepublic Song get(String songId) {return mongoTemplate.findById(songId,Song.class);}@Overridepublic List<Song> list(Song songParam) {// 本节课暂时不用修改List<Song> songs = new ArrayList<>();return songs;}@Overridepublic boolean modify(Song song) {Query query=new Query(Criteria.where("id").is(song.getId()));Update update=new Update();update.set("name",song.getName());UpdateResult updateResult=mongoTemplate.updateFirst(query,update,Song.class);return updateResult!=null&&updateResult.getModifiedCount()>0;}@Overridepublic boolean delete(String songId) {Song song=new Song();song.setId(songId);DeleteResult result=mongoTemplate.remove(song);return result!=null&&result.getDeletedCount()>0;}}

Spring Data Query

上节课学习到了根据主键 id 查询数据。但是显然只根据主键查询是不够的,通常情况下需要根据多条件查询。

条件查询就相对复杂一些,但功能也更强大。查询操作的核心方法是:

List<Song> songs = mongoTemplate.find(query, Song.class);

因为可能查询到多条数据,所以返回结果是对象的集合。第一个参数是查询对象 Query 实例;第二个参数就表示查询什么样的对象,写法是 类名.class 。

查询方法比较简单,但查询操作的复杂性在于条件,需要用构建好的 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);
      
    • orOperator()andOperator()的参数,都可以输入多个子条件,也可以输入子条件数组

当然,组合条件情况下,也可以多层组合,子条件也可以是组合而来的。

例如根据歌曲的专辑查询,并限定最多返回 10 条数据:

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;
}

这里用 Song 对象来表示查询条件。作为服务接口,使用对象做参数,具备比较好的扩展性和兼容性。例如,如果增加一个查询条件,就不需要增加方法参数,只需要为参数对象增加属性即可;否则所有的调用查询接口方法的代码都需要做修改,影响面可能很大,扩展性和兼容性都不好。

通常需求越复杂,组合条件就可能越复杂。大家需要仔细琢磨一下 Criteria 条件对象的运用,达到灵活运用的程度后,就可以根据需求任意组合,以满足多变的查询需求哦.

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() {@Overridepublic long getAsLong() {return count;}
});

PageableExecutionUtils.getPage() 方法第一个参数是查询结果;第二个参数是分页条件对象;第三个参数稍微复杂一点,实现一个 LongSupplier 接口的匿名类,在匿名类的 getAsLong() 方法中返回结果总数。方法返回值是一个 Page 分页器对象,使用起来非常方便。

这里做了一次重构,SongService 中的 list() 方法,用自定义的专用的参数类 SongQueryParam 替换原来的 Song 做方法参数。因为分页参数不属于歌曲模型,所以必须重构。

当然,为了分页,返回值也重构为 Page<Song> ,表示方法返回分页结果对象。

实际上,在项目设计的时候就必须考虑到分页功能。本课程只是为了演示方便,在前面的课程中就没有用参数类。

control 中调用 SongService.list() 方法得分页结果对象后,可以调用 Page 的各个方法取得数据集和前端分页器的各种数据值。详情请点此查看文档。

最常用的方法是 getContent() 取得本页的数据集:

Page<Song> songResult = songService.list(queryParam);
List<Song> songs = songResult.getContent();

因为 Page 表示本页的数据对象是 Song ,所以 songResult.getContent() 返回一个列表,同样,泛型指定了对象也是 Song 。

Page 其它的方法是取得当前页码号、总页码数等,就不赘述了,对照上述文档的注释看即可。只是注释为英文,可能不习惯,需要一些耐心,配合有道词典等翻译软件理解。 重要的是 ,自己动手编码看一下各个方法的返回值,对照文档见就更容易理解了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/480892.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一次完整的CNAS软件测试实验室内部审核流程

内部审核是软件测试实验室管理体系重的重要部分&#xff0c;通过内部审核可以为有效的管理评审和纠正、预防措施提供信息&#xff0c;以验证组织的管理体系是否持续的满足规定的要求并且正在运行。 内部审核需要依据文件化的程序&#xff0c;每年至少实施一次&#xff0c;软件…

Matlab数字信号处理——音频信号处理与分析GUI

1.实现内容 实现功能有回响、变声、倒放、变速、音量调整、加噪、设计 FIR和 IR 滤波器实现去噪功能(高通低通带通带阻)&#xff0c;并且在时域波形图和频域波形展示变化。滤波器包括各种参数的选择、滤波器结构和类型的选择等。同时GUI上还包含打开、播放、保存、退出功能。 …

pcb线宽与电流

三十年一路高歌猛进的中国经济&#xff0c; 中国经历了几个三十年&#xff1f; 第一个三十年&#xff1a;以计划为导向。 第二个三十年&#xff1a;以经济为导向。 现在&#xff0c;第三个三十年呢&#xff1f; 应该是以可持续发展为导向。 传统企业摇摇欲坠&#xff0c; 新兴企…

redis命令 及 redis 常见的数据结构

文章目录 一. 核心命令1. set2. get 二. 全局命令1. keys2. exists3. del4. expire5. ttl6. type 三. redis 常见的数据结构 一. 核心命令 1. set set key value key 和 value 都是string类型的 对于key value, 不需要加上引号, 就是表示字符串类型, 加上也可以 redis中, 不…

跨平台应用开发框架(4)----Qt(系统篇)

目录 1.Qt事件 1.事件来源 2.事件处理 3.按键事件 1.组合按键 4.鼠标事件 1.鼠标单击事件 2.鼠标释放事件 3.鼠标双击事件 4.鼠标移动事件 5.滚轮事件 5.定时器 1.QTimerEvent类 2.QTimer 类 3.获取系统日期及时间 6.事件分发器 7.事件过滤器 2.Qt文件 1.输入…

uniapp在App端定义全局弹窗,当打开关闭弹窗会触发onShow、onHide生命周期怎么解决?

在uniapp(App端)中实现自定义弹框&#xff0c;可以通过创建一个透明页面来实现。点击进入当前页面时&#xff0c;页面背景会变透明&#xff0c;用户可以根据自己的需求进行自定义&#xff0c;最终效果类似于弹框。 遇到问题&#xff1a;当打开弹窗(进入弹窗页面)就会触发当前页…

DM达梦管理工具拖出空白区块,无法关闭

1. 出现问题&#xff1a;DM达梦管理工具拖出空白区块&#xff0c;无法关闭。 2. 解决方法 新建查询页&#xff0c;把查询页拖到空白区块里&#xff0c;完全覆盖空白区块。之后空白区块会变成查询页&#xff0c;右上角会出现叉号&#xff0c;点击叉号关闭就行。 3. 后记 达梦…

DevExpress的web Dashboard应用

本文旨在从零开始创建一个包含dashboard的应用 一、前期准备 1、语言&#xff1a;C# 2、软件&#xff1a;Visual Studio 2019 3、框架&#xff1a;DevExpress19.2(付费)、ASP.NET(Web) 4、组件&#xff1a;dashboard 二、创建ASP.NET Web窗体仪表板应用程序 1、创建一个空的w…

【vue-router】Vue-router如何实现路由懒加载

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

go语言切片

切片 切片是一种数据结构&#xff0c;这种数据结构便于使用和管理数据集合。切片是围绕动态数组的概念构建的&#xff0c;可以按需自动增长和缩小。切片的动态增长是通过内置函数 append 来实现的。这个函数可以快速且高效地增长切片。还可以通过对切片再次切片来缩小一个切片的…

2024年一级建造师考试成绩,即将公布!

一级建造师考试成绩一般在考试结束后3个月左右的时间公布&#xff01; 根据官方通知&#xff0c;重庆、江苏、青海、江西、云南、湖南、福建、北京、山西、黑龙江等地在今年一建报名通知里提到&#xff1a;2024年一级建造师考试成绩预计于2024年12月上旬公布。考生可在这个时间…

基于Matlab的图像去噪算法仿真

中值滤波的仿真 本节选用中值滤波法对含有高斯噪声和椒盐噪声的图像进行去噪&#xff0c;并用Matlab软件仿真。 &#xff08;1&#xff09;给图像加入均值为0&#xff0c;方差为0.02的高斯噪声&#xff0c;分别选择33模板、55模板和77模板进行去噪 Matlab部分代码&#xff1…

交通流量预测:基于交通流量数据建立模型

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

嵌入式QT学习第4天:Qt 信号与槽

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 本章思维导图如下&#xff1a; 不使用 Qt Designer 的方式进行开发&#xff0c;用代码绘界面&#xff0c;可以锻炼我们的布局能力&#xff0c;和代码逻辑能力&#x…

多线程+线程池

普通线程的创建 三种创建方式实例&#xff1a; 多线程本质上是毫无关系的&#xff0c;执行顺序是不可预知的&#xff0c;但是由于callable方式创建的对象有返回值所以主函数在执行的时候&#xff0c;需要等待返回值回来才能继续执行其他线程&#xff0c;所以在这种状态下是…

mac访达打开终端

选择文件夹打开 选中文件夹&#xff0c;然后右键即可&#xff1a; 在当前文件夹打开 在访达的当前文件夹长按option键 左下角出现当前文件夹路径 右键即可打开终端

【模型剪枝】YOLOv8 模型剪枝实战 | 稀疏化-剪枝-微调

文章目录 0. 前言1. 模型剪枝概念2. 模型剪枝实操2.1 稀疏化训练2.2 模型剪枝2.3 模型微调总结0. 前言 无奈之下,我还是写了【模型剪枝】教程🤦‍♂️。回想当年,在写《YOLOv5/v7进阶实战专栏》 时,我经历了许多挫折,才最终完成了【模型剪枝】和【模型蒸馏】的内容。当时…

Django 路由层

1. 路由基础概念 URLconf (URL 配置)&#xff1a;Django 的路由系统是基于 urls.py 文件定义的。路径匹配&#xff1a;通过模式匹配 URL&#xff0c;并将请求传递给对应的视图处理函数。命名路由&#xff1a;每个路由可以定义一个名称&#xff0c;用于反向解析。 2. 基本路由配…

单点登录原理

允许跨域–>单点登录。 例如https://www.jd.com/ 同一个浏览器下&#xff1a;通过登录页面产生的cookie里的一个随机字符串的标识&#xff0c;在其他子域名下访问共享cookie获取标识进行单点登录&#xff0c;如果没有该标识则返回登录页进行登录。 在hosts文件下面做的域名…

保持角色一致性!flux新模型redux用法(含模型与工作流)

​ 目录 redux模型是什么&#xff0c;能干啥&#xff1f; 用到的工具有哪些&#xff1f; 工具和模型文件在哪里下载&#xff1f; 整合包&#xff1a; 下载后需要分别放到指定目录&#xff1a; redux模型怎么用&#xff1f; 加载工作流 上传图片和输入提示词 生成结果…