SpringBoot框架

IDEA自动注入警告问题

配置详见
在这里插入图片描述

SpringBoot

  • YML文件
  • SpringBoot用法
  • 整合Mybatis/MybatisPlus
  • 查询方法
    • selectObjs 查询符合条件的第一列数据
    • selectList 返回对象信息
    • selectMap 返回有效字段
    • selectPage 分页查询返回对象
    • selectMapsPage 分页查询返回有效字段
  • 条件构造器的使用
    • 特殊字符(eq,gt,lt,select)
    • or
    • like
    • orderBy
    • in

1.SpringBoot介绍

Spring Boot是由Pivotal团队提供的全新框架,设计目的是用来简化Spring应用的搭建以及开发过程
作用: SpringBoot的推出 让程序员更加专注于业务处理, SpringBoot是学习微服务框架基石(SpringCloud)
Spring 和SpringBoot关系 :SpringBoot是Spring的工具API框架

2.SpringBoot项目说明

2.1安装SpringBoot插件

  • IDEA破解版本,自带SpringBoot插件 无需安装
  • IDEA社区版本.需要手动安装一个SpringBoot插件.
    在这里插入图片描述

2.2 创建SpringBoot项目

在这里插入图片描述

  • 或在线创建直接导入
    在这里插入图片描述
    在这里插入图片描述
    springboot将公共的常用的功能以Filter插件的形式进行管理,直接查询勾选依赖项即可
    在这里插入图片描述

SpringBoot项目内置了Tomcat服务器,启动项目Tomcat服务器随之启动,Tomcat是项目部署的一个容器,项目需要持久的在服务器上运行,
在这里插入图片描述

2.2 SpringBoot项目结构

POM文件说明

  • parent标签
    父级工程 管理jar包的配置,SpringBoot将现有主流的框架都进行了整合,包含Spring,SpringMVC,Mybatis,以及其他主流框架radies , 在内部完成了jar包的依赖的配置.如果用户需要,则只添加某些核心包那么所有的依赖都会按照规则自动的下载.

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.5</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

在这里插入图片描述

  • 依赖配置项
    Springboot通过启动项的方式(spring-boot-starter) ,进行jar包文件的加载,同时这些功能中的配置项(Spring中写的cinfig) , SpringBoot有些也会自动的完成.无特殊的要求 无需手动的配置.开箱即用的思想!即加上jar包无需配置即可使用
		<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>
  • build标签
    关于build标签说明:
    1.build标签它是maven工程的要求.
    2.作用: 完成项目的打包/发布等一系列的功能.
    3.该标签的使用是SpringBoot必须的,如果没有该标签则项目无法使用jar包运行
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

2.3 SpringBoot Maven操作

项目打包

在这里插入图片描述
在target目录下找jar包,或是去本地库根据坐标寻找jar包

java命令运行项目

  • dos命令
    根目录输入cmd (command )
    1.cd 切换目录
    2.dir 查看当前目录结构
    3.cls 清屏
    4.tab键自动补齐

  • 将jar包文件放置到指定的目录下,根目录中不能有中文空格等特殊字符,jar包一般以项目名或端口号命名
    在这里插入图片描述

  • java命令
    在这里插入图片描述

  • 项目关闭
    1.直接将dos窗口关闭
    2.ctrl + c 万能的关闭指令

常见项目问题

2.4 jar包依赖传递性(Maven的工作原理)

  • 问题: 当引入spring-boot-starter-web包时, 其他jar包文件如何依赖的?

在这里插入图片描述

maven jar包依赖的传递性

  1. A.jar 运行时依赖于 B.jar
  2. B.jar 运行时依赖于 C.jar
    此时运行A.jar会自动加载B和C的依赖
  • Maven的工作原理:
    1.当maven解析pom.xml文件时,会根据maven坐标先去本地库查找指定的jar包文件
    在这里插入图片描述

2.当jar包加载完成之后,由于该项目也是maven工程,所以maven工具会解析该项目的pom.xml文件 , 根据POM.xml文件 再次加载依赖包 直到所有的jar包依赖加载完成
在这里插入图片描述

maven 如何保证jar包依赖传输的安全性

传输加密机制: SHA1算法
SHA-1(英语:Secure Hash Algorithm 1,中文名:安全散列算法1)是一种密码散列函数。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数
在这里插入图片描述

  • 常识:
  1. java中常见hash码为8位16进制数,共有(2^32)中组合
    一位数有16种可能即 (2^4 ), 8位数 即 (24)8

  2. 如果给定的key相同,那么计算所得的hashcode必然相同,key不同结果有可能一样(哈希碰撞,概率极低,位数越高碰撞几率越小)

  3. 一个数据1kb,一个数据1mb hash计算一样快

    • 数据占据内存中的一块区域,hash计算时,会在当前数据当中取点进行计算(不同hash算法取点的个数不同),不会因为数据大小而改变取点个数,所以计算速度相同
  • 原理:
    判断传递前后的sha1算法生成的消息摘要(40位16进制数)是否相同,如果相同则标识传输正常,如果不同,则文件有问题.
    在这里插入图片描述

3. SpringBoot 入门案例

3.1 配置文件(resource)

