常用框架

SpringSecerity

springsecurity 是用户请求进来,判断有没有请求的权限,抛出异常,重定向跳转。

它的底层是一个过滤器链。

    它自带登录页和退出页,下面的配置中,我设置了默认登录页用自定义的登录页。

首先导入依赖jar包

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

无需在配置文件中配置内容。

紧接着自定义一个配置类就可以了:
@EnableWebSecurity //开启WebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
    //首页所有人可以访问,但是功能页只有对应有权限的人才能访问
    // 请求授权的规则
    http.authorizeRequests()
             //"/afei" 此路径下所有均可访问
            .antMatchers("/afei").permitAll() 
   
            // "/leve1/**" 此路径下只有表明为vip1的权限才可以访问
            .antMatchers("/leve1/**").hasRole("vip1") 
            // "/leve2/**" 此路径下只有表明为vip2的权限才可以访问
            .antMatchers("/leve2/**").hasRole("vip2");

    /** http.formLogin() 没有权限 默认回到登录页 //开启登录页面
    *  定制登录页为自己的登录页 .loginPage("/loginPage")
    * .usernameParameter("user").passwordParameter("pwd") 设置接收前端表单的name值
    **/ loginProcessingUrl("/login"); 表单提交路径
    http.formLogin().loginPage("/loginPage").usernameParameter("user").passwordParameter("pwd").loginProcessingUrl("/login");

    //防止网站工具   get 是明文不安全会被拦截,
    http.csrf().disable(); //关闭防止跨战请求的拦截

    //开启注销
    // .logoutSuccessUrl("/afei"); 注销成功返回 /afei页面
    http.logout().logoutSuccessUrl("/afei");

    //开启记住我功能 其实就是个Cookie   默认保存两周
    //rememberMeParameter("remember"); 接受前端的remember信息
    http.rememberMe().rememberMeParameter("remember");
}

//认证  SpringBoot 2.1.x 可以直接使用
//但是高版本会报错,报错的信息为密码未加密,密码为明文形式是不安全的
// 密码未编码   PasswordEncoder
//Spring Security 5.0 提供了很多加密方式
 /*@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication() .withUser("admin").password("admin").roles("vip1")
            .and().withUser("afei").password("afei").roles("vip2")
            .and().withUser("root").password("root").roles("vip1","vip2");
}*/

//使用MD5对密码进行编译
//.withUser("admin") 配置账号
//.password(new BCryptPasswordEncoder().encode("admin")) 配置密码 并MD5加密
// .roles("vip1")  给此账号分配权限
//  .and() 是拼接的意思,就是继续拼接信息
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
            .withUser("admin").password(new BCryptPasswordEncoder().encode("admin")).roles("vip1")
            .and().withUser("afei").password(new BCryptPasswordEncoder().encode("afei")).roles("vip2")
            .and().withUser("root").password(new BCryptPasswordEncoder().encode("root")).roles("vip1","vip2");

}}

出现的样子用语言描述不清,cv运行一波就理解了~

现在展示页面,可以cv运行一波就行

主页html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
Test
 
<a href="/leve1/1">vip1</a>
<a href="/leve2/2">vip2</a>
 
<a href="/logout">注销</a>
</body>
</html>

vip1:页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
vip1
</body>
</html>

vip2:页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
vip2
</body>
</html>

自定义登录页面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post" action="/login">
    <input type="text" name="user"><br>
    <input type="text" name="pwd"><br>
    <input type="checkbox" name="remember">记住我<br>
    <button type="submit">登录</button>
</form>
</body>
</html>

controller层:

@Controller
public class AllController {
 
 
    @RequestMapping("/leve1/{id}")
    public String leve1(@PathVariable("id") int id){
        return "/leve"+id+".html";
    }
    @RequestMapping("/leve2/{id}")
    public String leve2(@PathVariable("id") int id){
        return "/leve"+id+".html";
    }
    @RequestMapping("/loginPage")
    public String loginPage(){
        return "login.html";
    }
}

MybatisPlus

1、什么是MybatisPlus

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

  • 无侵入
  • 损耗小:启动即会自动注入基本 CURD
  • 支持 Lambda 形式调用
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence)
  • 支持 ActiveRecord 模式
  • 支持自定义全局通用操作
  • 内置代码生成器
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件

2、快速入门

