Spring Data JPA|至尊荣耀篇

news/2024/11/7 16:38:24/

🙈作者简介:练习时长两年半的Java up主
🙉个人主页:程序员老茶
🙊 ps:点赞👍是免费的,却可以让写博客的作者开兴好久好久😎
📚系列专栏:Java全栈,计算机系列(火速更新中)
💭 格言:种一棵树最好的时间是十年前,其次是现在
🏡动动小手,点个关注不迷路,感谢宝子们一键三连

目录

  • 课程名:Java
    • 内容/作用:知识点/设计/实验/作业/练习
    • 学习:Java
  • Spring Data JPA
  • SpringBoot集成Spring Data JPA
    • 1.创建SpringBoot项目,选择依赖
    • 2.编辑配置文件,设置要连接的数据库信息
    • 3.创建实体类
    • 4.数据访问层接口
    • 5.测试常用方法
  • JPA进阶
    • 分页查询
    • 条件查询
      • 方法命名格式
      • 常用规则
    • 条件分页查询
    • 统计函数分组查询
      • 自定义SQL
      • 自定义SQL中带参数
  • 关联查询
      • 实体类
      • 使用
  • 前后端分离项目
    • 传统项目和前后端分离项目对比
      • 传统项目
      • 前后端分离项目
  • 前后端分离项目后端控制层设计
    • RestFul风格
      • 请求方式设计
      • 返回值设计
        • ResultData类具体设计
  • PostMan
  • 前端页面使用ajax提交单个JSON格式的对象
  • 前端页面使用ajax提交JSON格式的字符串
  • SpringBoot中的文件上传和读取excel
  • 总结与分析

课程名:Java

内容/作用:知识点/设计/实验/作业/练习

学习:Java

Spring Data JPA

2001年推出了Hibernate,是一个全自动ORM框架,可以不用编写SQL语句,就能实现对数据库的操作。

SUN公司在Hibernate的基础上,制定了Java Persistence API(Java持久化API),简称JPA,是一套访问操作数据库的规范,由一系列抽象类和接口组成。

后来Spring团队在SUN公司指定的JPA这套规范下,推出了Spring Data JPA,是JPA这套规范的具体实现。

如今常说的JPA,常指Spring Data JPA。

SpringBoot集成Spring Data JPA

1.创建SpringBoot项目,选择依赖

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IcrGiSis-1685283233954)(Spring Data JPA.assets/image-20230522091237459.png)]

2.编辑配置文件,设置要连接的数据库信息

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/bookdb?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
# 指定数据库类型
spring.jpa.database=mysql
# 打印sql语句
spring.jpa.show-sql=true

3.创建实体类

  • 类上加@Entity注解,来自于javax.presistence包中

  • 主键属性上加

    • @Id标明主键
    • **@GeneratedValue(strategy = GenerationType.IDENTITY)**设置MySQL数据库主键生成策略,数据库设置为自增
  • 其他属性名与字段名一致或驼峰命名法

    • 如果字段名和属性名不一致,使用**@Column(name=“字段名”)**注解指定该属性对应的字段名
@Entity
@Data
public class BookInfo {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer bookId;private Integer typeId;private String bookName;private String bookAuthor;private Integer bookNum;private Integer bookPrice;@Column(name = "publisher_date")private String date;private String bookImg;
}

4.数据访问层接口

  • 类上添加@Repository主键

  • 继承JpaRepository<T,ID>接口,T表示实体类的类型,ID表示主键的数据类型

@Repository
public interface BookInfoDao extends JpaRepository<BookInfo,Integer> {}

5.测试常用方法

方法名返回值说明
findAll()List查询所有数据
findById(主键)Optional根据主键查询,返回的对象调用isPresent()结果为true,表示查询到了数据,调用get()得到查询的数据
findAllById(主键集合)List根据主键集合查询
save(T entity)T添加或修改。如果参数对象中没有主键属性,执行添加,返回添加后的对象,会包含自增ID;如果参数对象中有主键属性且值存在,执行修改,返回修改后的对象。
delete(T entity)void根据对象删除。如果对象中有主键属性且值存在,执行删除。
deleteAll(对象集合)void根据对象集合删除。如果对象中有主键属性且值存在,执行删除。

JPA进阶

分页查询

调用数据访问层中**findAll(Pageable pageable)**方法,即可实现分页。

参数Pageable是一个接口,通过其实现类PageRequest的静态方法**of(int page,int size)**获取对象,