application.properties
  • 说明: 虽然properties文件是SpringBoot默认配置文件.但是其中编码格式比较繁琐,不便于查看.所以在项目中一般使用YML文件.直接改名即可
    在这里插入图片描述
yml文件说明

YML文件语法:

  • yml数据结构是K-V结构
  • k与v 中间需要使用冒号空格连接
  • yml配置文件有父子级关系,所以需要注意缩进项的位置
    servlet用来接参数的
    context -path: /jt 访问该项目是输入网址害得加/jt
    在这里插入图片描述

在这里插入图片描述

3.2 @Value属性赋值

  • 需求:在YML配置文件中编辑key=value结构,之后利用注解为属性赋值.
编辑YML配置文件
server:
  port: 8090    #配置端口
  servlet:      #web项目发布路径
    context-path: /  #/表示根目录

#无业务交叉所以与server同级
#定义dept属性值 YML文件默认支持UTF-8
#配置文件本身就是字符串,赋值不要加引号
dept:
  id: 100
  name: 财务部
为Dept对象赋值

配置文件已经归于Spring容器进行管理

@Component //将对象交给Spring容器管理
public class Dept {
    //spel表达式
    //注入具体值  使用@value
    //注入对象的引用 使用Autowire
    @Value("${dept.id}")
    private Integer id;
    @Value("${dept.name}")
    private  String  name;

Spring测试类中,需要手动调用容器,在容器中获取对象,执行方法
Springboot测试类简化了流程
注意事项: 以后写代码都必须写到主启动类的同包及子包中因为包扫描看的是主启动类的位置

/**
 * 在执行测试方法时,自动启动容器,所有对象都在容器内部,
 * 所有对象想要手機用,直接注入即可
 */
@SpringBootTest
class DemoApplicationTests {
	//容器启动成功之后,SpringBoot负责讲对象进行注入,对象的引用(注入)
	@Autowired
	private Dept dept;

	@Test
	void contextLoads() {
		System.out.println(dept);
	}
}

3.3 利用properties文件为属性赋值

  • dept.properties文件
#程序读取时,默认iso-8859-1
dept.id2=888
dept.name2=SpringBoot测试
  • 实体类
@Component //将对象交给Spring容器管理
@PropertySource(value = "classpath:/dept.properties",encoding ="utf-8")//加载指定的配置文件
public class Dept {
    //spel表达式
    //注入具体值  使用@value
    //注入对象的引用 使用Autowire
    @Value("${dept.id}")
    private Integer id;
    @Value("${dept.name}")
    private String name;

    //通过配置文件为属性赋值
    @Value("${dept.id2}")
    private Integer id2;
    @Value("${dept.name2}")
    private String name2;

3.4 Lombok插件

安装插件,添加jar包

<!--引入插件lombok 自动的set/get/构造方法插件  -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>	

@Data:ombok插件提供 自动生成get/set/toString/equals/hashcode等方法
@Accessors(chain = true):链式加载注解,相当于重写了set方法,返回this对象

  • 之所以不能连续点方法,是因为set方法返回值是void,想办法让他返回原对象即可,this代表本类对象
    在这里插入图片描述
    在这里插入图片描述

4. 补充:进制转换

二进制—>八进制:
在这里插入图片描述

二进制—>十进制:按权展开求和
在这里插入图片描述
十进制—>二进制:
在这里插入图片描述
八进制—>二进制:
在这里插入图片描述

5. SpringBoot 用法

5.1 环境切换

  • 通过云的方式动态连接配置中心(现在的yml),环境切换为配置中心提供服务
  • 软件一般会在多个不同的环境中运行. 开发阶段有一个dev环境.开发完成会进行测试.则会有测试环境.最终项目部署到用户的服务中 生产环境.如果每次切换环境,都需要手动的修改配置文件,则造成诸多的不便.

需求: 简化环境切换带来的影响

多环境编辑

要求: 如果采用多环境测试,则要求每个环境中的数据项都应该保持一致.(不能只改端口,业务数据也要添加)缺失可能导致项目启动异常.
多环境配置:

  • 关键语法"—" 环境分割 (画布裁剪)
  • 每个环境都应该有自己的名称
    在这里插入图片描述
  • 默认环境名称
    在这里插入图片描述
全部配置
#默认环境选项
spring:
  profiles:
    #默认环境配置名称
    active: test

#环境分割
---
#YML文件语法
## 1.YML数据结构k-v结构
## 2.k与v 需要使用 :"空格" 连接
## 3.YMl配置文件有父子级关系 所以注意缩进项的位置
spring:
  config:
    activate:
      on-profile: prod
server:
  port: 80    #配置端口
  servlet:      #web项目发布路径
    context-path: /  #/表示根目录

#定义dept属性值 YML文件默认支持UTF-8
dept:
  id: 100
  name: 财务部

#环境分割线
---

# 每个环境都应该有自己的名称
spring:
  config:
    activate:
      on-profile: test
server:
  port: 8080    #配置端口
  servlet:      #web项目发布路径
    context-path: /  #/表示根目录

#定义dept属性值 YML文件默认支持UTF-8
dept:
  id: 100
  name: 集團本部

在这里插入图片描述

5.2 热部署

在开发阶段每次修改完源码都要重启服务器,程序才能生效. 热部署可以让程序自动的完成监控,重启服务器.

引入jar包
		<!--支持热部署 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
配置IDEA环境

组合键: ctrl + shift + alt + / 或者 ctrl + alt + a
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.3 SpringBoot整合Mybatis

POM+yml配置+xml+接口

5.3.1 导入数据库

课前资料1.数据库

5.3.2 创建项目

在这里插入图片描述

5.3.3 导入jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.5</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.jt</groupId>
	<artifactId>springboot_demo_2</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>springboot_demo_2</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>