创建数据库mybatis_plus

DROP TABLE IF EXISTS user;CREATE TABLE user
(
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    age INT(11) NULL DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
    PRIMARY KEY (id)
);
DELETE FROM user;INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

初始化项目

引用spring boot starter 父工程
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.5</version>
    <relativePath/>
</parent>
引入spring-boot-starter、spring-boot-starter-test、mybatis-plus-boot-starter、h2依赖:
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.1</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>
注意:尽量不要同时导入mybatis和mybatis_plus,版本差异

application.yml

# DataSource Config
spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql:///mybatis_plus?userUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver

在spring boot启动类中添加@MapperScan注解,扫描Mapper文件夹:

@SpringBootApplication
@MapperScan("com.wen.mybatis_plus.mapper")  //扫描mapper
public class MybatisPlusApplication {public static void main(String[] args) {
        SpringApplication.run(MybatisPlusApplication.class, args);
    }}

3、配置日志

所有的SQL都是不可见的,所以在后台是希望看到SQL是怎么执行的,就必须要配置日志。

在.yml配置文件中配置日志:

#配置日志
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

在这里插入图片描述
简单使用

int insert = userMapper.insert(user);//如果没有设置id,那么会自动生成id
    System.out.println(insert);//受影响行数
    System.out.println(user);//id会自动回填

User user = new User();
    //可以通过条件自动拼接动态SQL
    user.setId(5L);
    user.setName("id:5,修改过后");
    //updateById 参数是一个对象!
    int i = userMapper.updateById(user);
    System.out.println(i);
//注意:updateById 参数是一个对象!而不是ID


4、ID生成器

在复杂分布式系统中,往往需要大量的数据和消息进行唯一标识。比如支付宝每一个账号在数据库分表后都需要有一个唯一ID做标识。此时一个能够生成全局唯一ID的系统是非常必要的。

所以,生成的ID需要具备一下特点:

  • 全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求。
  • 趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。
  • 单调递增:保证下一个ID一定大于上一个ID,例如事务版本号、IM增量消息、排序等特殊需求。
  • 信息安全:如果ID是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量。所以在一些应用场景下,会需要ID无规则、不规则。

上述123对应三类不同的场景,3和4需求还是互斥的,无法使用同一个方案满足。
在这里只讲两种自动生成ID的方案UUID和SnowFlake

在用户ID上添加@TableId注解,里面的值便是使用主键自动生成的方法

@Data
public class User {
//自 3.3.0 开始,默认使用雪花算法+UUID(不含中划线)
    //对应数据库中的主键(UUID、自增id、雪花算法、redis、zookeeper)
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

点击 IdType查看源码看有哪些自动生成方法

/**
 * 生成ID类型枚举类
 *
 * @author hubin
 * @since 2015-11-10
 */
@Getter
public enum IdType {
    /**
     * 数据库ID自增
     * <p>该类型请确保数据库设置了 ID自增 否则无效</p>
     */
    AUTO(0),
    /**
     * 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
     */
    NONE(1),
    /**
     * 用户输入ID
     * <p>该类型可以通过自己注册自动填充插件进行填充</p>
     */
    INPUT(2),/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
    /**
     * 分配ID (主键类型为number或string),
     * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
     *
     * @since 3.3.0
     */
    ASSIGN_ID(3),
    /**
     * 分配UUID (主键类型为 string)
     * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
     */
    ASSIGN_UUID(4);private final int key;IdType(int key) {
        this.key = key;
    }
}   

UUID
UUID(Universally Unique Identifier)的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符,示例:550e8400-e29b-41d4-a716-446655440000

优点:

  • 性能非常高:本地生成,没有网络消耗。

缺点:

  • 没有排序,无法保证趋势递增。
  • UUID往往使用字符串存储,查询的效率比较低。
  • 不易于存储:UUID太长,16字节128位,通常以36长度的字符串表示,很多场景不适用。
  • 信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露,这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。
  • ID作为主键时在特定的环境会存在一些问题,比如做DB主键的场景下,UUID就非常不适用:
  • MySQL官方有明确的建议主键要尽量越短越好[4],36个字符长度的UUID不符合要求。
  • 对MySQL索引不利:如果作为数据库主键,在InnoDB引擎下,UUID的无序性可能会引起数据位置频繁变动,严重影响性能。

SnowFlake(雪花算法)
这种方案大致来说是一种以划分命名空间(UUID也算,由于比较常见,所以单独分析)来生成ID的一种算法,这种方案把64-bit分别划分成多段,分开来标示机器、时间等

核心思想:
使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID),最后还有一个符号位,永远是0,。