当做Pageable使用,page从0开始表示第一页。

@Test
void test4(){//PageRequest.of(0, 5)PageRequest是Pageable的实现类,通过该构造方法创建对象,默认0表示第一页Page<BookInfo> pageInfo = bookInfoDao.findAll(PageRequest.of(0, 5));System.out.println("总记录数"+pageInfo.getTotalElements());System.out.println("总页数"+pageInfo.getTotalPages());System.out.println("当前页"+pageInfo.getNumber());System.out.println("当前页显示的数量"+pageInfo.getSize());System.out.println("分页查询到的数据集合"+pageInfo.getContent());System.out.println("是否是首页"+pageInfo.isFirst());System.out.println("是否是尾页"+pageInfo.isLast());System.out.println("是否是有下一页"+pageInfo.hasNext());System.out.println("是否是首上一页"+pageInfo.hasPrevious());
}

条件查询

在JPA中,使用自定义方法名自动生成对应的SQL语句,实现条件查询。

如定义了queryById(int id),表示根据id查询,生成select * from 表 where id=?

方法命名格式

[xxx] [By] [字段对应的属性名] [规则] [Or/And] [字段对应的属性名] [规则]…

  • xxx可以是find、get、query、search

  • 方法如果有参数,参数的属性和方法名中的参数顺序一致

如findByBookNameAndBookAuthor(String bookName,String bookAuthor)

对应的sql语句为select * from book_info where book_name =? and book_author=?

常用规则

规则方法名SQL中的条件
指定值findByBookName(String name)book_name = name
Or/AndfindByBookNameOrBookAuthor(String name,String author)book_name=name or book_author=author
After/BeforefindByBookPriceAfter(double price)book_price > price
GreaterThanEqual/LessThanEqualgetByBookNumLessThanEqual(int num)book_num <= num
BetweengetByBookPriceBetween(int a,int b)book_price between a and b
In/NotIngetByBookAuthorIn(String… authors)
getByBookAuthorIn(Collection authors)
book_author in (?,?,?..)
IsNull/IsNotNullqueryByDateIsNull()date is null
Like/NotLikequeryByBookNameLike(String keyword)book_name like ‘%keyword%’,实参为%keyword%
Containts/NotContainsqueryByBookNameContains(String keyword)book_name like ‘%keyword%’,实参为keyword
StartsWith/EndsWithqueryByBookAuthorStartsWith(String keyword)book_author like ‘keyword%’
无条件排序:searchByOrderBy字段Desc/AscsearchByOrderByBookId(int typeId)order by book_id asc
有条件排序:searchBy条件OrderBy字段Desc/AscsearchByTypeIdOrderByBookIdDesc(int typeId)type_id=type_id order by book_id desc
@Repository
public interface BookInfoDao extends JpaRepository<BookInfo, Integer> {//根据书名查询  book_name=?List<BookInfo> findByBookName(String bookName);//查询指定作者和大于指定价格  book_author=? and book_price>?List<BookInfo> findByBookAuthorAndBookPriceAfter(String author, Integer price);//查询库存小于等于指定值 book_num<=?List<BookInfo> getByBookNumLessThanEqual(Integer num);//开区间范围查询   book_price>? and book_price<?List<BookInfo> getByBookPriceAfterAndBookPriceBefore(Integer a, Integer b);//闭区间范围查询   book_price between a and bList<BookInfo> getByBookPriceBetween(Integer a, Integer b);//空值查询  date is nullList<BookInfo> queryByDateIsNull();//模糊查询  like 自定义参数    book_name like ?List<BookInfo> queryByBookNameLike(String keyword);//模糊查询  like '%参数%'    book_name like '%?%'List<BookInfo> queryByBookNameContains(String keyword);//模糊查询 like 金%          book_name like '?%'  List<BookInfo> queryByBookAuthorStartsWith(String keyword);//无条件排序                 order by book_id descList<BookInfo> searchByOrderByBookIdDesc();//条件排序                 type_id=? order by book_price ascList<BookInfo> searchByTypeIdOrderByBookPrice(Integer typeId);}

条件分页查询

只需在定义的方法的参数里,添加Pageable参数;方法的返回值改为Page类型

//多条件分页  根据书名和作者的关键字分页查询
Page<BookInfo> getByBookNameContainsAndBookAuthorContains(String bookName, String bookAuthor, Pageable pageable);

统计函数分组查询

自定义SQL

在数据访问层接口中的方法上,可以通过@Query注解,自定义SQL语句。

默认要使用HQL(Hibernate)专用格式的SQL语句。

如果要使用原生的SQL语句,需要添加nativeQuery=true属性,用value属性定义SQL语句。

//每个作者的图书数量
@Query(nativeQuery = true,value = "select book_author,count(book_id) from book_info group by book_author")
List testQuery();
List list = bookInfoDao.testQuery();
//遍历查询到的数据的每一行
for (Object row : list) {//将每一行转换为数组Object[] obj = (Object[]) row;//自定义的sql要查询哪些数据就获取对应的索引System.out.println(obj[0] +"--"+ obj[1]);
}

自定义SQL中带参数

SQL语句中的":xxx"表示参数

如果方法的形参名和xxx名称一致时直接使用;如果不一致,在形参上加入@Param设置SQL中参数的名称。

//根据作者查询该作者的图书总库存
@Query(nativeQuery = true,value = "select book_author,sum(book_num) from book_info where book_author = :zuozhe")
//List testQuest2(String zuozhe);
List testQuest2(@Param("zuozhe") String xxx);

关联查询

主表book_type

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yu1ylYgS-1685283233956)(Spring Data JPA.assets/image-20230522152545307.png)]

