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;

  @Autowired
  private 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);

  @Autowired
  private SubjectService subjectService;

  @Autowired
  private SongService songService;

  @PostConstruct
  public 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);

  @Autowired
  private MongoTemplate mongoTemplate;//实例注入

  @Override
  public Song add(Song song) {
    return mongoTemplate.insert(song);
  }

  @Override
  public Song get(String songId) {
    return mongoTemplate.findById(songId,Song.class);
  }

  @Override
  public List<Song> list(Song songParam) {
    // 本节课暂时不用修改
    List<Song> songs = new ArrayList<>();

    return songs;
  }

  @Override
  public 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;

  }

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

spider--某站搜索--自动化dp

免责声明&#xff1a;本文仅作分享&#xff01; 自动化&#xff1a; DrissionPage DrissionPage官网 import time from DrissionPage import ChromiumPage,ChromiumOptions import pandas as pd# 这里配置了浏览器路径&#xff0c;不配置的话直接 page ChromiumPage() co Ch…

学成在线day07

视频处理 技术方案 掌握了xxl-job的分片广播调度方式&#xff0c;下边思考如何分布式去执行学成在线平台中的视频处理任务。 任务添加成功后&#xff0c;对于要处理的任务会添加到待处理任务表中&#xff0c;现在启动多个执行器实例去查询这些待处理任务&#xff0c;此时如何…

vsftpd 的安装和应用(超详细!!!)

FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#xff09;是一种用于在计算机网络上进行文件传输的标准协议。它允许用户从一台计算机向另一台计算机上传或下载文件。FTP的工作原理涉及到客户端和服务器之间的交互&#xff0c;以及数据传输的过程。 一、FT…

学习笔记:黑马程序员JavaWeb开发教程(2024.11.29)

10.5 案例-部门管理-新增 如何接收来自前端的数据: 接收到json数据之后&#xff0c;利用RequestBody注解&#xff0c;将前端响应回来的json格式的数据封装到实体类中 对代码中Controller层的优化 发现路径中都有/depts&#xff0c;可以将每个方法对应请求路径中的…

基于Java的小程序电商商城开源设计源码

近年来电商模式的发展越来越成熟&#xff0c;基于 Java 开发的小程序电商商城开源源码&#xff0c;为众多开发者和企业提供了构建个性化电商平台的有力工具。 基于Java的电子商城购物平台小程序的设计在手机上运行&#xff0c;可以实现管理员&#xff1b;首页、个人中心、用户…

JiaJia-CP-1,2,3的WP(1)

一.JiaJia-CP-1 这是ctfshow里电子取证里面的题&#xff0c;以下下是我做题时的WP 审题&#xff0c;最后提交格式要进行md5 加密&#xff0c;给各位CTFer们找了一个md5加密的网站&#xff08;加紧收藏哦&#xff09;&#xff1a; MD5 在线加密工具 | 菜鸟工具 1.拿到题目&am…

微信小程序下拉刷新与上拉触底的全面教程

微信小程序下拉刷新与上拉触底的全面教程 引言 在微信小程序的开发中,用户体验至关重要。下拉刷新和上拉触底是提高用户交互体验的重要功能,能够让用户轻松获取最新数据和内容。本文将详细介绍这两个功能的实现方式,结合实际案例、代码示例和图片展示,帮助开发者轻松掌握…

Vision Transformer(vit)的主干

图解&#xff1a; 代码&#xff1a; class VisionTransformer(nn.Module):def __init__(self, img_size224, patch_size16, in_c3, num_classes1000,embed_dim768, depth12, num_heads12, mlp_ratio4.0, qkv_biasTrue,qk_scaleNone, representation_sizeNone, distilledFalse,…

无人机的起降装置:探索起飞和降落的秘密 !

一、起降系统的运行方式 起飞方式 垂直起飞&#xff1a;小型无人机通常采用垂直起飞方式&#xff0c;利用螺旋桨产生的升力直接从地面升起。这种方式适用于空间有限或需要快速起飞的场景。 跑道起飞&#xff1a;大型无人机或需要较长起飞距离的无人机&#xff0c;可能会采用…

【VUE3】npm : 无法加载文件 D:\Program\nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本。

npm : 无法加载文件 D:\Program\nodejs\npm.ps1。未对文件 D:\Program\nodejs\npm.ps1 进行数字签名。无法在当前系统上运行该脚本。有关运行脚本和设置执行策略的详细信息&#xff0c;请参阅 https:/go.microsoft.com/fwlink/?LinkID135170 中的 about_ Execution_Policies。…

跨平台应用开发框架(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.输入…

Mybatis集成篇(一)

Spring 框架集成Mybatis 目前主流Spring框架体系中&#xff0c;可以集成很多第三方框架&#xff0c;方便开发者利用Spring框架机制使用第三方框架的功能。就例如本篇Spring集成Mybatis 简单集成案例&#xff1a; Config配置&#xff1a; Configuration MapperScan(basePack…

【Debug】hexo-github令牌认证 Support for password authentication was removed

title: 【Debug】hexo-github令牌认证 date: 2024-07-19 14:40:54 categories: bug解决日记 description: “Support for password authentication was removed on August 13, 2021.” cover: https://pic.imgdb.cn/item/669b38ebd9c307b7e9f3e5e0.jpg 第一章 第一篇博客记录一…

算法笔记:力扣105从前序与中序遍历序列构造二叉树

首先重要的是要明白前序遍历&#xff0c;和中序遍历的含义&#xff1b; 前序遍历&#xff1a;根左右中序遍历&#xff1a;左根右 那么在前序遍历的数组中&#xff0c;第一位一定是根节点&#xff0c;而中序遍历数组中&#xff0c;根节点的左边部分就是该节点的左子树&#xf…

el-selet下拉菜单自定义内容,下拉内容样式类似表格

<el-form-item label"角色:" prop"username"><el-selectv-model"value"placeholder"Select"popper-class"role_select"><el-option disabled><div class"flex"><div style"width…

数据追踪技术有哪些?如何实现的?

在数字化时代&#xff0c;数据成为了业务决策和市场营销的关键资源。用户行为分析作为数据驱动的一部分&#xff0c;通过数据追踪技术帮助企业了解用户行为、趋势和偏好&#xff0c;从而制定更加精准的战略。本文将深入探讨数据追踪技术在用户行为分析中的神秘面纱&#xff0c;…

2024年信号处理与神经网络应用会

重要信息 会议时间&#xff1a;2024年12月13-15日 会议地点&#xff1a;中国武汉 会议官网&#xff1a;www.spnna.org 会议简介 2024年信号处理与神经网络应用&#xff08;SPNNA 2024&#xff09;将于2024年12月13日至15日在中国武汉召开。在为全球研究人员、工程师、学者和…

C++11-lambda表达式

目录 1.labmda的表达式 1.1.仿函数的使用 1.2lambda表达式的书写 1.3 lambda的捕获列表 1.3.1传值捕捉 1.3.2mutable可以修改拷贝对象 1.3.3 引用捕获 1.3.4混合捕捉 1.4 函数对象与lambda表达式 1.5 lambda和仿函数的比较 &#x1f33c;&#x1f33c;前言&#xff1a;从…

蓝桥杯模拟题不知名题目

题目:p是一个质数&#xff0c;但p是n的约数。将p称为是n的质因数。求2024最大质因数。 #include<iostream> #include<algorithm> using namespace std; bool fun(int x) {for(int i 2 ; i * i < x ; i){if(x % i 0)return false;}return true; } int main() …

mac访达打开终端

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