优点:

  • 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
  • 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。
  • 可以根据自身业务特性分配bit位,非常灵活。

缺点:

  • 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。

5、乐观锁和悲观锁

悲观锁:十分悲观,认为总是出现问题,无论干什么都会上锁,再去操作
悲观锁是基于一种悲观的态度类来防止一切数据冲突,它是以一种预防的姿态在修改数据之前把数据锁住,然后再对数据进行读写,在它释放锁之前任何人都不能对其数据进行操作,直到前面一个人把锁释放后下一个人数据加锁才可对数据进行加锁,然后才可以对数据进行操作,一般数据库本身锁的机制都是基于悲观锁的机制实现的;
特点:可以完全保证数据的独占性和正确性,因为每次请求都会先对数据进行加锁, 然后进行数据操作,最后再解锁,而加锁释放锁的过程会造成消耗,所以性能不高;

乐观锁:十分乐观,认为不会出现问题,无论干什么都不会去上锁,如果出现问题,就再次更新测试值

乐观锁是对于数据冲突保持一种乐观态度,操作数据时不会对操作的数据进行加锁(这使得多个任务可以并行的对数据进行操作),只有到数据提交的时候才通过一种机制来验证数据是否存在冲突(一般实现方式是通过加版本号然后进行版本号的对比方式实现);
特点:乐观锁是一种并发类型的锁,其本身不对数据进行加锁而是通过业务实现锁的功能,不对数据进行加锁就意味着允许多个请求同时访问数据,同时也省掉了对数据加锁和解锁的过程,这种方式因为节省了悲观锁加锁的操作,所以可以一定程度的的提高操作的性能,不过在并发非常高的情况下,会导致大量的请求冲突,冲突导致大部分操作无功而返而浪费资源,所以在高并发的场景下,乐观锁的性能却反而不如悲观锁。

配置乐观锁
乐观锁实现方式:

  • 取出记录时,获取当前version

  • 更新时,带上这个version

  • 执行更新时,set version = newVersion where version = oldVersion

  • 如果version不对,就更新失败

当要更新一条记录时,是希望这条记录没有被更新的

-- 乐观锁:1、先查询,获取版本号 version=1
-- A线程
update user name = "tian" ,version = version +1
where id = 2 and version = 1
-- 如果B线程抢先完成,这个时候version=2,就会导致A线程修改失败
-- B线程
update user name = "tian" ,version = version +1
where id = 2 and version = 1

测试MP的乐观锁插件

  • 数据库中添加version字段
    在这里插入图片描述
  • 同步实体类(记得在实体类上加上@Version注解)
@Version    //乐观锁version注解
private Integer version;

在这里插入图片描述

  • 配置插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    return interceptor;
}

首先创建配置类文件config,在该文件下创建配置类MyBatisPlusConfig,该类需要添加三个注解:

@Configuration  //配置类
@MapperScan("com.wen.mybatis_plus.mapper")  //扫描mapper
@EnableTransactionManagement    //自动管理事务,默认是开启的

@MapperScan()是将原先MybatisPlusApplication中的扫描换到这里的,所以MybatisPlusApplication中就不需要@MapperScan(),在该配置类里添加@MapperScan()即可

  • 创建完MyBatisPlusConfig类并添加完注解后,就可以将上面的组件的注解方式填入进来
@Configuration  //配置类
@MapperScan("com.wen.mybatis_plus.mapper")  //扫描mapper
@EnableTransactionManagement
public class MyBatisPlusConfig {
    //注册乐观锁插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

此时乐观锁就已经配置完成了!
在这里插入图片描述

6、增删改查

查询操作

//测试查询
@Test
public void testSelectById(){
    User user = userMapper.selectById(1L);
    System.out.println(user);
}
//批量查询
@Test
public void selectBatchIds(){
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
    users.forEach(System.out::println);
}

//条件查询
@Test
public void selectByMap(){
    HashMap<String,Object> map = new HashMap<>();
    //自定义查询
    map.put("name","小文");
    map.put("age",20);
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}

6.1、分页查询