从表book_info

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Hfpp0rN-1685283233957)(Spring Data JPA.assets/image-20230522152606894.png)]

实体类

主表实体BookType

@Entity
@Data
@Table(name = "book_type")
public class BookType {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer typeId;private String typeName;
}

从表实体Book

  • 无需写出外键字段属性
  • 额外添加外键字段对应的实体对象属性
@Entity
@Data
@Table(name = "book_info")
public class Book {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer bookId;//private Integer typeId;//外键字段private String bookName;private String bookAuthor;private Integer bookNum;private Integer bookPrice;@Column(name = "publisher_date")private String date;private String bookImg;//外键对应的实体对象//多对一查询,以当前从表信息为主,关联主表信息@JoinColumn(name = "type_id")//使用type_id进行子查询@ManyToOne//多对一private BookType bt;
}

使用

  • 查询时正常调用数据访问层中的方法,会自动给外键字段对应的对象赋值
@Test
void test(){bookInfoDao.findAll().forEach(System.out::println);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wlgkpq9c-1685283233958)(Spring Data JPA.assets/image-20230522153712332.png)]

  • 添加时,由于没有外键字段对应的属性,赋值时需要通过外键对象.外键字段进行赋值

@RequestMapping("/insert")
@ResponseBody
public Book saveOrUpdate(Book book) {System.out.println(book);return bookInfoDao.save(book);
}

实际访问时给type_id字段传参,需要写为bt.typeId

http://localhost:8080/insert?bookName=xx&bookAuthor=yy&bookNum=1&bookPrice=3&bt.typeId=2
  • 如果要使用外键字段查询时,条件命名为外键对象名_外键对象属性名,如Bt_TypeId。

    该方法同时也是多对一查询

    //根据type_id查询
    //findBy外键对象名_外键对象属性名
    List<Book> findByBt_TypeId(Integer typeId);
    

前后端分离项目

前后端分离,就是将web应用中的前端页面和后端代码分开完成、部署。

  • 前后端的开发者只需完成各自的事情,最终以文档的形式约定数据接口(URL、参数、返回值、请求方式等)
  • 前后端分别部署在各自的服务器中
  • 后端只负责处理数据并提供访问接口(路径),以RESTFul风格的JSON格式传输数据
  • 前端只负责渲染页面和展示数据

传统项目和前后端分离项目对比

传统项目

前端和后端的代码运行在一个服务器上,页面经由controller跳转

SSM项目、图书管理系统、答题系统

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HBlPgEOf-1685283233959)(Spring Data JPA.assets/image-20230523091503776.png)]

前后端分离项目

前后端的代码分别运行在各自的服务器上

后端提供JSON格式字符串的数据

前端负责跳转、解析JSON数据。

酒店房间管理系统

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oGeIu5Uu-1685283233960)(Spring Data JPA.assets/image-20230523091924388.png)]

前后端分离项目后端控制层设计

RestFul风格

风格,不是标准,可以不用强制遵循。

RESTFul分格:用不同的请求方式去访问同一个URL地址时,执行不同的操作。

特点

  • 通过URL就能知道当前在哪个模块
  • 用不同的请求方式决定执行某个操作
  • 通过返回的状态码得到操作结果

使用RESTFul风格和普通方式对比

