springboot + mybatis plus 入门-爱代码爱编程
SpringBoot + Mybatis Plus 入门
前置
- C/S架构:交互性强,网络流量低,响应速度快,客户端负责大部分UI和业务逻辑;但需要针对不同平台开发软件,而且需要更新客户端
- B/S架构:客户端通过浏览器,向服务器发送请求;服务器处理逻辑、数据等并返回Web页面,并把Web页面展示给用户
SpringBoot入门
SpringBoot
基于Spring框架的,简化配置流程;遵循了 约定优于配置 的原则,需要的配置较少;能够使用内嵌的Tomcat和Jetty服务器,不需要部署war包;提供定制化启动器Starters,简化Maven配置;纯Java配置,没有代码生成,不使用XML配置;提供了生产级的服务监控方案;
快速创建SpringBoot应用
-
使用Idea的Spring Initialzr创建,设定项目信息
-
选择需要配置的,如SpringWeb等
-
设置Controller类,使用@RestController标记Controller类,使用@GetMapping标记对应的请求路径和处理方法
@RestController public class HelloController { @GetMapping("/hello") public String hello(){ return "Hello World!"; } }
-
热部署
-
使用spring-boot-devtools组件使得可以不用重启SpringBoot应用即可重新编译、启动项目
-
devtools会监听classpath下的文件变动,触发Restart类加载器重新加载该类
-
不是所有的更改都需要重启应用,可以使用spring.devtools.restart.exclude指定某些不需要重启应用的路径
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
#在application.properties中配置 spring.devtools.restart.enabled = true spring.devtools.restart.additional-paths= src/main/java
-
在Settings中的Build、Execution、Deployment中选择Compile,勾选自动构建;
-
在Settings中的Advanced Settings中勾选在项目运行时自动构建
@SpringBootApplication public class SpringbootFastbuildApplication { public static void main(String[] args) { SpringApplication.run(SpringbootFastbuildApplication.class, args); } }
-
SpringBoot Web入门
SpringBoot整合了传统Web开发的mvc、json、tomcat等架构,提供了spring-boot-starter-web组件,简化了Web配置流程;在选择创建SpringWeb后,启动器会自动加入项目,并提供web、webmvc等基础依赖组件;webmvc为Web开发基础架构,json为JSON数据提供解析,tomcat为自带的容器依赖。
控制器
- @Controller:是普通的控制器,和SpringMVC一样,需要返回一个对应的视图名称,通常和thmeleaf结合使用
- @RestController :把默认返回的数据对象转为JSON格式,使用了RESTful风格的服务
路由映射
- @RequestMapping:负责URL的路径映射
- 添加在Controller类上,表示一个映射前缀,类中所有的方法的映射路径都包含此前缀;添加在方法上则表示该方法具体的映射路径
- 参数:
- value:请求的URL路径,支持模板和正则表达式
- *匹配单词 ,**匹配多级路径单词,?匹配字符
- method:响应的HTTP请求方法
- consumes:请求的媒体类型(Content-Type),如application/json
- produces:响应的媒体类型
- params,headers:请求的参数和请求头的值
- value:请求的URL路径,支持模板和正则表达式
参数传递
- 使用Controller方法对应的参数列表,设置与请求字段相同的参数名称即可
- 如果参数名称和请求字段名称不一致,可以使用@RequestParam设置把请求字段映射到参数的名称,可以使用required字段设置是否一定需要该字段
- 使用实体类型传递参数,需要参数类型和实体类的属性一致才能自动装配
- 如果使用POST方法且参数类型是3w的则不需要加@RequestBody注解,如果使用JSON则需要此注解
@RestController
public class HelloController {
@RequestMapping( value ="/hello/*",method = RequestMethod.POST)
//@GetMapping 使用get方式
public String hello(String username,@RequestParam(value = "pwd", required = false) String password){
return "Hello "+username+"\n"+password;
}
@RequestMapping( value ="/register",method = RequestMethod.POST)
public String register(User user){
return user.toString();
}
}
Web进阶
静态资源访问
-
IDEA创建的Spring Boot项目默认在Resources下创建了static目录,用于存放静态资源;如果是前后端分类项目,这里一般不存放东西;可以配置静态资源过滤策略和静态资源位置
-
存放在该目录的文件可以被直接在浏览器中使用文件名访问
-
设置虚拟路径(过滤规则)来使其访问必须经过该路径
spring.mvc.static-path-pattern=/images/**
-
使用自建目录存放静态资源
spring.web.resources.static-locations=classPath:/css
-
文件上传
- 需要在前端传输表单,表单的enctype属性规定了发送到服务器之前应该如何对表单数据进行编码;当enctype=“application/x-www-form-urlencoded”(默认情况),form表单的数据格式为 key=value&key=value; 当表单的enctype =“multipart/from-data”,表单会将不同的input分隔存储
- SpringBoot需要配置文件上传的大小(默认最大1M)和单个HTTP请求中的文件传送的总大小
- 当表单enctype =“multipart/from-data”,可以通过MultipartFile获取上传的文件,再通过transferTo方法写入磁盘中
spring.servlet.multipart.max-file-size=100MB
#配置tomcat运行的目录下的子目录
spring.web.resources.static-locations=/upload/
@RestController
public class FileUploadController {
@PostMapping("/upload")
public String upload(String username, MultipartFile photo, HttpServletRequest request){
System.out.println("From user "+ username);
System.out.println(photo.getName());
System.out.println(photo.getContentType());
//使用Request获得服务器端的上下文以及其对应的路径
String path = request.getServletContext().getRealPath("/upload/");
saveFile(photo,path);
System.out.println(path);
return "OK!";
}
public void saveFile(MultipartFile file, String path){
File dir = new File(path);
if(!dir.exists())
dir.mkdir();
File f = new File(path+file.getOriginalFilename());
try {
file.transferTo(f);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
拦截器
-
拦截器是Web系统中常见的操作,可以拦截一些请求做出统一处理,主要是多个业务需要的功能,都可以使用拦截器完成
- 权限检查:如登录检测
- 性能监控:如记录处理请求的时间
- 通用行为:读取cookie获得用户信息并放入请求,方便其他业务使用等
-
SpringBoot定义了HandlerInterceptor接口来定义拦截器的功能
-
HandlerInterceptor定义了preHandle,postHandle和afterHandle的方法,在pre和post之间调用目标方法
-
拦截器使用
-
定义拦截器,实现HandlerInterceptor接口,实现对应的三个需要的方法
public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("Login Check" + request.getRequestURL()); return true; } }
-
如果这些方法返回true,则会继续调用后续的拦截器,否则不再继续调用
-
注册拦截器,设置Web配置类,继承WebMvcCnfigurer类
- addPathPatterns方法定义拦截的地址,如果拦截器不设置此项则默认拦截所有请求
- excludePathPatterns定义排除某些地址不被拦截,如果不设置此项则也默认拦截所有请求
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/hello/**"); } }
-
-
RESTful + Swagger
RESTful
表述性状态转移,是一种简洁的互联网服务的架构风格
- 每个URI代表一种资源
- 客户端使用GET、POST、PUT、DELETE的HTTP动词方式表示对服务器资源的访问
- 通过操作资源的表现形式来实现服务端的请求操作
- 资源的形式是JSON或者HTML
- 客户端和服务器的交互是无状态的,每个请求包含必要的信息
- 符合RESTful规范的Web API要符合的特性
- 安全性:安全的方法被期望不产生副作用,如GET请求只能获取资源而不能删除或改变资源
- 幂等性:使用同一个接口,对一个资源进行请求多次的结果应该是一致的
- RESTful Method
- HTTP提供的POST、GET、PUT、DELETE的操作对Web资源进行访问
Spring实现RESTful API
-
使用@RequestMapping注解并设置处理请求的格式,如GET;使用@RequestMapping衍生的注解,如@GetMapping,@PatchMapping
-
要求请求的URI中不要包含动词,并且把参数作为路径传递
@Controller public class UserController { @GetMapping("/user/{id}") @ResponseBody public String getUserById(@PathVariable int id){ System.out.println(id); return "Hello" + id; } }
Swagger
Swagger是一个规范和完整的框架,用于生成、描述、调用可视化RESTful风格的WEb服务;也可以动态生成完善的RESTfulAPI的文档,并且根据后台代码修改来同步更新,同时提供完整的测试页面来调试API
-
引入依赖
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency>
-
加入配置类
@Configuration @EnableSwagger2 public class Swagger2Config { @Bean public Docket createRestApi(){ return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com")) .paths(PathSelectors.any()).build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("FastBuild API") .description("使用Swagger2") .build(); } }
MyBatis Plus
ORM
对象关系映射,解决面向对象与关系型数据库存在的不匹配,帮助程序员完成对象和数据库的数据之间的映射,本质上是减少了编程中操作数据库的编码
MyBatis Plus
在Mybatis上增强了功能,使得Java中的POJO和数据库中的表之间的映射更为轻松,简化了开发
添加依赖
Mybatis Plus的依赖与Mybatis
<!-- mybatis plus 依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.22</version>
</dependency>
全局配置
配置springboot配置文件
#配置数据源和数据库连接
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:33006/mybatis_plus
spring.datasource.username=root
spring.datasource.password=123456
#设置日志输出
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
配置启动器前的mapper扫描方式@MapperScan
@SpringBootApplication
@MapperScan("com.fantank.springbootmybatisplus.mapper")
public class SpringbootMybatisplusApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisplusApplication.class, args);
}
}
配置Mybatis CRUD注解
- @Insert
- @Update
- @Delete
- @Select
- @Result
- 封装结果集
- Results
- 可以和Result一起使用,封装多个结果集
- One
- 实现一对一结果封装
- Many
- 实现一对多结果封装
如果不使用@MapperScan,则需要在Mapper接口上加@Mapper注解
使用mybatis可以执行sql语句
@Mapper
public interface UserMapper {
@Select("select * from user")
public List<User> getAllUser();
//sql语句中占位符的参数名要和实体类属性一致
@Insert("insert into user values(null,#{username},#{password},#{birthday})")
public int insertUser(User user);
}
@RestController
public class UserController {
@Autowired
private UserMapper userMapper;
@GetMapping("/user")
public String getAllUser(){
List<User> allUser = userMapper.getAllUser();
System.out.println(allUser);
return allUser.toString();
}
@PostMapping("/insert")
public String insertUser(User user){
int records = userMapper.insertUser(user);
return records > 0 ? "Success" : "Failed";
}
}
Mybatis Plus使用
- 使用mybatis plus后,需要给实体类对应的mapper接口继承BaseMapper,这样就会使得和数据库中表同名的实体类可以直接使用预设的增删改查功能。
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
@RestController
public class UserController {
@Autowired
private UserMapper userMapper;
@GetMapping("/user")
public String getAllUser(){
List<User> allUser = userMapper.selectList(null);
System.out.println(allUser);
return allUser.toString();
}
@PostMapping("/insert")
public String insertUser(User user){
int records = userMapper.insert(user);
return records > 0 ? "Success" : "Failed";
}
}
package com.baomidou.mybatisplus.core.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;
public interface BaseMapper<T> extends Mapper<T> {
int insert(T entity);
int deleteById(Serializable id);
int deleteByMap(@Param("cm") Map<String, Object> columnMap);
int delete(@Param("ew") Wrapper<T> queryWrapper);
int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);
int updateById(@Param("et") T entity);
int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);
T selectById(Serializable id);
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);
List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);
T selectOne(@Param("ew") Wrapper<T> queryWrapper);
Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);
List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);
List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);
<E extends IPage<T>> E selectPage(E page, @Param("ew") Wrapper<T> queryWrapper);
<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper);
}
- 如果数据库表名和实体类不一致,可以在实体类前加@TableName注解来设置对应的表名
- 使用@TableId设置id相关,如使用type=IdType.AUTO来设置主键,使得其不用设置值也可以自增;也可以使用IdType.ASSIGN_UUID获取唯一标识符;
- 这样设置后,同时也会更新实体类中的主键属性的值
- 如果属性值和数据库对应字段不一致,可以使用@TableFiled设置value对应表中的字段名称,也可以使用exist字段设置该属性是否为数据库表的字段
package com.fantank.springbootmybatisplus.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private int id;
@TableField("username")
private String username;
private String password;
private String birthday;
public User(int id, String username, String password, String birthday) {
this.id = id;
this.username = username;
this.password = password;
this.birthday = birthday;
}
public User() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", birthday='" + birthday + '\'' +
'}';
}
}
多表查询
-
多表查询:为了实现一些复杂的关系映射,可以使用@Results注解、@Result,@One注解和@Many注解完成复杂关系的组合
-
使用@Results来自定义字段到实体类的映射方法,使用@Result获取每个字段对应的属性赋值
-
使用@Many把需要装配该字段,执行对应的方法后返回,可以装配一个一对多的映射集合
@Mapper public interface UserMapper extends BaseMapper<User> { @Select("select * from user where id = #{id}") @Results({ @Result(column = "id", property = "id"), @Result(column = "username", property = "username"), @Result(column = "password",property = "password"), @Result(column = "birthday",property = "birthday"), @Result(column = "id",property = "orders",javaType = List.class, many = @Many(select = "com.fantank.springbootmybatisplus.mapper.OrderMapper.getOrderByUid")) }) List<User> selectUserAndOrdersById(int id); }
public interface OrderMapper { @Select("select * from t_order where uid = #{uid}") public List<Order> getOrderByUid(int uid); }
@GetMapping("/multi/{id}") public String getOrderAndUserById(@PathVariable("id") int id){ List<User> users = userMapper.selectUserAndOrdersById(id); return users.toString(); }
-
使用@One来仅为一个属性指定一个返回的值
@Mapper public interface OrderMapper { @Select("select * from t_order where uid = #{uid}") public List<Order> getOrderByUid(int uid); @Select("select * from t_order") @Results({ @Result(column = "id",property = "id"), @Result(column = "time",property = "time"), @Result(column = "uid",property = "uid",javaType = User.class, one = @One(select = "com.fantank.springbootmybatisplus.mapper.UserMapper.selectById")) }) List<Order> selectAllOrderAndUser(); }
-
条件查询
可以使用QueryWrapper,需要先创建对应类,使用其中的判断条件;注意这样的查询方式使用的是MybatisPlus的方式,需要设置好属性对应不存在的字段和类对应表名等参数
@GetMapping("/find/{id}")
public String findById(@PathVariable("id") int id) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id", id);
return userMapper.selectList(queryWrapper).toString();
}
分页查询
-
定义一个配置类,设置数据库类型
@Configuration public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor paginationInterceptor(){ MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); PaginationInnerInterceptor pi = new PaginationInnerInterceptor(DbType.MYSQL); mybatisPlusInterceptor.addInnerInterceptor(pi); return mybatisPlusInterceptor; } }
-
调用selectPage方法,需要传入一个Page对象,Page中需要设置起始位置索引和每次索引的条数,以及可以传入一个queryWrapper来设置查询条件,Ipage是一个表述分页查询结果的类,其中提供了一些方法和查询得到的内容
@GetMapping("/list/limit") public IPage listByLimit(){ Page<User> page = new Page<>(0,2); IPage iPage = userMapper.selectPage(page, null); return iPage; }