  • 原始的limit进行分页

  • pageHelper第三方插件

  • MyBatisPlus内置分页插件

配置拦截器组件即可

/**
 * 注册插件
 */
@Bean
public MybatisPlusInterceptor paginationInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    // 添加分页插件
    PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor();
    // 设置请求的页面大于最大页后操作,true调回到首页,false继续请求。默认false
    pageInterceptor.setOverflow(false);
    // 单页分页条数限制,默认无限制
    pageInterceptor.setMaxLimit(500L);
    // 设置数据库类型
    pageInterceptor.setDbType(DbType.MYSQL);
​
    interceptor.addInnerInterceptor(pageInterceptor);
    return interceptor;
}

分页组件测试

//测试MybatisPlus分页插件
@Test
public void testMybatisPlus_Page(){
    // 两个参数:current的值默认是1,从1开始,不是0。size是每一页的条数。
    Page<User> page = new Page<>(1, 4);
    userMapper.selectPage(page,null);
    page.getRecords().forEach(System.out::println);
    //page的其他方法
System.out.println("当前页:" + page.getCurrent());
System.out.println("总页数:" + page.getPages());
System.out.println("记录数:" + page.getTotal());
System.out.println("是否有上一页:" + page.hasPrevious());
System.out.println("是否有下一页:" + page.hasNext());
}

删除操作

//测试删除
@Test
public void testDeleteById(){
    userMapper.deleteById(4L);
}//批量删除
@Test
public void testDeleteBatchId(){
    userMapper.deleteBatchIds(Arrays.asList(1L,2L));
}//通过map删除
@Test
public void testdeleteByMap(){
    Map<String, Object> map = new HashMap<>();
    map.put("name","xiaotian");
    userMapper.deleteByMap(map);
}

6.2、逻辑删除

物理删除:从数据库中直接删除

逻辑删除:在数据库中没有被删除,而是通过一个变量来让它失效。 deleted=0 ==》deleted=1
管理员可以查看被删除的记录,防止数据丢失,相当于回收站。

  • 在数据表中增加一个deleted字段
    在这里插入图片描述
  • 同步实体类,在实体类上加上@TableLogic 注解
@TableLogic //逻辑删除
private Integer deleted;

配置application.yml文件

#配置日志
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2 同步实体类在实体类上加上@TableLogic 注解d的操作)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

6.3、执行SQL分析打印

可输出 SQL 语句以及其执⾏时间,建议开发测试时启⽤该功能,能快速揪出慢查询
注意:PerformanceInterceptor在3.2.0被移除了,如果想进⾏性能分析,⽤第三⽅的,官⽅这样写的“该插件 3.2.0 以上版本移除 推荐使⽤第三⽅扩展 执⾏SQL分析打印 功能”。也就是p6spy。

p6spy依赖引入

<dependency>
  <groupId>p6spy</groupId>
  <artifactId>p6spy</artifactId>
  <version>最新版本</version> 
</dependency>

application.yml

spring:
  datasource:
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:h2:mem:test
    ...

注意: driver-class-name 为 p6spy 提供的驱动类 url 前缀为 jdbc:p6spy 跟着冒号为对应数据库连接地址

实际配置为:

spring:
  datasource:
    username: root
    password: 123456
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:mysql:///mybatis_plus?userUnicode=true&characterEncoding=utf-8

spy.properties配置

#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2

测试

//测试MybatisPlus分页插件
@Test
public void testMybatisPlus_Page(){
    // 两个参数:current的值默认是1,从1开始,不是0。size是每一页的条数。
    Page<User> page = new Page<>(2, 4);
    userMapper.selectPage(page,null);
    page.getRecords().forEach(System.out::println);
    //page的其他方法
    System.out.println("当前页:" + page.getCurrent());
    System.out.println("总页数:" + page.getPages());
    System.out.println("记录数:" + page.getTotal());
    System.out.println("是否有上一页:" + page.hasPrevious());
    System.out.println("是否有下一页:" + page.hasNext());
}

在这里插入图片描述
因为在配置文件中设置了慢SQL的检查,为2s,所以这里的查询可以通过

但是只要超过了时长就会抛出异常
在这里插入图片描述

7、条件构造器

Wrapper,可以通过其构造复杂的SQL

wx-小程序

ding-小程序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值