		<!--
			spring-boot-starter 启动项
			当程序解析到该jar包时,就会按照jar包内的配置实例化对象
			包括数据源的配置. 加载数据源......
			没有数据库的链接,springboot并不知道要连接哪个数据库
		-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>

		<!--引入数据库驱动 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<!--只对当前项目有效-->
			<optional>true</optional>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<!--true该jar包文件父子项目之间不传递.-->
			<!--<optional>true</optional>-->
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<!--spring整合mybatis-plus -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.4.2</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<!--在项目打包时将lombok包除外.-->
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>
Maven的jar包作用范围
  • test范围是指测试范围有效,在编译和打包时都不会使用这个依赖
  • compile范围是指编译范围内有效,在编译和打包时都会将依赖存储进去
  • provided依赖,在编译和测试过程中有效,最后生成的war包时不会加入 例如:
    servlet-api,因为servlet-api tomcat服务器已经存在了,如果再打包会冲突
  • runtime在运行时候依赖,在编译时候不依赖
    默认依赖范围是compile
<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-devtools</artifactId>
		<scope>runtime</scope>
		<!--只对当前项目有效,表示两个项目之间依赖不传递-->
		<optional>true</optional>
</dependency>
5.3.4 编辑application.yml
  • 可以理解成SpringBoot启动项有个属性叫jdbc,它要求我们添加数据库的链接,我们就在核心配置文件中添加需求的配置
  • 当程序执行,Spring容器去加载这些核心配置项,这些属性交给了容器进行管理,由于有自动化的配置(开箱即用),只要容器中有数据,自动化配置就会生效,即SpringBoot成功关联数据库
server:
  port: 8090

#SpringBoot 开箱即用
#对应pom文件中的启动项
#<dependency>
#	    <groupId>org.springframework.boot</groupId>
#	    <artifactId>spring-boot-starter-jdbc</artifactId>
#</dependency>

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/jtadmin?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
    username: root
    #检查密码是否正确
    password: root

#SpringBoot整合MybatisPlus配置
mybatis-plus:
  #定义别名包
  type-aliases-package: com.jt.pojo
  mapper-locations: classpath:/mappers/*.xml
  #开启驼峰映射
  configuration:
    map-underscore-to-camel-case: true

#添加MP日志  打印执行的sql
logging:
  level:
    com.jt.mapper: debug
数据源参数说明
  • url: jdbc:mysql://127.0.0.1:3306/jtadmin? 3306为数据库的服务端口,8080位Tomcat服务器占用端口,不可混为一谈
  • serverTimezone=GMT%2B8 %2B 是转义字符代表 “+” 号 .服务时区,新版本的驱动要求必须配置时区
  • &useUnicode=true&characterEncoding=utf8 使用Unicode编码字符集 , 要求字符UTF-8编码
  • &autoReconnect=true 是否自动重连.
  • &allowMultiQueries=true 是否允许批量操作 同时执行多个sql!
Mybatis配置说明

定义别名包:对相同的包路径进行公共的抽取

#SpringBoot整合Mybatis配置项
mybatis:
  #定义别名包
  type-aliases-package: com.jt.pojo
  mapper-locations: classpath:/mybatis/mappers/*.xml
  #开启驼峰映射
  configuration:
    map-underscore-to-camel-case: true
5.3.5 编辑映射文件和接口
  • namespace
    1.Mapper.xml配置文件的唯一标识 , 需要与Mapper接口保持一致.
    如:UserMapper.findAll(); 我们写的是方法,数据库并不认识,只能识别sql语句,UserMapper所调用的方法(findAll)应与映射文件的id(sql)进行绑定,绑定之前应该找到UserMapper接口所关联的配置文件才行,关联映射文件后,通过方法映射Sql语句!!!
  • 查询标签必须有返回值
    • resultType :直接返回POJO对象的数据 适用于单表查询
    • resultMap: 适用于关联查询的结果封装 一般采用第三方对象接收,如查询AB两张表,结果为pojoA和pojoB,谁接收都不合适

resultType="包名.类名"
resultType="com.jt.pojo.DemoUser"
pojo对象DemoUser与数据库表demo_user进行了对应
在这里插入图片描述
在这里插入图片描述
优化:将不变的包路径进行抽取,因为变化的只有类名.
解决方案: 定义别名包即可

	yml配置文件中包路径进行公共的抽取,定义别名包
	type-aliases-package: com.jt.pojo
	则可以直接返回对象的名称:resultType="DemoUser" 
    程序解析时: 首先根据别名包的名称进路径的拼接 com.jt.pojo.DemoUser
映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.jt.mapper.DemoUserMapper">

	<!--编辑更新操作  #mybatis中的一种表达式写法 -->
	<update id="updateUser">
		update demo_user set name=#{nowName} where name=#{oldName}
		and sex=#{sex}
	</update>


	<!--
	完成用户入库操作 id="与方法名称保持一致"
	sql结束时不需要添加;号 在Mysql数据库中执行;号没有问题,
	但是如果在Oracle中执行则必然报错.
	-->
	<insert id="insertUser">
		insert into demo_user(id,name,age,sex)
			value(null,#{name},#{age},#{sex})
	</insert>

	
	<select id="findAll" resultType="DemoUser">
		select * from demo_user
	</select>

	<!--更新操作的标签就是为了方便程序员开发时使用,程序
	执行时没有任何的差别,所以可以混用
	<insert id="">
	</insert>-->
</mapper>
将Mapper接口交给容器管理

@MapperScan("com.jt.mapper")
主启动类添加注解

mapper接口
package com.jt.mapper;
/*注意事项: basemapper必须添加泛型对象 切记!!!*/
//@Mapper //表示将mapper接口交给Spring容器管理,即可以通过Autowire的方式进行动态的注入

public interface DemoUserMapper extends BaseMapper<DemoUser> {
    //使用MP不要重载里边的方法 容易解析异常
    List<DemoUser> findAll();

