SpringBoot学习
Spring Boot的基础结构共三个文件(具体路径根据用户生成项目时填写的Group所有差异):
-
src/main/java
下的程序入口:Chapter11Application
-
src/main/resources
下的配置文件:application.properties
-
src/test/
下的测试入口:Chapter11ApplicationTests
Pom.xml中的模块:
- 项目元数据:创建时候输入的Project Metadata部分,也就是Maven项目的基本元素,包括:groupId、artifactId、version、name、description等
- parent:继承
spring-boot-starter-parent
的依赖管理,控制版本与打包等内容 - dependencies:项目具体依赖,这里包含了
spring-boot-starter-web
用于实现HTTP接口(该依赖中包含了Spring MVC);spring-boot-starter-test
用于编写单元测试的依赖包。更多功能模块的使用我们将在后面的教程中逐步展开。 - build:构建配置部分。默认使用了
spring-boot-maven-plugin
,配合spring-boot-starter-parent
就可以把Spring Boot应用打包成JAR来直接运行。
基础注解
@Controller
:修饰class,用来创建处理http请求的对象@RestController
:Spring4之后加入的注解,原来在@Controller
中返回json需要@ResponseBody
来配合,如果直接用@RestController
替代@Controller
就不需要再配置@ResponseBody
,默认返回json格式@RequestMapping
:配置url映射。现在更多的也会直接用以Http Method直接关联的映射注解来定义,比如:GetMapping
、PostMapping
、DeleteMapping
、PutMapping
等@Data
注解可以实现在编译器自动添加set和get函数的效果。该注解是lombok提供的@NoArgsConstrucor
注解在类上,为类提供一个无参的构造方法。
工程结构
-
root package
:com.example.myproject
,所有的类和其他package都在root package之下。 - 应用主类:
Application.java
,该类直接位于root package
下。通常我们会在应用主类中做一些框架配置扫描等配置,我们放在root package下可以帮助程序减少手工配置来加载到我们希望被Spring加载的内容 -
com.example.myproject.domain
包:用于定义实体映射关系与数据访问相关的接口和实现 -
com.example.myproject.service
包:用于编写业务逻辑相关的接口与实现 -
com.example.myproject.web
:用于编写Web层相关的实现,比如:Spring MVC的Controller等
在定义请求的时候使用contentType(MediaType.APPLICATION_JSON)
指定提交内容为json格式,使用content
传入要提交的json字符串。
request = post("/users/")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"id\":1,\"name\":\"测试大师\",\"age\":20}");
MockMvc
MockMvc是由spring-test包提供,实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,使得测试速度快、不依赖网络环境。同时提供了一套验证的工具,结果的验证十分方便。
接口MockMvcBuilder,提供一个唯一的build方法,用来构造MockMvc。主要有两个实现:StandaloneMockMvcBuilder和DefaultMockMvcBuilder,分别对应两种测试方式,即独立安装和集成Web环境测试(并不会集成真正的web环境,而是通过相应的Mock API进行模拟测试,无须启动服务器)。MockMvcBuilders提供了对应的创建方法standaloneSetup方法和webAppContextSetup方法,在使用时直接调用即可。
SpringBoot集成Mybatis连接Oracle数据库
- 导入依赖
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.1.0</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
- oracle连接配置文件
server.port=8081
server.address=127.0.0.1
server.contextPath=/
spring.datasource.url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
spring.datasource.username=test1
spring.datasource.password=123
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
- 创建Student表操作接口:StudentMapper
@Mapper
public interface StudentMapper {
@Select("select * from student")
List<Student> AllStudents();
- 在单元测试中进行测试
返回结果绑定
查询的结果往往就不再是简单的实体对象了,往往需要返回一个与数据库实体不同的包装类,那么对于这类情况,就可以通过@Results
和@Result
注解来进行绑定
@Results({
@Result(property = "name", column = "name"),
@Result(property = "age", column = "age")})
@Select("SELECT name, age FROM user")
List<User> findAll();
Rest接口
在设计web接口的时候,REST主要是用于定义接口名,接口名一般是用名次写,不用动词。即通过Http请求的接口类型来判断是什么业务操作。
如:
增加一个朋友,uri: generalcode.cn/v1/friends 接口类型:POST
删除一个朋友,uri: generalcode.cn/va/friends 接口类型:DELETE
修改一个朋友,uri: generalcode.cn/va/friends 接口类型:PUT
查找朋友,uri: generalcode.cn/va/friends 接口类型:GET
junit.framework包下的Assert提供了多个断言方法. 主用于比较测试传递进去的两个参数.
**Assert.assertEquals();**及其重载方法: 1. 如果两者一致, 程序继续往下运行. 2. 如果两者不一致, 中断测试方法, 抛出异常信息 AssertionFailedError .
关系型数据库
什么是JDBC?
Java数据库连接(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,通常说的JDBC是面向关系型数据库的。
JDBC API主要位于JDK中的java.sql
包中(之后扩展的内容位于javax.sql
包中),主要包括(斜体代表接口,需驱动程序提供者来具体实现):
- DriverManager:负责加载各种不同驱动程序(Driver),并根据不同的请求,向调用者返回相应的数据库连接(Connection)。
- Driver:驱动程序,会将自身加载到DriverManager中去,并处理相应的请求并返回相应的数据库连接(Connection)。
- Connection:数据库连接,负责与进行数据库间通讯,SQL执行以及事务处理都是在某个特定Connection环境中进行的。可以产生用以执行SQL的Statement。
- Statement:用以执行SQL查询和更新(针对静态SQL语句和单次执行)。PreparedStatement:用以执行包含动态参数的SQL查询和更新(在服务器端编译,允许重复执行以提高效率)。
- CallableStatement:用以调用数据库中的存储过程。
- SQLException:代表在数据库连接的建立和关闭和SQL语句的执行过程中发生了例外情况(即错误)。
什么是数据源?
数据源是指数据库应用程序所使用的数据库或者数据库服务器。在java.sql
中并没有数据源(Data Source)的概念。这是由于在java.sql
中包含的是JDBC内核API,另外还有个javax.sql
包,其中包含了JDBC标准的扩展API。而关于数据源(Data Source)的定义,就在javax.sql
这个扩展包中。
为什么需要数据源:
- 封装关于数据库访问的各种参数,实现统一管理
- 通过对数据库的连接池管理,节省开销并提高效率
嵌入式数据库
嵌入式数据库通常用于开发和测试环境,不推荐用于生产环境。Spring Boot提供自动配置的嵌入式数据库有H2、HSQL、Derby,不需要提供任何连接配置就能使用。
连接JNDI数据源
当你将应用部署于应用服务器上的时候想让数据源由应用服务器管理,那么可以使用如下配置方式引入JNDI数据源。
spring.datasource.jndi-name=java:jboss/datasources/customers
在Spring Boot自动化配置中,对于数据源的配置可以分为两类:
- 通用配置:以
spring.datasource.*
的形式存在,主要是对一些即使使用不同数据源也都需要配置的一些常规内容。比如:数据库链接地址、用户名、密码等。 - 数据源连接池配置:以
spring.datasource.<数据源名称>.*
的形式存在,比如:Hikari的配置参数就是spring.datasource.hikari.*
形式。
详见:http://blog.didispace.com/spring-boot-learning-21-3-2/
JPA
JPA是一套ORM(对象关系映射)规范,Hibernate实现了JPA规范,Spring Data JPA 可以理解为 JPA 规范的再次封装抽象,底层还是使用了 Hibernate 的 JPA 技术实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QlLvEy3d-1610421736385)(C:/Users/11517/AppData/Roaming/Typora/typora-user-images/image-20201231085533680.png)]
spring.jpa.properties.hibernate.hbm2ddl.auto
是hibernate的配置属性,其主要作用是:自动创建、更新、验证数据库表结构。该参数的几种配置如下:
-
create
:每次加载hibernate时都会删除上一次的生成的表,然后根据model类再重新来生成新表,哪怕两次没有改变也要这样执行,是导致数据库表数据丢失的一个重要原因。 -
create-drop
:每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。 -
update
:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。 -
validate
:每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。
多数据源配置:
在Spring Boot的配置文件application.properties
中设置两个你要链接的数据库配置,比如这样:
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/test1
spring.datasource.primary.username=root
spring.datasource.primary.password=123456
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/test2
spring.datasource.secondary.username=root
spring.datasource.secondary.password=123456
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
- 多数据源配置的时候,与单数据源不同点在于
spring.datasource
之后多设置一个数据源名称primary
和secondary
来区分不同的数据源配置,这个前缀将在后续初始化数据源的时候用到。 - 数据源连接配置2.x和1.x的配置项是有区别的:2.x使用
spring.datasource.secondary.jdbc-url
,而1.x版本使用spring.datasource.secondary.url
。如果配置的时候发生了这个报错java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.
,那么就是这个配置项的问题。
Thymeleaf
Thymeleaf提供了一个用于整合Spring MVC的可选模块,主要目标在于提供一种可被浏览器正确显示的、格式良好的模板创建方式,因此也可以用作静态建模。你可以使用它创建经过验证的XML与HTML模板。相对于编写逻辑或代码,开发者只需将标签属性添加到模板中即可。接下来,这些标签属性就会在DOM(文档对象模型)上执行预先制定好的逻辑。
Thymeleaf的特点
- 动静结合:Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
- 开箱即用:它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。
- 多方言支持:Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
- 与SpringBoot完美整合,SpringBoot提供了Thymeleaf的默认配置,并且为Thymeleaf设置了视图解析器,我们可以像以前操作jsp一样来操作Thymeleaf。代码几乎没有任何区别,就是在模板语法上有区别。
springmvc中,有支持的默认类型的绑定。也就是说,直接在controller方法形参上定义默认类型的对象,就可以使用这些对象。
- HttpServletRequest对象
- HttpServletResponse对象
- HttpSession对象
- Model/ModelMap对象:Model/ModelMap对象,Model是一个接口,ModelMap是一个接口实现 ,作用是将Model数据填充到request域,跟ModelAndView类似
简单类型的绑定中,方法形参中的参数名要和前台传进来的名一样才能完成参数的绑定。
使用注解@RequestParam
不用限制request传入参数名称和controller方法的形参名称一致。通过@RequestParam中的required属性指定参数是否必须要传入,如果设置为true,没有传入参数就会报错。
使用RestController
(@Controller+@ResponseBody)后,返回的是JSON字符串,无法通过return的方式跳转到页面,而使用@Controller
可以 ,若Controller类中的函数想要返回JSON类型数据,需要在函数上面添加一个注解@ResponseBody。
使用Mybatis逆向工程
使用逆向工程可以省去对mapper层,sql语句,实体的编写,通过插件能够直接生成。
这里使用的是oracle数据库,这个版本的依赖包需要自己下载。
- 导入插件
<!--Mybatis逆向工程的插件-->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configuration>
<!--指定本地的逆向工程设置文件-->
<configurationFile>src/main/resources/generator.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
<!--在插件配置数据库驱动包以防止插件运行找不到驱动类-->
<dependencies>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.1.0</version>
</dependency>
</dependencies>
</plugin>
- 导入配置文件
generator.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="testTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="false"/>
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<!--?serverTimezone=UTC设置时区-->
<jdbcConnection
driverClass="oracle.jdbc.driver.OracleDriver"
connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:orcl"
userId="test1"
password="123">
<!--设置可以获取tables remarks信息-->
<property name="useInformationSchema" value="true"/>
<!--设置可以获取remarks信息-->
<property name="remarks" value="true"/>
</jdbcConnection>
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- targetProject:生成POJO/Bean类的位置 -->
<javaModelGenerator targetPackage="com.yhfin.MissionDemo.model"
targetProject=".\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.yhfin.MissionDemo.dao"
targetProject=".\src\main\resources">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.yhfin.MissionDemo.dao"
targetProject=".\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
</javaClientGenerator>
<!--指定数据库表的生成策略-->
<table tableName="parm_fund_info" domainObjectName="FundInfo"
enableCountByExample="true" enableUpdateByExample="true"
enableDeleteByExample="true" enableSelectByExample="true"
selectByExampleQueryId="true">
</table>
</context>
</generatorConfiguration>
如果要扩展对可以调用的数据库操作方法,可以将下面几个值设为true
- 启用插件
使用逆向工程生成的mapper.xml,会自动配置好resultmap id=“BaseResultMap”并且注入实体与数据库的映射
工具类Result
目前的开发模式主要为前后端分离,前后端需要约定好数据交互的格式(JSON),在后端,可以通过建立一个工具类来将数据封装成既定的格式来返回给前端
public class Result<T> {
/**
* 状态码
*/
private String status;
/**
* 获取状态。
*
* @return 状态
*/
public String getStatus() {
return status;
}
/**
* 状态信息,错误描述.
*/
private String message;
/**
* 获取消息内容。
*
* @return 消息
*/
public String getMessage() {
return message;
}
/**
* 数据.
*/
private T data;
/**
* 获取数据内容。
*
* @return 数据
*/
public T getData() {
return data;
}
private Result(String status, String message, T data) {
this.status = status;
this.message = message;
this.data = data;
}
private Result(String status, String message) {
this.status = status;
this.message = message;
}
private Result(String message) {
this.message = message;
}
/**
* 创建一个带有状态、消息和数据的结果对象.
*
* @param status 状态
* @param message 消息内容
* @param data 数据
* @return 结构数据
*/
public static <T> Result<T> buildResult(Status status, String message, T data) {
return new Result<T>(status.getCode(), message, data);
}
/**
* 创建一个带有状态、消息和数据的结果对象.
*
* @param status 状态
* @param message 消息内容
* @return 结构数据
*/
public static <T> Result<T> buildResult(Status status, String message) {
return new Result<T>(status.getCode(), message);
}
/**
* 创建一个带有状态和数据的结果对象.
*
* @param status 状态
* @param data 数据
* @return 结构数据
*/
public static <T> Result<T> buildResult(Status status, T data) {
return new Result<T>(status.getCode(), status.getReason(), data);
}
/**
* 创建一个带有状态的结果对象.
*
* @param status 状态
* @return 结构数据
*/
public static <T> Result<T> buildResult(Status status) {
return new Result<T>(status.getCode(), status.getReason());
}
public enum Status {
/**
* 状态
*/
OK("200", "正确"),
BAD_REQUEST("400", "错误的请求"),
UNAUTHORIZED("401", "禁止访问"),
NOT_FOUND("404", "没有可用的数据"),
PWD_ERROR("300", "密码错误"),
EXIT("403", "已经存在"),
INTERNAL_SERVER_ERROR("500", "服务器遇到了一个未曾预料的状况"),
SERVICE_UNAVAILABLE("503", "服务器当前无法处理请求"),
ERROR("9999", "数据不能为空");
/**
* 状态码,长度固定为6位的字符串.
*/
private String code;
/**
* 错误信息.
*/
private String reason;
Status(String code, String reason) {
this.code = code;
this.reason = reason;
}
public String getCode() {
return code;
}
public String getReason() {
return reason;
}
@Override
public String toString() {
return code + ": " + reason;
}
}
}
调用方法
集成PageHelper进行分页
- 添加依赖:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.3</version>
</dependency>
- 在mapper.xml中添加查询sql语句
<select id="findByPaging" resultMap="BaseResultMap" parameterType="map">
select * from PARM_FUND_INFO
</select>
- 添加接口
Page<FundInfo> findByPaging();
- 在业务层及其实现类调用接口
//service
List<FundInfo> findByPage(Integer pageNum,Integer pageSize);
//servic.impl
@Override
public List<FundInfo> findByPage(Integer pageNum, Integer pageSize) {
PageHelper.startPage(pageNum,pageSize);
Page<FundInfo> data=fundInfoMapper.findByPaging();
List<FundInfo> fundInfos=data;
return fundInfos;
}
- 在controller层进行调用
@ApiOperation("分页测试")
@GetMapping("/page")
@CrossOrigin
public Result findByPaging(Integer pageNum,Integer pageSize){
List<FundInfo> fundInfos=fundInfoService.findByPage(pageNum,pageSize);
return Result.buildResult(Result.Status.OK,fundInfos);
}
- 通过swagger测试
返回成功,有对应条数的数据