全套SpringBoot讲义01-一https://developer.aliyun.com/article/1491488
JC-2-4.yaml数据读取
对于yaml文件中的数据,其实你就可以想象成这就是一个小型的数据库,里面保存有若干数据,每个数据都有一个独立的名字,如果你想读取里面的数据,肯定是支持的,下面就介绍3种读取数据的方式
读取单一数据
yaml中保存的单个数据,可以使用Spring中的注解直接读取,使用@Value可以读取单个数据,属性名引用方式:${一级属性名.二级属性名……}
记得使用@Value注解时,要将该注入写在某一个指定的Spring管控的bean的属性名上方。现在就可以读取到对应的单一数据行了
总结
- 使用@Value配合SpEL读取单个数据
- 如果数据存在多层级,依次书写层级名称即可
读取全部数据
读取单一数据可以解决读取数据的问题,但是如果定义的数据量过大,这么一个一个书写肯定会累死人的,SpringBoot提供了一个对象,能够把所有的数据都封装到这一个对象中,这个对象叫做Environment,使用自动装配注解可以将所有的yaml数据封装到这个对象中,这样就避免了一个一个value了
数据封装到了Environment对象中,获取属性时,通过Environment的接口操作进行,具体方法时getProperties(String),参数填写属性名即可
总结
使用Environment对象封装全部配置信息
使用@Autowired自动装配数据到Environment对象中
读取对象数据
单一数据读取书写比较繁琐,environment全数据封装又封装的太厉害了,每次拿数据还要一个一个的getProperties(),总之用起来都不是很舒服。由于Java是一个面向对象的语言,很多情况下,我们会将一组数据封装成一个对象。SpringBoot也提供了可以将一组yaml对象数据封装一个Java对象的操作
首先定义一个对象,并将该对象纳入Spring管控的范围,也就是定义成一个bean,然后使用注解@ConfigurationProperties指定该对象加载哪一组yaml中配置的信息。
这个@ConfigurationProperties必须告诉他加载的数据前缀是什么,这样当前前缀下的所有属性就封装到这个对象中。记得数据属性名要与对象的变量名一一对应啊,不然没法封装。其实以后如果你要定义一组数据自己使用,就可以先写一个对象,然后定义好属性,下面到配置中根据这个格式书写即可。
温馨提示
细心的小伙伴会发现一个问题,自定义的这种数据在yaml文件中书写时没有弹出提示,是这样的,咱们到原理篇再揭秘如何弹出提示。
总结
使用@ConfigurationProperties注解绑定配置信息到封装类中
封装类需要定义为Spring管理的bean,否则无法进行属性注入
yaml文件中的数据引用
如果你在书写yaml数据时,经常出现如下现象,比如很多个文件都具有相同的目录前缀
这是linux写法
center: dataDir: /usr/local/fire/data tmpDir: /usr/local/fire/tmp logDir: /usr/local/fire/log msgDir: /usr/local/fire/msgDir
或者(window写法)
center: dataDir: D:/usr/local/fire/data tmpDir: D:/usr/local/fire/tmp logDir: D:/usr/local/fire/log msgDir: D:/usr/local/fire/msgDir
这个时候你可以使用引用格式来定义数据,其实就是搞了个变量名,然后引用变量了,格式如下:
baseDir: /usr/local/fire center: dataDir: ${baseDir}/data tmpDir: ${baseDir}/tmp logDir: ${baseDir}/log msgDir: ${baseDir}/msgDir
还有一个注意事项,在书写字符串时,如果需要使用转义字符,需要将数据字符串使用双引号包裹起来
lesson: "Spring\tboot\nlesson"
总结
- 在配置文件中可以使用${属性名}方式引用属性值
- 如果属性中出现特殊字符,可以使用双引号包裹起来作为字符解析
到这里有关yaml文件的基础使用就先告一段落,在实用篇中再继续研究更深入的内容。
JC-3.基于SpringBoot实现SSMP整合
学习整合第三方技术思想,而非仅仅技术
重头戏来了,SpringBoot之所以好用,就是它能方便快捷的整合其他技术,这一部分咱们就来聊聊一些技术的整合方式,通过这一章的学习,大家能够感受到SpringBoot到底有多酷炫。这一章咱们学习如下技术的整合方式
整合JUnit
整合MyBatis
整合MyBatis-Plus
整合Druid
上面这些技术都整合完毕后,我们做一个小案例,也算是学有所用吧。涉及的技术比较多,综合运用一下。
JC-3-1.整合JUnit
创建工程的时候不用选择web工程,神魔都不选默认即可
我们做的springboot工程可以说是maven工程,它也是一个spring程序,需要spring管理,maven执行它的声明周期过程中,它必须先测试,所以随便创建一个boot工程,junit是自带的
SpringBoot技术的定位用于简化开发,再具体点是简化Spring程序的开发。所以在整合任意技术的时候,如果你想直观感触到简化的效果,你必须先知道使用非SpringBoot技术时对应的整合是如何做的,然后再看基于SpringBoot的整合是如何做的,才能比对出来简化在了哪里。
我们先来看一下不使用SpringBoot技术时,Spring整合JUnit的制作方式
//加载spring整合junit专用的类运行器 @RunWith(SpringJUnit4ClassRunner.class) //指定对应的配置信息 @ContextConfiguration(classes = SpringConfig.class) public class AccountServiceTestCase { //注入你要测试的对象 @Autowired private AccountService accountService; @Test public void testGetById(){ //执行要测试的对象对应的方法 System.out.println(accountService.findById(2)); } }
其中核心代码是前两个注解,第一个注解@RunWith是设置Spring专用于测试的类运行器,简单说就是Spring程序执行程序有自己的一套独立的运行程序的方式,不能使用JUnit提供的类运行方式了,必须指定一下,但是格式是固定的,琢磨一下,每次都指定一样的东西,这个东西写起来没有技术含量啊,第二个注解@ContextConfiguration是用来设置Spring核心配置文件或配置类的,简单说就是加载Spring的环境你要告诉Spring具体的环境配置是在哪里写的,虽然每次加载的文件都有可能不同,但是仔细想想,如果文件名是固定的,这个貌似也是一个固定格式。似然有可能是固定格式,那就有可能每次都写一样的东西,也是一个没有技术含量的内容书写
SpringBoot就抓住上述两条没有技术含量的内容书写进行开发简化,能走默认值的走默认值,能不写的就不写,具体格式如下
@SpringBootTest class Springboot04JunitApplicationTests { //注入你要测试的对象 @Autowired private BookDao bookDao; //bookDao是接口,BookDaoImpl是实现类,覆盖的方法 @Test void contextLoads() { //执行要测试的对象对应的方法 bookDao.save(); System.out.println("two..."); } }
看看这次简化成什么样了,一个注解就搞定了,而且还没有参数,再体会SpringBoot整合其他技术的优势在哪里,就两个字——简化。使用一个注解@SpringBootTest替换了前面两个注解。至于内部是怎么回事?和之前一样,只不过都走默认值。
这个时候有人就问了,你加载的配置类或者配置文件是哪一个?就是我们前面启动程序使用的引导类。如果想手工指定引导类有两种方式,第一种方式使用属性的形式进行,在注解@SpringBootTest中添加classes属性指定配置类
@SpringBootTest(classes = Springboot04JunitApplication.class) class Springboot04JunitApplicationTests { //注入你要测试的对象 @Autowired private BookDao bookDao; @Test void contextLoads() { //执行要测试的对象对应的方法 bookDao.save(); System.out.println("two..."); } }
第二种方式回归原始配置方式,仍然使用@ContextConfiguration-加载配置文件的注解进行,效果是一样的
@SpringBootTest @ContextConfiguration(classes = Springboot04JunitApplication.class) class Springboot04JunitApplicationTests { //注入你要测试的对象 @Autowired private BookDao bookDao; @Test void contextLoads() { //执行要测试的对象对应的方法 bookDao.save(); System.out.println("two..."); } }
温馨提示
使用SpringBoot整合JUnit需要保障导入test对应的starter,由于初始化项目时此项是默认导入的,所以此处没有提及,其实和之前学习的内容一样,用什么技术导入对应的starter即可。
总结步骤
整合junit步骤
导入测试对应的starter
测试类使用@SpringBootTest修饰
使用自动装配的形式添加要测试的对象
测试类如果存在于引导类所在包或子包中无需指定引导类
测试类如果不存在于引导类所在的包或子包中需要通过classes属性指定引导类
如果把测试类随便移动包位置就不能用了,这是why?
报错:java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need t
如果当前的这个测试类在上面的引导类所在的包或者子包下面 ,不需要管,不报错。
@SpringBootTest class Springboot04JunitApplicationTests
引导类:
package com.jixy; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Springboot04JunitApplication { public static void main(String[] args) { SpringApplication.run(Springboot04JunitApplication.class, args); } }
如果测试类不在引导类所在的包或者子包下面就会找不到这个引导类,但我们可以在@SpringBootTest 后面显式的写上引导类是什么,这样才不会报错
把测试类挪到com包下,引导类在com.jixy包下
@SpringBootTest(classes = Springboot04JunitApplication.class) or //@ContextConfiguration(Springboot04JunitApplication.class)
底层是如何找配置类的?
在当前所在的包,也就是com包下面开始找(层级要一样,不光在test下找,还在上面的非测试类src下找),有没有哪一个类加载了@SpringBootConfiguration,有的话则能执行
tips:
@SpringBootApplication内部含有@SpringBootConfiguration这个注解
扩展:整合junit的时候有两个注解
//设置运行器,这个对我们来说不重要,关键是@ContextConfiguration @Runwith // @ContextConfiguration指定对应的配置文件或者配置类是哪一个 //Spring //指定当前测试类在Spring的测试环境中执行,此时就可以通过注入的方式直接获取IOC容器中bean @RunWith(SpringJUnit4ClassRunner.class) //设置Spring测试环境的配置文件 @ContextConfiguration("classpath:spring-jdbc.xml")
被测试的对象在Spring容器中呢,所以我们要拿到Spring容器才行,如果没有按照层次放文件,是拿不到的,或者告诉它配置文件or配置类在哪里
JC-3-2.整合MyBatis
创建工程用什么√勾什么,一个勾就代表一个坐标
整合完JUnit下面再来说一下整合MyBatis,这个技术是大部分公司都要使用的技术,务必掌握。如果对Spring整合MyBatis不熟悉的小伙伴好好复习一下,下面列举出原始整合的全部内容,以配置类的形式为例进行(想想MyBatis用了哪些技术)
导入坐标,MyBatis坐标不能少,Spring整合MyBatis还有自己专用的坐标,此外Spring进行数据库操作的jdbc坐标是必须的,剩下还有mysql驱动坐标,本例中使用了Druid数据源,这个倒是可以不要
<dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!--1.导入mybatis与spring整合的jar包--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <!--导入spring操作数据库必选的包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.10.RELEASE</version> </dependency> </dependencies>
Spring核心配置
@Configuration @ComponentScan("com.itheima") @PropertySource("jdbc.properties") public class SpringConfig { }
- MyBatis要交给Spring接管的bean
//定义mybatis专用的配置类 @Configuration public class MyBatisConfig { // 定义创建SqlSessionFactory对应的bean @Bean public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){ //SqlSessionFactoryBean是由mybatis-spring包提供的,专用于整合用的对象 SqlSessionFactoryBean sfb = new SqlSessionFactoryBean(); //设置数据源替代原始配置中的environments的配置 sfb.setDataSource(dataSource); //设置类型别名替代原始配置中的typeAliases的配置 sfb.setTypeAliasesPackage("com.itheima.domain"); return sfb; } // 定义加载所有的映射配置 @Bean public MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer msc = new MapperScannerConfigurer(); msc.setBasePackage("com.itheima.dao"); return msc; } }
数据源对应的bean,此处使用Druid数据源
@Configuration public class JdbcConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String userName; @Value("${jdbc.password}") private String password; //@Bean("dataSource") 是 Spring 框架中的注解,用于告诉 Spring 容器,将一个方法返回的对象注册为一个 Bean,并指定该 Bean 的名称为 "dataSource"。 @Bean("dataSource") public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(userName); ds.setPassword(password); return ds; } }
数据库连接信息(properties格式)
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false jdbc.username=root jdbc.password=123456
- 上述格式基本上是简格式了,要写的东西还真不少。下面看看SpringBoot整合MyBaits格式
步骤①:创建模块时勾选要使用的技术,MyBatis,由于要操作数据库,还要勾选对应数据库
或者手工导入对应技术的starter,和对应数据库的坐标
<dependencies> <!--1.导入对应的starter--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> </dependencies>
步骤②:配置数据源相关信息,没有这个信息你连接哪个数据库都不知道
#2.配置相关信息 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ssm_db username: root password: 123456 #2.配
完了,就这么多,没了。有人就很纳闷,这就结束了?对,这就结束了,SpringBoot把配置中所有可能出现的通用配置都简化了。下面就可以写一下MyBatis程序运行需要的Dao(或者Mapper)就可以运行了
实体类
public class Book { private Integer id; private String type; private String name; private String description; }
映射接口(Dao)
@Mapper public interface BookDao { @Select("select * from tbl_book where id = #{id}") public Book getById(Integer id); }
测试类
@SpringBootTest class Springboot05MybatisApplicationTests { @Autowired private BookDao bookDao; @Test void contextLoads() { System.out.println(bookDao.getById(1)); } }
完美,开发从此变的就这么简单。再体会一下SpringBoot如何进行第三方技术整合的,是不是很优秀?具体内部的原理到原理篇再展开讲解
注意:当前使用的SpringBoot版本是2.5.4,对应的坐标设置中Mysql驱动使用的是8x版本。当SpringBoot2.4.3(不含)版本之前会出现一个小BUG,就是MySQL驱动升级到8以后要求强制配置时区,如果不设置会出问题。解决方案很简单,驱动url上面添加上对应设置就行了
#2.配置相关信息 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC username: root password: root
这里设置的UTC是全球标准时间,你也可以理解为是英国时间,中国处在东八区,需要在这个基础上加上8小时,这样才能和中国地区的时间对应的,也可以修改配置不写UTC,写Asia/Shanghai也可以解决这个问题。
#2.配置相关信息 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=Asia/Shanghai username: root password: root
如果不想每次都设置这个东西,也可以去修改mysql中的配置文件mysql.ini,在mysqld项中添加default-time-zone=+8:00也可以解决这个问题。其实方式方法很多,这里就说这么多吧。
此外在运行程序时还会给出一个提示,说数据库驱动过时的警告,根据提示修改配置即可,弃用com.mysql.jdbc.Driver,换用com.mysql.cj.jdbc.Driver。前面的例子中已经更换了驱动了,在此说明一下。
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
总结
整合操作需要勾选MyBatis技术,也就是导入MyBatis对应的starter
数据库连接相关信息转换成配置
数据库SQL映射需要添加**@Mapper**被容器识别到
MySQL 8.X驱动强制要求设置时区
修改url,添加serverTimezone设定
修改MySQL数据库配置
驱动类过时,提醒更换为com.mysql.cj.jdbc.Driver
,前缀mybatis的是springboot与mybatis整合对应的坐标
所有springboot对应的starter都是spring-boot-starter
第三方的会有前缀
JC-3-3.整合MyBatis-Plus
称mp。这是中国制造
做完了两种技术的整合了,各位小伙伴要学会总结,我们做这个整合究竟哪些是核心?总结下来就两句话
导入对应技术的starter坐标
根据对应技术的要求做配置
虽然看起来有点虚,但是确实是这个理儿,下面趁热打铁,再换一个技术,看看是不是上面这两步。
接下来在MyBatis的基础上再升级一下,整合MyBaitsPlus(简称MP),国人开发的技术,符合中国人开发习惯,谁用谁知道。来吧,一起做整合步骤①:导入对应的starter
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3</version> </dependency>
关于这个坐标,此处要说明一点,之前我们看的starter都是spring-boot-starter-???,也就是说都是下面的格式
Spring-boot-start-***
而这个坐标的名字书写比较特殊,是第三方技术名称在前,boot和starter在后。此处简单提一下命名规范,后期原理篇会再详细讲解
starter所属 | 命名规则 | 示例 |
官方提供 | spring-boot-starter-技术名称 | spring-boot-starter-web spring-boot-starter-test |
第三方提供 | 第三方技术名称-spring-boot-starter | druid-spring-boot-starter |
第三方提供 | 第三方技术名称-boot-starter(第三方技术名称过长,简化命名) | mybatis-plus-boot-starter |
温馨提示
有些小伙伴在创建项目时想通过勾选的形式找到这个名字,别翻了,没有。截止目前,SpringBoot官网还未收录此坐标,而我们Idea创建模块时读取的是SpringBoot官网的Spring Initializr,所以也没有。如果换用阿里云的url创建项目可以找到对应的坐标步骤②:配置数据源相关信息
#2.配置相关信息 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ssm_db username: root password: 123456
没了,就这么多,剩下的就是写MyBaitsPlus的程序了
不需要写@Select查询语句 等了,它里面有众多的增删改查方法,继承直接有,直接测试功能即可)但是需要注意mp技术他不知道你的数据库表名字,需要配置文件中写上 @Select("select * from tbl_book where id = #{id}") public Book getById(Integer id);
加了这个前缀它就能找到所有的带tal_前缀的表
映射接口(Dao)
//把实体类作为泛型传进去即可 @Mapper public interface BookDao extends BaseMapper<Book> { }
核心在于Dao接口继承了一个BaseMapper的接口,这个接口中帮助开发者预定了若干个常用的API接口,简化了通用API接口的开发工作。
下面就可以写一个测试类进行测试了,此处省略。
温馨提示
目前数据库的表名定义规则是tbl_模块名称,为了能和实体类相对应,需要做一个配置,相关知识各位小伙伴可以到MyBatisPlus课程中去学习,此处仅给出解决方案。配置application.yml文件,添加如下配置即可,设置所有表名的通用前缀名
mybatis-plus: global-config: db-config: table-prefix: tbl_ #设置所有表的通用前缀名称为tbl_
总结
- 手工添加MyBatis-Plus对应的starter
- 数据层接口使用BaseMapper简化开发
- 需要使用的第三方技术无法通过勾选确定时,需要手工添加坐标
JC-3-4.整合Druid
使用SpringBoot整合了3个技术了,发现套路基本相同,导入对应的starter,然后做配置,各位小伙伴需要一直强化这套思想。下面再整合一个技术,继续深入强化此思想。
前面整合MyBatis和MP的时候,使用的数据源对象都是SpringBoot默认的数据源对象,下面我们手工控制一下,自己指定了一个数据源对象,Druid。
在没有指定数据源时,我们的配置如下:
#2.配置相关信息 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=Asia/Shanghai username: root password: root
此时虽然没有指定数据源,但是根据SpringBoot的德行,肯定帮我们选了一个它认为最好的数据源对象,这就是HiKari。通过启动日志可以查看到对应的身影。
2021-11-29 09:39:15.202 INFO 12260 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2021-11-29 09:39:15.208 WARN 12260 --- [ main] com.zaxxer.hikari.util.DriverDataSource : Registered driver with driverClassName=com.mysql.jdbc.Driver was not found, trying direct instantiation. 2021-11-29 09:39:15.551 INFO 12260 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
上述信息中每一行都有HiKari的身影,如果需要更换数据源,其实只需要两步即可。
- 导入对应的技术坐标
- 配置使用指定的数据源类型
下面就切换一下数据源对象
步骤①:导入对应的坐标(注意,是坐标,此处不是starter)
<dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> </dependencies>
步骤②:修改配置,在数据源配置中有一个type属性,专用于指定数据源类型
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource
这里其实要提出一个问题的,目前的数据源配置格式是一个通用格式,不管你换什么数据源都可以用这种形式进行配置。但是新的问题又来了,如果对数据源进行个性化的配置,例如配置数据源对应的连接数量,这个时候就有新的问题了。每个数据源技术对应的配置名称都一样吗?肯定不是啊,各个厂商不可能提前商量好都写一样的名字啊,怎么办?就要使用专用的配置格式了。这个时候上面这种通用格式就不能使用了,怎么办?还能怎么办?按照SpringBoot整合其他技术的通用规则来套啊,导入对应的starter,进行相应的配置即可。步骤①:导入对应的starter
<dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.6</version> </dependency> </dependencies>
步骤②:修改配置(druid专用配置)
spring: datasource: druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC username: root password: 123456
注意观察,配置项中,在datasource下面并不是直接配置url这些属性的,而是先配置了一个druid节点,然后再配置的url这些东西。言外之意,url这个属性时druid下面的属性,那你能想到吗?除了这4个常规配置外,还有druid专用的其他配置。通过提示功能可以打开druid相关的配置查阅
与druid相关的配置超过200条以上,这就告诉你,如果想做druid相关的配置,使用这种格式就可以了,这里就不展开描述了,太多了。
这是我们做的第4个技术的整合方案,还是那两句话:导入对应starter,使用对应配置。没了,SpringBoot整合其他技术就这么简单粗暴。
总结
整合Druid需要导入Druid对应的starter
根据Druid提供的配置方式进行配置
整合第三方技术通用方式
导入对应的starter
根据提供的配置格式,配置非默认值对应的配置项
JC-3-5.SSMP整合综合案例
SpringBoot能够整合的技术太多太多了,对于初学者来说慢慢来,一点点掌握。前面咱们做了4个整合了,下面就通过一个稍微综合一点的案例,将所有知识贯穿起来,同时做一个小功能,体会一下。不过有言在先,这个案例制作的时候,你可能会有这种感觉,说好的SpringBoot整合其他技术的案例,为什么感觉SpringBoot整合其他技术的身影不多呢?因为这东西书写太简单了,简单到瞬间写完,大量的时间做的不是这些整合工作。
先看一下这个案例的最终效果
主页面
添加
删除
修改
分页
条件查询
整体案例中需要采用的技术如下,先了解一下,做到哪一个说哪一个
实体类开发————使用Lombok快速制作实体类
ctrl+f12可以查看本类中的字段和方法
lombok插件里面的@Data注解包含setter、getter、toString、hashCode、equals方法等,但是没有帮我们生成构造方法
常用@Data
@AllConstructor富含全部参数的构造方法
@NoArgsConstructor无参构造方法
Dao开发————整合MyBatisPlus,制作数据层测试
Service开发————基于MyBatisPlus进行增量开发,制作业务层测试类
Controller开发————基于Restful开发,使用PostMan测试接口功能
Controller开发————前后端开发协议制作
页面开发————基于VUE+ElementUI制作,前后端联调,页面数据处理,页面消息处理
列表
新增
修改
删除
分页
查询
项目异常处理
按条件查询————页面功能调整、Controller修正功能、Service修正功能
可以看的出来,东西还是很多的,希望通过这个案例,各位小伙伴能够完成基础开发的技能训练。整体开发过程采用做一层测一层的形式进行,过程完整,战线较长,希望各位能跟进进度,完成这个小案例的制作。
10.模块创建
对于这个案例如果按照企业开发的形式进行应该制作后台微服务,前后端分离的开发。
我知道这个对初学的小伙伴要求太高了,咱们简化一下。后台做单体服务器,前端不使用前后端分离的制作了。
一个服务器即充当后台服务调用,又负责前端页面展示,降低学习的门槛。
下面我们就可以创建一个新的模块,加载要使用的技术对应的starter,修改配置文件格式为yml格式,并把web访问端口先设置成80。
pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
application.yml
server: port: 80
1.实体类开发
本案例对应的模块表结构如下:
-- ---------------------------- -- Table structure for tbl_book -- ---------------------------- DROP TABLE IF EXISTS `tbl_book`; CREATE TABLE `tbl_book` ( `id` int(11) NOT NULL AUTO_INCREMENT, `type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 51 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of tbl_book -- ---------------------------- INSERT INTO `tbl_book` VALUES (1, '计算机理论', 'Spring实战 第5版', 'Spring入门经典教程,深入理解Spring原理技术内幕'); INSERT INTO `tbl_book` VALUES (2, '计算机理论', 'Spring 5核心原理与30个类手写实战', '十年沉淀之作,手写Spring精华思想'); INSERT INTO `tbl_book` VALUES (3, '计算机理论', 'Spring 5 设计模式', '深入Spring源码剖析Spring源码中蕴含的10大设计模式'); INSERT INTO `tbl_book` VALUES (4, '计算机理论', 'Spring MVC+MyBatis开发从入门到项目实战', '全方位解析面向Web应用的轻量级框架,带你成为Spring MVC开发高手'); INSERT INTO `tbl_book` VALUES (5, '计算机理论', '轻量级Java Web企业应用实战', '源码级剖析Spring框架,适合已掌握Java基础的读者'); INSERT INTO `tbl_book` VALUES (6, '计算机理论', 'Java核心技术 卷I 基础知识(原书第11版)', 'Core Java 第11版,Jolt大奖获奖作品,针对Java SE9、10、11全面更新'); INSERT INTO `tbl_book` VALUES (7, '计算机理论', '深入理解Java虚拟机', '5个维度全面剖析JVM,大厂面试知识点全覆盖'); INSERT INTO `tbl_book` VALUES (8, '计算机理论', 'Java编程思想(第4版)', 'Java学习必读经典,殿堂级著作!赢得了全球程序员的广泛赞誉'); INSERT INTO `tbl_book` VALUES (9, '计算机理论', '零基础学Java(全彩版)', '零基础自学编程的入门图书,由浅入深,详解Java语言的编程思想和核心技术'); INSERT INTO `tbl_book` VALUES (10, '市场营销', '直播就该这么做:主播高效沟通实战指南', '李子柒、李佳琦、薇娅成长为网红的秘密都在书中'); INSERT INTO `tbl_book` VALUES (11, '市场营销', '直播销讲实战一本通', '和秋叶一起学系列网络营销书籍'); INSERT INTO `tbl_book` VALUES (12, '市场营销', '直播带货:淘宝、天猫直播从新手到高手', '一本教你如何玩转直播的书,10堂课轻松实现带货月入3W+');
有些接口喜欢命名为BookMapper,这里用BookDao.java
根据上述表结构,制作对应的实体类
实体类
public class Book { private Integer id; private String type; private String name; private String description; }
实体类的开发可以自动通过工具手工生成get/set方法,然后覆盖toString()方法,方便调试,等等。不过这一套操作书写很繁琐,有对应的工具可以帮助我们简化开发,介绍一个小工具,lombok。
Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发,SpringBoot目前默认集成了lombok技术,并提供了对应的版本控制,所以只需要提供对应的坐标即可(parent中收入了parent的坐标),在pom.xml中添加lombok的坐标。
<dependencies> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
使用lombok可以通过一个注解@Data完成一个实体类对应的getter,setter,toString,equals,hashCode等操作的快速添加
import lombok.Data; @Data public class Book { private Integer id; private String type; private String name; private String description; }
到这里实体类就做好了,是不是比不使用lombok简化好多,这种工具在Java开发中还有N多,后面课程中遇到了能用的东西时,在不增加各位小伙伴大量的学习时间的情况下,尽量多给大家介绍一些
总结
实体类制作
使用lombok简化开发
lombok是一个java类库,提供了一组注解,简化POJO实体类开发
导入lombok无需指定版本,由SpringBoot提供版本
@Data注解
2.数据层开发——基础CRUD
数据层开发本次使用MyBatisPlus技术,数据源使用前面学习的Druid,学都学了都用上
需要自己加上MybatisPlus和druid-spring的依赖,parent中没有维护这个依赖(没有维护的都要自己加上)
<artifactId>mybatis-plus-boot-starter</artifactId> <artifactId>druid-spring-boot-starter</artifactId>
步骤①:导入MyBatisPlus与Druid对应的starter,当然mysql的驱动不能少
MySQL 从 8.0.31 开始从原来的 mysql:mysql-connector-java 改为 com.mysql:mysql-connector-j
<dependencies> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> </dependencies>
步骤②:配置数据库连接相关的数据源配置
server: port: 80 spring: datasource: druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC username: root password: root
步骤③:使用MP的标准通用接口BaseMapper加速开发,别忘了@Mapper和泛型的指定
@Mapper的作用是可以给mapper接口自动生成一个实现类,让spring对mapper接口的bean进行管理,并且可以省略去写复杂的xml文件
@Mapper注解:
作用:在接口类上添加了@Mapper,在编译之后会生成相应的接口实现类
@Mapper是等同于不用配置相关接口的XML文件,直接在Mybatis自动化注册全部了。剩下只要写sql行了
Mybati-plus用法:
mp中提供了一个接口BaseMapper<操作的模块对应的实体类 >
在mp中所有的查询都是select开头的
@Mapper public interface BookDao extends BaseMapper<Book> { } //mybatis-非plus @Mapper public interface BookDao { @Select("select *from tbl_book where id=#{id}") Book getById(Integer id); }
回顾MyBatis:
springboot写了@Mapper就不用写映射文件了
<!--这是EmpMapper.java接口-Emp getEmpByEmpId(@Param("empId") Integer empId);下面是EmpMapper.xml--> <select id="getEmpByEmpId" resultMap="empResultMap"> select * from t_emp where emp_id = #{empId} </select>
步骤④:制作测试类测试结果,这个测试类制作是个好习惯,不过在企业开发中往往都为加速开发跳过此步,且行且珍惜吧
package com.itheima.dao; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.itheima.domain.Book; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class BookDaoTestCase { @Autowired private BookDao bookDao; @Test void testGetById(){ System.out.println(bookDao.selectById(1)); } @Test void testSave(){ Book book = new Book(); book.setType("测试数据123"); book.setName("测试数据123"); book.setDescription("测试数据123"); bookDao.insert(book); } @Test void testUpdate(){ Book book = new Book(); book.setId(17); book.setType("测试数据abcdefg"); book.setName("测试数据123"); book.setDescription("测试数据123"); bookDao.updateById(book); } @Test void testDelete(){ bookDao.deleteById(16); } @Test void testGetAll(){ bookDao.selectList(null); } }
温馨提示
MP技术默认的主键生成策略为雪花算法,生成的主键ID长度较大,和目前的数据库设定规则不相符,需要配置一下使MP使用数据库的主键生成策略,方式嘛还是老一套,做配置。在application.yml中添加对应配置即可,具体如下
server: port: 80 spring: datasource: druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC username: root password: root mybatis-plus: global-config: db-config: table-prefix: tbl_ #设置表名通用前缀 id-type: auto #设置主键id字段的生成策略为参照数据库设定的策略,当前数据库设置id生成策略为自增
为什么设置为auto
在 Mybatis-Plus 中,id-type 属性用于指定自动生成主键的类型。id-type 属性有以下几个可选值:
AUTO: 自动判断使用何种策略生成主键。
NONE: 不设置主键类型。
INPUT: 手动输入主键值。
ID_WORKER: 使用雪花算法生成主键。
UUID: 使用 UUID 生成主键。
当对一个实体类使用 Mybatis-Plus 进行操作时,如果该实体类没有定义主键字段或者主键字段的值为 null,那么就会根据 id-type 指定的策略来自动生成主键。例如,在 id-type 设置为 ID_WORKER 时,Mybatis-Plus 会使用雪花算法生成唯一的主键值,从而保证主键的唯一性。
在这里,id-type 属性设置为 auto,表示自动判断主键生成策略。Mybatis-Plus 会根据实体类中的字段及其注解来判断主键生成策略。例如,如果实体类中有一个名为 id 的字段,且该字段上标记了 @TableId 注解,并且该注解的 type 属性为 AUTO,那么 Mybatis-Plus 就会使用数据库自增长主键的方式生成主键值。如果实体类中没有名为 id 的字段,或者该字段没有标记 @TableId 注解,那么 Mybatis-Plus 就会采用雪花算法或 UUID 生成主键值。
因此,将 id-type 设置为 auto,可以让 Mybatis-Plus 自动判断主键生成策略,并根据实体类中的信息生成正确的主键值。这样可以简化代码编写,并提高开发效率。
查看MP运行日志
在进行数据层测试的时候,因为基础的CRUD操作均由MP给我们提供了,所以就出现了一个局面,开发者不需要书写SQL语句了,这样程序运行的时候总有一种感觉,一切的一切都是黑盒的,作为开发者我们啥也不知道就完了。如果程序正常运行还好,如果报错了,这个时候就很崩溃,你甚至都不知道从何下手,因为传递参数、封装SQL语句这些操作完全不是你干预开发出来的,所以查看执行期运行的SQL语句就成为当务之急。
SpringBoot整合MP的时候充分考虑到了这点,通过配置的形式就可以查阅执行期SQL语句,配置如下
mybatis-plus: global-config: db-config: table-prefix: tbl_ id-type: auto #StdOutImpl就是标准输出-打印到控制台上,有了这个就不用sout输入到控制台了,直接运行即可显示到控制台 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
再来看运行结果,此时就显示了运行期执行SQL的情况。
Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2c9a6717] was not registered for synchronization because synchronization is not active JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6ca30b8a] will not be managed by Spring ==> Preparing: SELECT id,type,name,description FROM tbl_book ==> Parameters: <== Columns: id, type, name, description <== Row: 1, 计算机理论, Spring实战 第5版, Spring入门经典教程,深入理解Spring原理技术内幕 <== Row: 2, 计算机理论, Spring 5核心原理与30个类手写实战, 十年沉淀之作,手写Spring精华思想 <== Row: 3, 计算机理论, Spring 5 设计模式, 深入Spring源码剖析Spring源码中蕴含的10大设计模式 <== Row: 4, 计算机理论, Spring MVC+MyBatis开发从入门到项目实战, 全方位解析面向Web应用的轻量级框架,带你成为Spring MVC开发高手 <== Row: 5, 计算机理论, 轻量级Java Web企业应用实战, 源码级剖析Spring框架,适合已掌握Java基础的读者 <== Row: 6, 计算机理论, Java核心技术 卷I 基础知识(原书第11版), Core Java 第11版,Jolt大奖获奖作品,针对Java SE9、10、11全面更新 <== Row: 7, 计算机理论, 深入理解Java虚拟机, 5个维度全面剖析JVM,大厂面试知识点全覆盖 <== Row: 8, 计算机理论, Java编程思想(第4版), Java学习必读经典,殿堂级著作!赢得了全球程序员的广泛赞誉 <== Row: 9, 计算机理论, 零基础学Java(全彩版), 零基础自学编程的入门图书,由浅入深,详解Java语言的编程思想和核心技术 <== Row: 10, 市场营销, 直播就该这么做:主播高效沟通实战指南, 李子柒、李佳琦、薇娅成长为网红的秘密都在书中 <== Row: 11, 市场营销, 直播销讲实战一本通, 和秋叶一起学系列网络营销书籍 <== Row: 12, 市场营销, 直播带货:淘宝、天猫直播从新手到高手, 一本教你如何玩转直播的书,10堂课轻松实现带货月入3W+ <== Row: 13, 测试类型, 测试数据, 测试描述数据 <== Row: 14, 测试数据update, 测试数据update, 测试数据update <== Row: 15, -----------------, 测试数据123, 测试数据123 <== Total: 15
其中清晰的标注了当前执行的SQL语句是什么,携带了什么参数,对应的执行结果是什么,所有信息应有尽有。
此处设置的是日志的显示形式,当前配置的是控制台输出,当然还可以由更多的选择,根据需求切换即可
总结
- 手工导入starter坐标(2个),mysql驱动(1个)
- 配置数据源与MyBatisPlus对应的配置
- 开发Dao接口(继承BaseMapper)
- 制作测试类测试Dao功能是否有效
- 使用配置方式开启日志,设置日志输出方式为标准输出即可查阅SQL执行日志
3.数据层开发——分页功能制作
前面仅仅是使用了MP提供的基础CRUD功能,实际上MP给我们提供了几乎所有的基础操作,这一节说一下如果实现数据库端的分页操作
MP提供的分页操作API如下
@Test void testGetPage(){ //page(current,size)第一个数显示第几页的数据,第二个是显示多少条 IPage page = new Page(2,5); bookDao.selectPage(page, null); System.out.println(page.getCurrent()); System.out.println(page.getSize()); System.out.println(page.getTotal()); System.out.println(page.getPages()); System.out.println(page.getRecords()); }
其中selectPage方法需要传入一个封装分页数据的对象,可以通过new的形式创建这个对象,当然这个对象也是MP提供的,别选错包了。创建此对象时就需要指定分页的两个基本数据
- 当前显示第几页
- 每页显示几条数据
可以通过创建Page对象时利用构造方法初始化这两个数据
IPage page = new Page(2,5);
将该对象传入到查询方法selectPage后,可以得到查询结果,但是我们会发现当前操作查询结果返回值仍然是一个IPage对象,这又是怎么回事?
IPage page = bookDao.selectPage(page, null);
原来这个IPage对象中封装了若干个数据,而查询的结果作为IPage对象封装的一个数据存在的,可以理解为查询结果得到后,又塞到了这个IPage对象中,其实还是为了高度的封装,一个IPage描述了分页所有的信息。下面5个操作就是IPage对象中封装的所有信息了
@Test void testGetPage(){ IPage page = new Page(2,5); bookDao.selectPage(page, null); System.out.println(page.getCurrent()); //当前页码值 System.out.println(page.getSize()); //每页显示数 System.out.println(page.getTotal()); //数据总量 System.out.println(page.getPages()); //总页数 System.out.println(page.getRecords()); //详细数据 }
到这里就知道这些数据如何获取了,但是当你去执行这个操作时,你会发现并不像我们分析的这样,实际上这个分页当前是无效的。为什么这样呢?这个要源于MP的内部机制。
对于MySQL的分页操作使用limit关键字进行,而并不是所有的数据库都使用limit关键字实现的,这个时候MP为了制作的兼容性强,将分页操作设置为基础查询操作的升级版,你可以理解为IPhone6与IPhone6S-PLUS的关系。
基础操作中会有查询全部的功能,而在这个基础上只需要升级一下(PLUS)就可以得到分页操作。所以MP将分页操作做成了一个开关,你用分页功能就把开关开启,不用就不需要开启这个开关。而我们现在没有开启这个开关,所以分页操作是没有的。这个开关是通过MP的拦截器的形式存在的(可以选择加不加limit等功能),其中的原理这里不分析了,有兴趣的小伙伴可以学习MyBatisPlus这门课程进行详细解读。具体设置方式如
下定义MP拦截器并将其设置为Spring管控的bean
做的所有东西都需要受Spring管理,Spring是用来管理bean的
//@Configuration能保证这里面的配置信息能够被读取到,添加后能够保证@SpringbootApplication这个主启动引导类所在的包以及子包下的所有配置以及bean的定义都会被加载,它会被扫描到的 @Configuration public class MPConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ //单单创建可不行-只是一个壳,需要添加内部拦截器addInnerInterceptor MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; } }
上述代码第一行是创建MP的拦截器栈,这个时候拦截器栈中没有具体的拦截器,第二行是初始化了分页拦截器,并添加到拦截器栈中。如果后期开发其他功能,需要添加全新的拦截器,按照第二行的格式继续add进去新的拦截器就可以了。
总结
使用IPage封装分页数据,封装了分页操作中的所有数据
数据、当前页码值、最大页码值、数据总量
分页操作依赖MyBatisPlus分页拦截器实现功能
借助MyBatisPlus日志查阅执行SQL语句
分页操作是在MyBatisPlus的常规操作基础上增强得到,内部是动态的拼写SQL语句,因此需要增强对应的功能,使用MyBatisPlus拦截器(MybatisPlusInterceptor)实现
4.数据层开发——条件查询功能制作
除了分页功能,MP还提供有强大的条件查询功能。以往我们写条件查询要自己动态拼写复杂的SQL语句,现在简单了,MP将这些操作都制作成API接口,调用一个又一个的方法就可以实现各种套件的拼装。这里给大家普及一下基本格式,详细的操作还是到MP的课程中查阅吧
下面的操作就是执行一个模糊匹配对应的操作,由like条件书写变为了like方法的调用
@Test void testGetBy(){ QueryWrapper<Book> qw = new QueryWrapper<>(); //属性名colum,属性值val;查询的时候按照name去匹配Spring qw.like("name","Spring"); bookDao.selectList(qw); }
Preparing: SELECT id,type,name,description FROM tbl_book WHERE (name LIKE ?) Parameters: %Spring%(String)
其中第一句QueryWrapper对象是一个用于封装查询条件的对象,该对象可以动态使用API调用的方法添加条件,最终转化成对应的SQL语句。第二句就是一个条件了,需要什么条件,使用QueryWapper对象直接调用对应操作即可。比如做大于小于关系,就可以使用lt或gt方法,等于使用eq方法,等等,此处不做更多的解释了。
这组API使用还是比较简单的,但是关于属性字段名的书写存在着安全隐患,比如查询字段name,当前是以字符串的形态书写的,万一写错,编译器还没有办法发现,只能将问题抛到运行器通过异常堆栈告诉开发者,不太友好。
MP针对字段检查进行了功能升级,全面支持Lambda表达式,就有了下面这组API。由QueryWrapper对象升级为LambdaQueryWrapper对象,这下就变了上述问题的出现
@Test void testGetBy2(){ String name = "1"; LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>(); lqw.like(Book::getName,name); bookDao.selectList(lqw); }
为了便于开发者动态拼写SQL,防止将null数据作为条件使用(String name=“null” --> name like %null%),MP还提供了动态拼装SQL的快捷书写方式
@Test void testGetBy2(){ String name = "1"; LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>(); //if(name != null) lqw.like(Book::getName,name); //方式一:JAVA代码控制 lqw.like(name != null,Book::getName,name); //方式二:API接口提供控制开关,name != null这个位置是true or false 来判断后面是否执行 bookDao.selectList(lqw); }
其实就是个格式,没有区别。关于MP的基础操作就说到这里吧,如果这一块知识不太熟悉的小伙伴还是去完整的学习一下MP的知识吧,这里只是蜻蜓点水的用了几个操作而已。
总结
使用QueryWrapper对象封装查询条件
推荐使用LambdaQueryWrapper对象
所有查询操作封装成方法调用
查询条件支持动态条件拼装
5.业务层开发
业务层接口关注的是业务名称,数据层接口关注的是数据库相关的操作;
比如login在业务层的名叫做login,在数据层的名一般会根据接口名称定义成让所有开发人员一看就明白的名字
数据层开发告一段落,下面进行业务层开发,其实标准业务层开发很多初学者认为就是调用数据层,怎么说呢?这个理解是没有大问题的,更精准的说法应该是组织业务逻辑功能,并根据业务需求,对数据持久层发起调用。有什么差别呢?目标是为了组织出符合需求的业务逻辑功能,至于调不调用数据层还真不好说,有需求就调用,没有需求就不调用。
一个常识性的知识普及一下,业务层的方法名定义一定要与业务有关,例如登录操作
login(String username,String password);
而数据层的方法名定义一定与业务无关,是一定,不是可能,也不是有可能,例如根据用户名密码查询
selectByUserNameAndPassword(String username,String password);
我们在开发的时候是可以根据完成的工作不同划分成不同职能的开发团队的。比如一个哥们制作数据层,他就可以不知道业务是什么样子,拿到的需求文档要求可能是这样的
接口:传入用户名与密码字段,查询出对应结果,结果是单条数据 接口:传入ID字段,查询出对应结果,结果是单条数据 接口:传入离职字段,查询出对应结果,结果是多条数据
但是进行业务功能开发的哥们,拿到的需求文档要求差别就很大
接口:传入用户名与密码字段,对用户名字段做长度校验,4-15位,对密码字段做长度校验,8到24位,对
你比较一下,能是一回事吗?差别太大了,所以说业务层方法定义与数据层方法定义差异化很大,只不过有些入门级的开发者手懒或者没有使用过公司相关的ISO标准化文档而已。
多余的话不说了,咱们做案例就简单制作了,业务层接口定义如下:
public interface BookService { Boolean save(Book book); Boolean update(Book book); Boolean delete(Integer id); Book getById(Integer id); List<Book> getAll(); IPage<Book> getPage(int currentPage,int pageSize); }
业务层实现类如下,转调数据层即可
@Service public class BookServiceImpl implements BookService { @Autowired private BookDao bookDao; @Override public Boolean save(Book book) { //注意到数据层接口名称是insert,业务层是save,注意这样常见! //因为底层返回的是Integer,所以需要写上 > 0 return bookDao.insert(book) > 0; } @Override public Boolean update(Book book) { return bookDao.updateById(book) > 0; } @Override public Boolean delete(Integer id) { return bookDao.deleteById(id) > 0; } @Override public Book getById(Integer id) { return bookDao.selectById(id); } @Override public List<Book> getAll() { return bookDao.selectList(null); } @Override public IPage<Book> getPage(int currentPage, int pageSize) { IPage page = new Page(currentPage,pageSize); bookDao.selectPage(page,null); return page; } }
别忘了对业务层接口进行测试,测试类如下
@SpringBootTest public class BookServiceTest { @Autowired private IBookService bookService; @Test void testGetById(){ //这地方还是需要sout的,有查询结果 //不写sout就没有这些,Book(id=2, type=计算机理论, name=Spring 5核心原理与30个类手写实战, description=十年沉淀之作,手写Spring精华思想) System.out.println(bookService.getById(4)); } @Test void testSave(){ Book book = new Book(); book.setType("测试数据123"); book.setName("测试数据123"); book.setDescription("测试数据123"); bookService.save(book); } @Test void testUpdate(){ Book book = new Book(); book.setId(17); book.setType("-----------------"); book.setName("测试数据123"); book.setDescription("测试数据123"); bookService.updateById(book); } @Test void testDelete(){ bookService.removeById(18); } @Test void testGetAll(){ bookService.list(); } @Test void testGetPage(){ IPage<Book> page = new Page<Book>(2,5); bookService.page(page); System.out.println(page.getCurrent()); System.out.println(page.getSize()); System.out.println(page.getTotal()); System.out.println(page.getPages()); System.out.println(page.getRecords()); } }
总结
Service接口名称定义成业务名称,并与Dao接口名称进行区分
制作测试类测试Service功能是否有效
业务层快速开发
其实MP技术不仅提供了数据层快速开发方案,业务层MP也给了一个通用接口,个人观点不推荐使用,凑合能用吧,其实就是一个封装+继承的思想,代码给出,实际开发慎用
业务层接口快速开发
public interface IBookService extends IService<Book> { //添加非通用操作API接口 }
业务层接口实现类快速开发,关注继承的类需要传入两个泛型,一个是数据层接口,另一个是实体类
@Service public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements IBookService { @Autowired private BookDao bookDao; //添加非通用操作API }
如果感觉MP提供的功能不足以支撑你的使用需要,其实是一定不能支撑的,因为需求不可能是通用的,在原始接口基础上接着定义新的API接口就行了,此处不再说太多了,就是自定义自己的操作了,但是不要和已有的API接口名冲突即可。
总结
使用通用接口(ISerivce)快速开发Service
使用通用实现类(ServiceImpl)快速开发ServiceImpl
可以在通用接口基础上做功能重载或功能追加
注意重载时不要覆盖原始操作,避免原始提供的功能丢失(用的时候随时调用,ctrl+f12可以查询当前类方法)
6.表现层开发
终于做到表现层了,做了这么多都是基础工作。其实你现在回头看看,哪里还有什么SpringBoot的影子?前面1,2步就搞完了。继续完成表现层制作吧,咱们表现层的开发使用基于Restful的表现层接口开发,功能测试通过Postman工具进行
表现层接口如下:
@RestController @RequestMapping("/books") public class BookController2 { @Autowired private IBookService bookService; @GetMapping public List<Book> getAll(){ return bookService.list(); } @PostMapping //异步请求通过请求体发json数据过来 public Boolean save(@RequestBody Book book){ return bookService.save(book); } @PutMapping public Boolean update(@RequestBody Book book){ return bookService.modify(book); } @DeleteMapping("{id}") public Boolean delete(@PathVariable Integer id){ return bookService.delete(id); } @GetMapping("{id}") public Book getById(@PathVariable Integer id){ return bookService.getById(id); } @GetMapping("{currentPage}/{pageSize}") public IPage<Book> getPage(@PathVariable int currentPage,@PathVariable int pageSize){ return bookService.getPage(currentPage,pageSize, null); } }
在实用Postman测试时关注提交类型,对应上即可,不然就会报405的错误码了
普通GET请求
PUT请求传递json数据,后台实用@RequestBody接收数据
GET请求传递路径变量,后台实用@PathVariable接收数据
总结
基于Restful制作表现层接口
新增:POST
删除:DELETE
修改:PUT
查询:GET
接收参数
实体数据:@RequestBody
路径变量:@PathVariable
RequestBody作用
https://blog.csdn.net/u011066470/article/details/112502544
RequestParam处理的是请求参数( ?之后 ),而PathVariable处理的是路径变量( /之后 ),
全套SpringBoot讲义01-三https://developer.aliyun.com/article/1491512