    void insertUser(DemoUser user);

    void updateUser(String oldName, String nowName, String sex);
}

5.4 测试类

主启动类,管理了接口测试时才可以注入,测试类位置应与主启动类位置对应

@SpringBootApplication  //标识主启动类的注解
@MapperScan("com.jt.mapper")    //将Mapper接口交给容器管理
public class SpringBootRun {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootRun.class,args);
    }
}
@SpringBootTest
public class TestSpringBoot {

    @Autowired
    //通过jdk动态代理创建的代理对象
    private DemoUserMapper userMapper;

    /**
     *  Spring未优化前需要在容器中获取对象
     *  UserService userService = context.getBean(UserService.class);
     */

    @Test
    public void testFindAll(){
        //检查是否为代理对象
        System.out.println(userMapper.getClass());
        /**
         * class com.sun.proxy.$Proxy73
         */

        //有数据说明整合mybatis成功
        List<DemoUser> userList = userMapper.findAll();
        System.out.println(userList);
    }

5.5 SpringBoot创建接口代理对象流程

  • 原先mybatis 获取sql过程
public class MybatisTest02 {
   SqlSession session = null;
	
	@Before
   public  void beforeMethod() throws Exception {

//1.读取mybatis核心配置文件中的配置信息(起名:mybatis-config.xml)
InputStream in= Resources.getResourceAsStream("mybatis-config.xml");
//2.基于读取的配置信息获取SqlSessionFactory对象(工厂)
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);        
//3.通过工厂对象打开与数据库的连接(即获取SqlSession对象,类似于connection)
//提交方式二:true: 表示自动提交事务, 默认是false, 表示关闭自动提交, 需要手动提交!
session = factory.openSession( true );
//4.获取由Mybatis提供的接口的子类对象实例(参数为接口的字节码对象),底层利用反射剖析这个接口
EmpMapper mapper = session.getMapper(EmpMapper.class);
//接口的实现类中实现了定义的查询方法
    List<Emp> list = mapper.findAll();
		for (Emp emp : list) {
			System.out.println(emp);
		}
	}
  • 模拟mybatis提供的EmpMapper接口的实现类
    构造实现类对象时将连接作为参数传递进来,实现类的目的是执行sql方法,两个重要参数是namespace和id
    • namespace : 通过字节码对象获取实现类的接口数组(Interfaces),根据下标获取接口全类名getName(),即namespace
    • id : 通过当前线程获取方法调用栈的信息(StackTrace),由于栈内存结构特殊性(先进后出),执行sql的方法总是在第二位(栈底总是是用户调用的测试方法),通过getMethodName()获取当前执行的方法名即id

将namespace和id拼接起来就可以通过session执行

	public class EmpMapperImpl implements EmpMapper{
		//当外部有需求与实现类提供的功能一样,就会去new实现类对象
		/**new实现类对象时,将session对象作为参数传递进来,保存在实现类对象内部,类中就可以使用session对象提供的方法*/
		public EmpMapperImpl( SqlSession session ) {
			this.session = session;
		}
		private SqlSession session;
		/* 查询所有的员工信息 */
		public List<Emp> findAll() {
// 1.获取当前这个类的父接口的全类名(=namespace)
			String interName = this.getClass().getInterfaces()[0].getName();
// 2.获取当前方法的名字(=SQL标签的id值)
			StackTraceElement[] st = Thread.currentThread().getStackTrace();
			String methodName = st[1].getMethodName();
			List<Emp> list = session.selectList( interName+"."+methodName );
			return list;
		}
	}	

SpringBoot根据pom文件加载依赖项,Spring根据这些配置实例化对象,放入Spring容器进行统一管理
在这里插入图片描述
容器启动成功之后,通过容器即可注入要使用的对象

  @Autowired
    //通过jdk动态代理创建的代理对象
    private DemoUserMapper userMapper;

在这里插入图片描述

6. SpringBoot 操作DB练习

先写方法,根据报错在接口中创建方法,复制方法名在映射文件中编写sql
如果前端传过来的数据封装在map集合中,#{ }占位符名称要和Map集合的key保持一致!
如果前端传过来的数据封装在POJO对象中,#{ }占位符名称要在POJO对象中有对应的getXxx方法,或者有对应的属性(名称和属性名相同)