普通方式

localhost:8080/book/queryAll								查询所有
localhost:8080/book/findById?id=1001						查询单个
localhost:8080/book/insert?bookName=xx&bookAuthor=yy		添加
localhost:8080/book/update?bookName=xx&bookAuthor=yy		修改
localhost:8080/book/delelte?id=1001							删除单个             

RESTFul风格

localhost:8080/book								查询所有,使用get请求
localhost:8080/book/1001						查询单个,使用get请求
localhost:8080/book								添加,使用post请求
localhost:8080/book								修改,使用put请求
localhost:8080/book/1001						删除单个,使用delete请求             

请求方式设计

  • 在请求映射的命名上,统一使用小写字母的名词形式表示当前位于哪个模块。如/book,/user,/student

  • 访问时如果要传参,使用"/模块名/参数"方式,配合controller中的@PathVariable获取

    @GetMapping("/student/{id}")
    public Student findById(@PathVariable("id")Integer id){return service.findById(id);
    }
    
  • 在controller的方法上,使用@XXXMapping()设置访问该方法的请求方式

    • @GetMapping(“路径”) 查询
    • @PostMapping(“路径”) 添加
    • @PutMapping(“路径”) 修改
    • @DeleteMapping(“路径”) 删除
    • @RequestMapping(value=“路径”,method=RequestMethod.GET/POST/PUT/DELETE)
  • 如果访问时的请求方式不匹配,会报405异常

  • 在同一个controller中,不能出现两个请求方式和路径都一致的方法

返回值设计

前后端分离项目的controller方法的返回值也需要进行同一。

返回值通常包含以下信息

  • 传递状态,用数字表示。Integer code;
  • 传递消息,用字符串表示。String msg;
  • 传递集合,用集合表示。List list;
  • 返回对象,用对象表示。Object obj;

将这些信息封装到一个对象中,这个对象称为返回结果类ResultData对象。

ResultData类具体设计

@Data
public class ResultData<T>{private Integer code;//0表示正常,其他自定义private String msg;private List<T> list;private Object obj;//查询返回集合时public ResultData(String msg,List<T> list){this.code=0;this.msg=msg;this.list=list;}//返回对象时,如分页查询、添加或修改public ResultData(String msg,Object obj){this.code=0;this.msg=msg;this.obj=obj;}//无需返回数据时,如删除public ResultData(String msg){this.code=0;this.msg=msg;}//请求失败时public ResultData(Integer code,String msg){this.code=code;this.msg=msg;}public static<T> ResultData ok(String msg,List<T> list){return new ResultData(msg,list);}public static ResultData ok(String msg,Object obj){return new ResultData(msg,obj);}public static ResultData ok(String msg){return new ResultData(msg);}public static ResultData error(Integer code,String msg){return new ResultData(code,msg);}
}

PostMan

Postman API Platform | Sign Up for Free

HTTP测试工具

前端页面使用ajax提交单个JSON格式的对象

$.ajax({url:"http://localhost:8080/unit",data:JSON.stringify(unitInfo),contentType:"application/json;charset=utf-8",//发送的数据格式为jsondataType:"json",//发送的数据格式为jsontype:"post"
});
@PostMapping("/unit")
public Unit saveOrUpdate(@RequestBody Unit unit){//如果前端提交JSON对象,需要给参数添加@RequestBody注解//return unitDao.save(unit);System.out.println(unit);return null;
}

前端页面使用ajax提交JSON格式的字符串

$.ajax({url:"http://localhost:8080/unit",type:"post",data:{userList:JSON.stringify(userList),unitInfo:JSON.stringify(unitInfo)},traditional:true,//发送原生数据success:function(res){if(res.code==0){alert(res.msg);}}
});
@PostMapping("/unit")
public ResultData saveOrUpdate(@RequestParam("unitInfo") String jsonUnit, @RequestParam("userList") String jsonUsers) throws JsonProcessingException {//将JSON格式字符串转换为对应的对象//System.out.println(jsonUnit);//System.out.println(jsonUsers);//ObjectMapper类用于JSON字符串和对象直接转换ObjectMapper jsonTool = new ObjectMapper();//将某个json字符串转换为对应的对象Unit unit = jsonTool.readValue(jsonUnit, Unit.class);//添加成功后,获取添加的自增idunit = unitDao.save(unit);//将某个json字符串转换为对应的对象数组Userinfo[] userinfos = jsonTool.readValue(jsonUsers, Userinfo[].class);//遍历数组,给每个userinfo对象设置unit参数for (Userinfo userinfo : userinfos) {userinfo.setUnit(unit);//userinfoDao.save(userinfo);}//批量添加userinfoDao.saveAll(Arrays.asList(userinfos));return ResultData.ok("添加成功");
}