  • 入库操作
<insert id="insertUser">
		insert into demo_user(id,name,age,sex)
			value(null,#{name},#{age},#{sex})
	</insert>
//入库操作
    @Test
    public void testInsert(){
        DemoUser user = new DemoUser();
        user.setId(null).setName("mybatis信息").setAge(18).setSex("男");
        userMapper.insertUser(user);
    }
  • 更新操作
<update id="updateUser">
		update demo_user set name=#{nowName} where name=#{oldName}
		and sex=#{sex}
</update>
/**将mybatis name="mybatis信息" 改为"测试信息". 条件  sex="男"
    update demo_user set name="测试信息"
           where name="mybatis信息" and sex="男"
*/
 @Test
    public void testUpdate(){
        //1.封装数据
        String oldName = "mybatis信息";
        String nowname = "测试信息";
        String sex = "男";
        userMapper.updateUser(oldName,nowname,sex);
    }

7. Spring-MybatisPlus

MyBatis-Plus (简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

7.1 MP特点

  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作(xml文件中的sql)
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求(无法解决多表关联查询)

7.2 MP原理

之前操作数据库采用sql(面向过程的语言)方法进行编辑. 但是如果所有的单表操作都由程序员完成.则开发效率低. MP开发了一种机制实现以面向对象的方式操作数据库即 动态拼接sql

  • 1.对象与数据库中的表实现一对一映射—@TableName , 对象是包装体,我们操作的是对象中的属性,对象属性要与表中的字段一对一映射—@TableId+@TableField
  • 2.MP采用BaseMapper的方式 将公共的接口方法进行了抽取. 采用泛型T的方式进行约束
  • 3 .MP将用户操作的对象方法(userMapper.selectList(null))在底层自动的转化为Sql语句!
对象转化Sql原理

int userMapper.insert(T entity);
Sql语句: insert into 表名(字段名) value(属性值…)
在这里插入图片描述

7.3 MP入门案例

导入jar包

苞米豆家的jar包

<!--spring整合mybatis-plus -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.4.2</version>
		</dependency>
编辑POJO对象

MP以面向对象的方式操作数据库.其中对象与表 , 属性与字段都一对一映射
在这里插入图片描述

  • @TableName
    如果如果没有添加注解的属性值,则默认以类名(DemoUser)当做表名(demouser)
  • @TableId
    标识主键 , type 属性取值 IdType.AUTO 表示定义主键自增
    有特殊需求,要求取唯一性数据,如订单号,因为每次新增订单号都需要找到末位订单号再进行自增操作,数据量较大时,主键自增的方式效率较低
    主键可设置为UUID的取值方式,type 属性取值 IdType.ASSIGN_UUID 表示定义主键UUID的取值方式
    • UUID是一种随机HASH算法,HASH算法以当前时间戳T为参数能够保证得到的ID不重复
  • @TableField
    标识属性与表字段映射
    value属性表示表字段名称,若与属性名相同,可省略不写
    exist属性取false表示当前属性不是数据库的字段,但在项目中必须使用,这样在执行入库操作时,mybatis-plus就会忽略这个属性,不会报错
@Data
@Accessors(chain = true)    
@TableName("demo_user") 
public class DemoUser {
    @TableId(type = IdType.AUTO)        
    //@TableId(type = IdType.ASSIGN_UUID)
    private Integer id;
    //@TableField(value = "name",exist = true) 名称一致可以省略
    private String name;
    private Integer age;
    private String sex;
}

编辑Mapper接口

MP提供BaseMapper接口,接口中编辑了常见单表的CRUD操作,用于规范化常见方法的书写,我们的mapper接口在继承BaseMapper时注意添加泛型,泛型即与数据库表对应的接口对象的名称BaseMapper<DemoUser>,使用MP注意不要重写里面提供的方法,容易解析异常
在这里插入图片描述

//@Mapper
public interface DemoUserMapper extends BaseMapper<DemoUser> {   }
编译YML配置文件
  • MP增强了Mybatis, MP内部包含了Mybatis 所以将Mybatis的包删除,否则内部jar包异常
  • 修改mybatis名称
    在这里插入图片描述
测试MP提供的单表查询
 //测试MybatisPlus
    @Test
    public void testMP(){
        List<DemoUser> userList = userMapper.selectList(null);
        System.out.println(userList);
        /**
         * com.jt.mapper.DemoUserMapper.selectList  : ==>  
         *     Preparing: SELECT id,name,age,sex FROM demo_user
         */
    }

7.4 常见MP的API

添加日志打印

yml配置文件中添加日志打印配置,接口包路径对应日志级别debug

#添加MP日志  打印执行的sql
logging:
  level:
    com.jt.mapper: debug
入库操作
/**
     * 入库操作
     * 结果:     ==>  Preparing: INSERT INTO demo_user ( name, age, sex ) VALUES ( ?, ?, ? ) 预编译
     *          ==> Parameters: MP测试(String), 19(Integer), 男(String)
     *          <==    Updates: 1
     */
    @Test
    public void insert(){
        DemoUser user = new DemoUser();
        user.setName("MP测试").setSex("男").setAge(19);
        userMapper.insert(user);
    }
更新操作

updateById方法 , 原理是根据对象中不为null的属性当做set条件. 且Id必须赋值 并且ID当做唯一where条件
在这里插入图片描述

/**
     *  更新操作:
     *  com.jt.mapper.DemoUserMapper.updateById  : 
     *  ==>  Preparing: UPDATE demo_user SET name=?, age=? WHERE id=?
     *  ==>  Parameters: 英雄联盟(String), 22(Integer), 231(Integer)
     *  <==    Updates: 1
     */
    @Test
    public void updateById(){
        DemoUser user = new DemoUser();
        user.setName("英雄联盟").setAge(22).setId(231);
        userMapper.updateById(user);
    }

update(entity, updateWrapper);
参数说明:
1.entity 实体对象 需要修改的数据进行封装
2.updateWrapper 条件构造器

 /**
     * 需求: 更新数据
     *       将name="中午吃什么" 改为name="晚上吃什么"
     *       性别: 改为 其他
     * Sql:
     *      update demo_user set name="xxx",sex="其他"
     *          where name="xxxx"
     *  结果:          
     *   Preparing: UPDATE demo_user SET name=?, sex=? WHERE (name = ?)
     *   Parameters: 卡特琳娜(String), 其他(String), 英雄联盟(String)
     *   
     *    Preparing: SELECT id,name,age,sex FROM demo_user WHERE (sex = ? AND name = ?)
     *    Parameters: 其他(String), 卡特琳娜(String)
     *
     */
    @Test
    public void testSelect10(){
        DemoUser user = new DemoUser();
        user.setName("卡特琳娜").setSex("其他");
        UpdateWrapper<DemoUser> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("name", "英雄联盟");
        userMapper.update(user, updateWrapper);
        
        QueryWrapper<DemoUser> queryWrapper1 = new QueryWrapper<>();
        queryWrapper1.eq("sex", "其他").eq("name", "卡特琳娜");
        DemoUser user1 = userMapper.selectOne(queryWrapper1);
        System.out.println(user1);
    }
查询操作
根据主键ID查询

返回的一条记录

 /**
     * 需求:id=21的用户  根据ID查询数据   
     * 结果: Preparing: SELECT id,name,age,sex FROM demo_user WHERE id=?
     *      Parameters: 21(Integer)
     *       DemoUser(id=21, name=孙尚香D, age=18, sex=女)
     */
    @Test
    public void testSelect(){
        DemoUser user = userMapper.selectById(21);
        System.out.println(user);
    }
根据对象查询
  • selectObjs
    返回表中第一列数据,由于不知道第一列数据类型,所以返回对象类型组成的list集合

应用场景:MP虽然不支持多表关联查询,但是可以单表自关联查询

1.一般根据条件查询Id的值,查询之后为后续的sql提供数据支持

2.有时用户只需要查询ID的值,并不需要其他数据项时 使用objs.

 /**
     * 需求: 查询表中第一列数据   selectObjs
     * 说明: queryWrapper=null 不需要where条件
     * 结果:Preparing: SELECT id,name,age,sex FROM demo_user
     *     Parameters:
     *     Total: 48
     * [1, 3, 4, 5, 6, 7, 8, 9, 11, 12, 16, 17, 18, 21, 22, 23, 24,
     * 25, 27, 31, 38, 39, 40, 41, 43, 44, 46, 49, 50, 51, 52, 53, 55,
     * 58, 62, 66, 68, 172, 173, 182, 194, 196, 227, 228, 229, 230, 231, 232]
     */
    @Test
    public void testSelect11(){
        List objs = userMapper.selectObjs(null);
        System.out.println(objs);
    }

如下:此处查询到的objs可以作为参数按需查询

/**
     * 需求: 查询表中所有男性用户的ID
     * 结果:  Preparing: SELECT id,name,age,sex FROM demo_user WHERE (sex = ?)
     *       Parameters: 男(String)
     *       Total: 30
     * [1, 3, 4, 5, 6, 7, 8, 9, 22, 23, 24, 25, 43, 49, 50, 51, 52, 
     * 53, 55, 58, 62, 66, 68, 182, 194, 196, 227, 229, 231, 232]
     */
    @Test
    public void testSelect7(){
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("sex","男");
        List objs = userMapper.selectObjs(queryWrapper);
        System.out.println(objs);
    }
  • selectList()

返回的是满足所有条件的List集合单条数据包含表中所有列
selectList()需要传递queryWrapper对象(条件构造器) , 用于拼接Where条件,值可以为null

原则:根据对象中不为null的属性生成Where条件,具体操作时,需要先封装要执行的条件对象,将条件对象传递给条件构造器,用于构造Where条件,左侧一定要加泛型才可以链式加载多个条件

给查询方法selectList()传递条件构造器,即可完成查询

实现面向对象的方式操作数据库

 /**
     * 根据属性查询
     * 需求:查询name="白骨精" sex=女 的数据   List
     * 结果:Preparing: SELECT id,name,age,sex FROM demo_user WHERE name=? AND sex=?
     *      Parameters: 白骨精(String), 女(String)
     * 		[DemoUser(id=41, name=白骨精, age=3000, sex=女)]
     */
    @Test
    public void test2(){
        DemoUser user = new DemoUser();
        DemoUser user1 = user.setName("白骨精").setSex("女");
        QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>(user1);
        List<DemoUser> users = userMapper.selectList(queryWrapper);
        System.out.println(users);
    }
根据条件构造器查询

条件构造器为空则查询泛型所对应的表的全部数据
条件构造器参数 :

  • @param condition 执行条件,表示该条件是否加入最后生成的sql,默认为true
  • @param column 字段 ,表示数据库字段,
  • @param val
 /*Preparing: SELECT id,name,age,sex FROM demo_user
==> Parameters: 
<== Total: 48*/
  List<DemoUser> userList = userMapper.selectList(queryWrapper);
        System.out.println(userList);
特殊字符的用法

如果查询条件中有特殊关系符,则使用特殊转义字符查询

特殊关系符转义
>gt
<lt
=eq
> =ge
< =le
< >ne

默认连接符: AND 不用写
or直接点

/**
     * 需求: 查询age>18岁  并且性别为女的用户
     * Sql: select * from demo_user where age > 18 and sex="女"
     * 结果:Preparing: SELECT id,name,age,sex FROM demo_user WHERE (age > ? AND sex = ?)
     *                 Parameters: 18(Integer), 女(String)
     *                 Total: 6
     * */
    @Test
    public void testSelect2(){
        QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("age", 18).eq("sex", "女");
        List<DemoUser> userList = userMapper.selectList(queryWrapper);
        System.out.println(userList);
    }
OR的用法

主动调用or表示紧接着下一个方法不是用and连接!(不调用or则默认为使用and连接)

/**
     * 需求: 查询  age>18 or age < 50 的数据
     * Sql: select * from demo_user where age > 18 or age < 50
     * 结果: SELECT id,name,age,sex FROM demo_user WHERE (age > ? OR age < ?)
     *      18(Integer), 50(Integer)
     *      Total: 48
     */
 @Test
    public void testSelect2(){
        QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("age", 18).or().lt("age", 50);
        List<DemoUser> userList = userMapper.selectList(queryWrapper);
        System.out.println(userList);
    }
Like的用法

看 % 的位置 决定左右

释义Sql条件构造代码
查询name中以精开头的数据name like " 精 % "likeRight
查询name中以精结尾的数据name like " % 精 "LikeLeft
查询name中含"精"字的数据name like " % 精 % "Like
查询name中不包含"精"字的数据notlike " % 精 % "notLike

notLikle : 查询与条件不匹配的数据
在这里插入图片描述

/**
     * like关键字
     * 需求: 查询name中包含"精"字的数据
     * 结果: Preparing: SELECT id,name,age,sex FROM demo_user WHERE (name LIKE ?)
     *      Parameters: %精%(String)
     *      Total: 2
  */
     @Test
    public void testSelect3(){
        QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>();
        QueryWrapper<DemoUser> like = queryWrapper.like("name", "精");
        List<DemoUser> userList = userMapper.selectList(like);
        System.out.println(userList);
    }
Order by的用法(排序)

orderByDesc()降序排列,参数为字段column

/**
     * 查询sex=男的数据,以id倒序排列
     * Sql: select * from demo_user where sex='男' order by id desc
     * 结果: Preparing: SELECT id,name,age,sex FROM demo_user WHERE (sex = ?) ORDER BY id DESC
     *      Parameters: 男(String)
 */
    @Test
    public void testSelect4(){
        QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("sex", "男")
                    .orderByDesc("id");
        List<DemoUser> userList = userMapper.selectList(queryWrapper);
        System.out.println(userList);
    }
IN的用法

in(R column, Collection<?> value) 集合形式传递参数
in(R column, Object... values)可变参数形式传递,注意是对象类型

  • 可变参数形式
==> Preparing: SELECT id,name,age,sex FROM demo_user WHERE (id IN (?,?,?,?,?))
==> Parameters: 1(Integer), 3(Integer), 5(Integer), 6(Integer), 7(Integer)
 <==      Total: 5

在这里插入图片描述
值得注意,用户查询数据时 1,3,5,6,7 是从前端传递来的参数,本身就是可变参数,此种方式不符合动态要求

参数应是一个数组.
在这里插入图片描述

在这里插入图片描述
MP在解析时,把数组整体当做了对象查询,并没有根据数组内的数据进行查询,需要将数组转化成集合

API:
在这里插入图片描述
传递可变参数的泛型对象

List<Integer> asList = Arrays.asList(1, 3, 5, 6, 7);

要么传递一个数组对象,所以数组类型不可用基本数据类型int参与转换,应用包装类型

int[] ids = {1,3,5,6,7}; x
Integer[] ids = {1,3,5,6,7};
/**
     * 5.查询id= 1,3,5,6,7的用户
     * Sql: select * from demo_user where id in (xxx,xx,xx)
     * 结果:Preparing: SELECT id,name,age,sex FROM demo_user WHERE (id IN (?,?,?,?,?))
     *      Parameters: 1(Integer), 3(Integer), 5(Integer), 6(Integer), 7(Integer)
     *      Total: 5
     */
    @Test
    public void testSelect5(){
        Integer[] ids = {1,3,5,6,7};
        List<Integer> idlist = Arrays.asList(ids);
        //List<Integer> asList = Arrays.asList(1, 3, 5, 6, 7);
        QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>();
        // queryWrapper.in("id",ids);   数组必须为包装类型,不如list集合好用
        queryWrapper.in("id",idlist);
        List<DemoUser> userList = userMapper.selectList(queryWrapper);
        System.out.println(userList);
    }
condition参数用法—可变参数(动态查询)

condition: 内部编辑一个判断的条件, 如果返回值结果为true 则拼接该字段 . 如果为false 则不拼接该字段.

 select * from demo_user
		 where name!=null name=xxx and age!=null age>xxx
name不为空才会执行 name= xxx , 同理age不为空才会执行后面的语句
此处 name!=null 和 age!=null 就是 condition
  • CollectionUtils.isEmpty(ageList) 判断集合是否为空
public static boolean isEmpty(@Nullable Collection<?> collection) {
		return (collection == null || collection.isEmpty());
	}
  • StringUtils.isEmpty(name)判断字符串是否为空 基本不用

  • StringUtils.hasLength(str)判断字符串是否有效

public static boolean hasLength(@Nullable CharSequence str) {
        return str != null && str.length() > 0;
    }
  • 如果调用的是对象(DemoUser)的equals()方法,则一定要重写后再使用,此处调用的是String 的equals(),底层已经重写,比的就是内容
boolean flag =  name !=null && !"".equals(name);
 /**
     * 需求: 如果根据name属性和age属性查询数据. 有时某个数据可能为null,要求动态查询!!!
     *      where name=xxx age>xxxx    mybatis中的if标签
     *
     * 伪Sql: select * from demo_user
     *              where name!=null name=xxx and age!=null age>xxx
     *              
     * 结果:SELECT id,name,age,sex FROM demo_user WHERE (name = ? AND age > ?)
     * Parameters: 小乔(String), 18(Integer)
     * Total: 1---[DemoUser(id=43, name=小乔, age=19, sex=男)]
     */

    @Test
    public void testSelect6(){
        QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>();
        String name = "小乔";
        int age = 18;
        //boolean flag =  name !=null && !"".equals(name);
       queryWrapper.eq(StringUtils.hasLength(name),"name", name)
                    .gt(age>0,"age",age);
        List<DemoUser> userList = userMapper.selectList(queryWrapper);
        System.out.println(userList);
    }
  • 案例二 含有condition参数的like和in关键字的sql
 /**
     * 需求:查询name包含字符小,并且age属于[17 , 18 , 19 , 20 ]。
     * 伪sql: select * from demo_user
     *              where  (name LIKE %小%) and   age IN (?,?,?,?,?)
     * 结果:Preparing: SELECT id,name,age,sex FROM demo_user WHERE (name LIKE ? AND age IN (?,?,?,?))
     * Parameters: %小%(String), 17(Integer), 18(Integer), 19(Integer), 20(Integer)
     *  Total: 5
     *  [DemoUser(id=11, name=小乔, age=17, sex=女), 
     *   DemoUser(id=43, name=小乔, age=19, sex=男), 
     *   DemoUser(id=49, name=小兰兰, age=18, sex=男), 
     *   DemoUser(id=53, name=小明, age=18, sex=男), 
     *   DemoUser(id=227, name=小法, age=20, sex=男)  ]
     */
    @Test
    public void testcondition(){
        QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>();
        String name = "小";
        List<Integer> asList = Arrays.asList(17, 18, 19, 20);
        queryWrapper.like(StringUtils.hasLength(name),"name",name)
                     .in(!asList.isEmpty(),"age",asList);
        List<DemoUser> userList = userMapper.selectList(queryWrapper);
        System.out.println(userList);
    }
查询指定字段的数据

selectObjs 对比记忆

 /**
     * 需求: 想查询name/sex字段
     *  queryWrapper.select("name","sex"); 挑选指定字段
     *  Preparing: SELECT name,sex FROM demo_user
     *  [   DemoUser(id=null, name=黑熊精, age=null, sex=男),
     *      DemoUser(id=null, name=金角大王, age=null, sex=男),
     *      DemoUser(id=null, name=银角大王, age=null, sex=男),
     *      DemoUser(id=null, name=唐僧, age=null, sex=男), .....]
     */
    @Test
    public void testSelect8(){
        QueryWrapper<DemoUser> queryWrapper = new QueryWrapper();
        queryWrapper.select("name","sex");
        List objs = userMapper.selectList(queryWrapper);
        System.out.println(objs);
    }
返回有效字段

上述方法查询结果封装在了DemoUser对象中,结果有冗余数据,并不方便,应只返回有效字段更为高效,不能用DemoUser对象封装,selectMap要求返回数据使用Map集合封装,K取表字段名,所以是String类型,字段值类型繁杂,所以为objs类型,由于存在多条数据,所以最外层嵌套List集合.

/**
     *   需求: 想查询name/age字段 要求只返回有效字段
     *   返回有效字段的查询:selectMaps
     *   结果: Preparing: SELECT name,age FROM demo_user
     *         Total: 48  
     *   [  {name=黑熊精, age=3000},
     *      {name=金角大王, age=3000},
     *      {name=银角大王, age=4000},
     *      {name=唐僧, age=30},.....    ]
     */
    @Test
    public void testSelect9(){
        QueryWrapper<DemoUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("name","age");
        List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
        System.out.println(maps);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值