SpringBoot中的文件上传和读取excel

在application.properties中

#上传文件大小限制 10M
spring.servlet.multipart.max-file-size=10485760

上传表单

<form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data"><input type="file" name="file" /><input type="submit" />
</form>

easyexcel依赖

<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.1</version>
</dependency>

controller

@PostMapping("/upload")
public ResultData upload(MultipartFile file) throws IOException {String oldName = file.getOriginalFilename();//获取当前项目根目录绝对路径,即D:\230202\framework\demo练习\sbjpaString contextPath = System.getProperty("user.dir");File target = new File(contextPath + "\\src\\main\\resources\\static\\upload", UUID.randomUUID() + oldName.substring(oldName.lastIndexOf(".")));file.transferTo(target);//读取excelEasyExcel.read(target, Userinfo.class, new PageReadListener<Userinfo>(dataList -> {userinfoDao.saveAll(dataList);//target.delete();})).sheet().doRead();return ResultData.ok("批量添加成功");
}

实体类

@Entity
@Data
public class Userinfo {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer id;@ExcelProperty("姓名")private String username;@ExcelProperty("密码")private String password;private String sex;private Integer userState;private String idcard;@JoinColumn(name = "unit_id")@ManyToOneprivate Unit unit;
}

excel文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bZBODSFC-1685283233961)(Spring Data JPA.assets/image-20230523175726900.png)]

总结与分析

   好好学习,天天向上。

往期专栏
Java全栈开发
数据结构与算法
计算机组成原理
操作系统
数据库系统
物联网控制原理与技术

http://www.ppmy.cn/news/99939.html

相关文章

如何在Java中使用回调函数

目录 背景Java中的同步回调匿名内部类中的回调lambda的回调异步回调函数简单线程回调平行执行的异步回调CompletableFuture中的回调结论 背景 在Java中一个回调的操作是一个在一些操作完成之后被传递到另一个函数中并且被执行的函数。一个回调函数既可以被同步或者异步执行。在…

HTML <col> 标签

实例 col 元素为表格中的三个列规定了不同的对齐方式: <table width="100%" border="1"><col align="left" /><col align="left" /><col align="right" /><tr><th>ISBN</th>&…

k8s实战篇1-用minikube发布服务hello-minikube

1 安装minikube curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-darwin-amd64 sudo install minikube-darwin-amd64 /usr/local/bin/minikube 2 Install kubectl binary with curl on macOS 1 Download the latest release: curl -LO "h…

云上高校导航 开发指引 与 注意事项

&#x1f52c; 注意事项 大部分数据存储在utils.js中的&#xff0c;页面通过引入utils.js方式渲染数据 图标全部存储在项目images文件夹里,均下载自 iconfont网站&#xff08;自行替换&#xff09; 部分图片引用自 免费图床 - CDN加速图床&#xff08;自行替换&#xff09; …

Flume系列:Flume数据监控Ganglia

目录 Apache Hadoop生态-目录汇总-持续更新 安装说明 1&#xff09;安装 ganglia 2&#xff09;在 worker213 修改配置文件 3&#xff09;在 所有服务器 修改配置文件/etc/ganglia/gmond.conf 4&#xff09;启动 ganglia 5&#xff09;打开网页浏览 ganglia 页面 6&…

linux 条件变量 pthread_cond_signal

专栏内容&#xff1a;linux下并发编程个人主页&#xff1a;我的主页座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物&#xff0e; 目录 前言 简介 应用场景 与互斥量/信号量的区别 接口介绍 变量定义 初始化 等待被唤…

2023上半年软考记录

关注软考的小朋友们&#xff0c;应该都知道每年的5月份是软考的时间&#xff0c;鄙人有幸参与了这次的考试&#xff0c;在此记录一下。主要是考试相关的内容&#xff0c;包含使用的笔、答题卡等。 一、考前准备 首先&#xff0c;就不多说了&#xff0c;还是要学一下的&#x…

SpringBoot整合EasyExcel

SpringBoot整合EasyExcel 1.导入依赖 添加maven依赖, 依赖的poi最低版本3.17 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.3</version> </dependency>2.创建实体类 NoArg…