【零基础入门SpringBoot2】—— 数据访问专题

一、SQL

  • 导入数据库开发的相关场景 + 配置数据库依赖
  • SpringBoot对数据访问层的整合,都是 spring-boot-data 开头

1、数据源的自动配置

(1)导入JDBC场景

<!--数据库相关依赖-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

请添加图片描述

  • 我们需要确保数据库版本和驱动版本一致,为什么官方导入JDBC场景不包含驱动呢
    • 因为数据库有很多种,官方不知道我们要使用的是哪种数据库
  • 数据库驱动的默认版本:<mysql.version>8.0.22</mysql.version>
    • 我们如何将驱动指定为我们需要的版本呢?
      • 方法一:在pom.xml中直接引入具体版本的依赖【maven的就近依赖原则】
      • 方法二:通过在pom.xml的properties标签中指定驱动版本 【maven属性的就近优先原则】
        <properties>
          <java.version>1.8</java.version>
          <mysql.version>5.1.49</mysql.version>
        </properties>
        

(2)分析自动配置

  • DataSourceAutoConfiguration: 数据源的自动配置
    • 修改数据源相关的配置:spring.datasource
    • 数据库连接池的配置,是自己容器中没有DataSource才自动配置的
    • 底层配置好的连接池是:HikarDataSource
	@Configuration(proxyBeanMethods = false)
	@Conditional(PooledDataSourceCondition.class)
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
	@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
			DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
			DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
	protected static class PooledDataSourceConfiguration
  • DataSourceTransactionManagerAutoConfiguration:事务管理器的自动配置

  • JdbcTemplateAutoConfiguration:JdbcTemplate的自动配置,可以来对数据库进行增删改查

    • 可以修改这个配置项 @ConfigurationProperties(prefix = “spring.jdbc”) 来修改 JdbcTemplate
    • @Bean@Primary JdbcTemplate, 容器中有这个组件 【我们通过@Autowire注解直接注入使用即可】
  • JndiDataSourceAutoConfiguration: jndi 的自动配置

  • XADataSourceAutoConfiguration: 分布式事务相关的

(3)修改配置项 【就是我们需要对我们操作的数据库进行配置】

  • 我们的application.yml 一般是完成与数据相关的配置
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/smbms
    username: root
    password: 111111
    driver-class-name: com.mysql.jdbc.Driver

(4)我们测试是否可以成功操作数据库

  • 测试方法直接写在主程序测试类中,这个是我们通过模版快速创建的

请添加图片描述

package com.atguigu.admin;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;

@SpringBootTest
@Slf4j
class Boot05WebAdminApplicationTests {
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Test
    void contextLoads() {
        //查询表中的数据
//        jdbcTemplate.queryForObject("select * from smbms_user");
//        jdbcTemplate.queryForList("select * from smbms_user");
        Long aLong = jdbcTemplate.queryForObject("select count(*) from smbms_user", Long.class);
        log.info("记录总数: {}", aLong);
    }

}

请添加图片描述

2、使用Druid数据源

  • 整合阿里提供的第三方数据源
    • 方法一:自定义
    • 方法二:找官方提供的场景(starter)

(1)自定义方式

创建数据源

  • 引入德鲁伊连接池的依赖:

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.17</version>
    </dependency>
    
  • 下面这张图我想说明:如果我们在容器中没有自定义数据源,那么系统会给我们创建HikariDataSource数据源

请添加图片描述

  • 之前我们在Spring自定义数据源时,需要通过bean标签配置,或者采用set方法设置
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
		destroy-method="close">
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
		<property name="maxActive" value="20" />
		<property name="initialSize" value="1" />
		<property name="maxWait" value="60000" />
		<property name="minIdle" value="1" />
		<property name="timeBetweenEvictionRunsMillis" value="60000" />
		<property name="minEvictableIdleTimeMillis" value="300000" />
		<property name="testWhileIdle" value="true" />
		<property name="testOnBorrow" value="false" />
		<property name="testOnReturn" value="false" />
		<property name="poolPreparedStatements" value="true" />
		<property name="maxOpenPreparedStatements" value="20" />
  • 在SpringBoot中,我们只需要通过 @ConfigurationProperties 指向我们的配置文件,就可以实现属性自动绑定

    @Configuration
    public class MyDataSourceConfig {
        @Bean
        @ConfigurationProperties("spring.datasource")
        public DataSource dataSource(){
            DruidDataSource druidDataSource = new DruidDataSource();
            return druidDataSource;
        }
    }
    

StatViewServlet

  • StatViewServlet 的用途包括:

    • 提供监控信息展示的html页面

    • 提供监控信息的JSON API

      	<servlet>
      		<servlet-name>DruidStatView</servlet-name>
      		<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
      	</servlet>
      	<servlet-mapping>
      		<servlet-name>DruidStatView</servlet-name>
      		<url-pattern>/druid/*</url-pattern>
      	</servlet-mapping>
      
  • 配置Druid的监控页功能

    /**
     * 配置druid的监控页功能
     * @return
     */
    @Bean
    public ServletRegistrationBean statViewServlet() {
        StatViewServlet statViewServlet = new StatViewServlet();
        ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");
        
        // 监控页账号密码
        registrationBean.addInitParameter("loginUsername", "admin");
        registrationBean.addInitParameter("loginPassword"," 123456");
        
        return registrationBean;
    }
    

StatFilter

  • 用于统计监控信息:如果SQL监控、URI监控

    需要给数据源中配置如下属性;可以允许多个filter,多个用,分割;如:
    
    <property name="filters" value="stat,slf4j" />
    

请添加图片描述

慢SQL记录配置:

<bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter">
    <property name="slowSqlMillis" value="10000" />
    <property name="logSlowSql" value="true" />
</bean>

使用 slowSqlMillis 定义慢SQL的时长

(2)使用官方 starter 方式

引入 德鲁伊数据源的starter

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid-spring-boot-starter</artifactId>
  <version>1.1.17</version>
</dependency>

我们查看场景中的配置,分析一下SpringBoot为我们完成了哪些自动配置

  • 先定位到自动配置类

请添加图片描述

@Configuration
@ConditionalOnClass({DruidDataSource.class})
@AutoConfigureBefore({DataSourceAutoConfiguration.class})
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
@Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class, DruidFilterConfiguration.class})
public class DruidDataSourceAutoConfigure {
  • ConditionalOnClass 说明容器中有DruidDataSource的组件才会生效
  • 这个配置文件在默认的数据源自动配置类之前生效,为的是让我们自定义的数据源代替默认的数据源
  • EnableConfigurationProperties 的作用是与我们的配置文件关联上,实现属性的绑定
    • DruidStatProperties.class 对应**@ConfigurationProperties(“spring.datasource.druid”)**
    • DataSourceProperties.class 对应**@ConfigurationProperties(prefix = “spring.datasource”)**
  • 又利用 @Import 注解导入四个配置 【把类加入Spring IOC容器】
    • DruidSpringAopConfiguration.class

      • 通过spring.datasource.druid.aop-patterns进行配置
      • 作用是监控Spring的组件的
    • DruidStatViewServletConfiguration.class

      • 通过spring.datasource.druid.stat-view-servlet进行配置
      • 作用是完成监控页的配置
    • DruidWebStatFilterConfiguration.class

      • 通过spring.datasource.druid.web-stat-filter进行配置
      • 作用是web监控配置
    • DruidFilterConfiguration.class

      • Druid 自己的filter配置
          private static final String FILTER_STAT_PREFIX = "spring.datasource.druid.filter.stat";
          private static final String FILTER_CONFIG_PREFIX = "spring.datasource.druid.filter.config";
          private static final String FILTER_ENCODING_PREFIX = "spring.datasource.druid.filter.encoding";
          private static final String FILTER_SLF4J_PREFIX = "spring.datasource.druid.filter.slf4j";
          private static final String FILTER_LOG4J_PREFIX = "spring.datasource.druid.filter.log4j";
          private static final String FILTER_LOG4J2_PREFIX = "spring.datasource.druid.filter.log4j2";
          private static final String FILTER_COMMONS_LOG_PREFIX = "spring.datasource.druid.filter.commons-log";
          private static final String FILTER_WALL_PREFIX = "spring.datasource.druid.filter.wall";
          private static final String FILTER_WALL_CONFIG_PREFIX = "spring.datasource.druid.filter.wall.config";
      

接下来我们要演示具体是如何进行配置以及使用的

  • 下面这个文件是我在项目的 application.yml 中进行配置的

    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/smbms
        username: root
        password: 111111
        driver-class-name: com.mysql.jdbc.Driver
    
        druid:
          filters: stat,wall # 指定开启哪些功能[此处分别为 sql监控、防火墙]
    
          stat-view-servlet: # 监控页功能
            enabled: true
            login-password: admin # 监控登录密码
            login-username: admin # 监控登录账号
            reset-enable: false
    
          web-stat-filter: # web监控和uri监控
            enabled: true
            exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*' # 不指定默认就是这个
            url-pattern: /*
          aop-patterns: com.atguigu.admin.* # 监控这个包下的所有组件
    
          filter: # 对于上面拦截器/过滤器的详细配置
            stat:
              slow-sql-millis: 1000 # 指定超过1s的查询都是慢查询
              log-slow-sql: true # 是否开启慢查询日志记录功能
              enabled: true
    
            wall:
              enabled: true
              config:
                delete-allow: false # 禁止删表操作
      jdbc:
        template:
          query-timeout: 3
    
  • 如果我们想来到druid的监控页

    • 在浏览器的地址栏输入: localhost:8080/druid/login.html
    • 然后输入我们配置好的德鲁伊连接池的账号和密码

请添加图片描述

3、整合MyBatis操作

  • JdbcTemplateSpring内置的简单版数据库操作工具,在平时工作的时候我们会使用第三方提供的数据库操作工具,例如:MyBatis
  • 使用MyBatis之前,先引入对应的starter的依赖
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>

(1)配置模式

  • SpringBoot 底层为我们做了什么?

    • SqlSessionFactory 自动配置好了
    • SqlSession 中自动配置了 SqlSessionTemplate 并组合了SqlSession
    • 通过 @Import(AutoConfiguredMapperScannerRegistrar.class) 导入对应组件
    • 我们编写的Mapper接口,通过**@Mapper**扫描进来
  • 通过 @EnableConfigurationProperties注解实现与配置文件的绑定

    @EnableConfigurationProperties(MybatisProperties.class)MyBatis配置项绑定类。
    @AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
    public class MybatisAutoConfiguration{}
    
    @ConfigurationProperties(prefix = "mybatis")
    public class MybatisProperties
    
  • 先看下一目录结构,了解都创建了哪些文件

    • Account 代表封装数据库字段的POJO
    • 我们把请求的处理写在了 IndexController 控制器中
    • mapper.AccountMapper 接口设置了操作数据库的方法,此处为 getAcct
    • service.AccountService 类调用了接口对象,创建了一个业务方法调用接口方法实现功能
    • resources目录下的mybatis目录专门存放于mybatis相关的
      • mybatis-config.xml 是mybatis的核心配置文件,后期可以省略直接在springboot的yaml文件中进行配置
      • AccountMapper.xml 来完成具体的映射

请添加图片描述

  • 接下来描述一下整体通过配置MyBatis操作数据库的流程:
    • 导入mybatis官方starter
    • 编写mapper接口并使用@Mapper注解
    • 编写sql映射文件并绑定到mapper接口上
    • 在application.yaml 中指定Mapper核心配置文件位置、sql映射文件位置
  • yaml中的配置 【对于采取配置文件和configuration配置只能选取一种】
# 配置mybatis的规则
mybatis:
#  config-location: classpath:mybatis/mybatis-config.xml # 指定mybatis全局文件的位置
  mapper-locations: classpath:mybatis/mapper/*.xml # 指定mybatis的sql映射文件位置
  configuration:
    map-underscore-to-camel-case: true # 通过配置文件的方式开启驼峰映射

接下来附上我们的各个文件

//AccountMapper 接口
package com.atguigu.admin.mapper;

import com.atguigu.admin.bean.Account;
import org.apache.ibatis.annotations.Mapper;

/**
 * @author Bonbons
 * @version 1.0
 */
@Mapper
public interface AccountMapper {
    public Account getAcct(Long id);
}
//AccountService类
package com.atguigu.admin.service;

import com.atguigu.admin.bean.Account;
import com.atguigu.admin.mapper.AccountMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author Bonbons
 * @version 1.0
 */
@Service
public class AccountService {
    //注入操作数据库的对象
    @Autowired
    AccountMapper accountMapper;

    public Account getAcctById(Long id){
        return accountMapper.getAcct(id);
    }
}
<!--AccountMapper.xml 映射文件-->
<?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.atguigu.admin.mapper.AccountMapper">
    <!--根据id查询用户信息-->
    <select id="getAcct" resultType="com.atguigu.admin.bean.Account">
        select * from account_tbl where id = #{id}
    </select>
</mapper>
<!--此处的目的是开启驼峰映射,以后大多就不设置mybatis的核心配置文件了-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
</configuration>

请添加图片描述

  • 一些补充知识点:
    • 当我们的@Bean注解用于方法上,参数类型如果在容器中有对应组件,那么就会从容器中取

请添加图片描述
请添加图片描述

(2)注解模式

  • 不需要编写mybatis的核心配置文件与sql映射文件
  • 整体流程
    • 第一步,仍然需要导入 mybatis 的 starter
    • 第二步,编写我们的POJO类,此处为 City
    • 第三步,编写我们的mapper接口,此处和我们以往不同,直接通过注解来指明对应的SQL语句
    • 第三步,编写我们的 CityServer(业务层),在业务里调用mapper层的方法
    • 第四步,在我们的控制器(此处为 IndexController)中编写方法,处理我们的请求
  • 接下来附上我们的文件代码
// City类
package com.atguigu.admin.bean;

import lombok.Data;

/**
 * @author Bonbons
 * @version 1.0
 */
@Data
public class City {
    private Long id;
    private String name;
    private String state;
    private String country;
}
// CityMapper接口
package com.atguigu.admin.mapper;

import com.atguigu.admin.bean.City;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

/**
 * @author Bonbons
 * @version 1.0
 */
@Mapper
public interface CityMapper {
    //不去编写sql映射文件了,而是直接在我们接口方法的上方通过注解来指定
    @Select("select * from city where id = #{id}")
    public City getById(Long id);
}
// CityService类
package com.atguigu.admin.service;

import com.atguigu.admin.bean.City;
import com.atguigu.admin.mapper.CityMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author Bonbons
 * @version 1.0
 */
@Service
public class CityService {
    //注入我们的mapper
    @Autowired
    CityMapper cityMapper;

    public City getById(Long id){
        return cityMapper.getById(id);
    }
}
//控制器方法
//纯注解的方式
@Autowired
CityService cityService;
@GetMapping("/city")
@ResponseBody
public City getCityById(@RequestParam("id") Long id){
  return cityService.getById(id);
}

请添加图片描述

(3)混合模式

  • 什么情况下会混合 配置文件 + 注解的形式呢?

    • 就是当我们的SQL很复杂的时候
    • 就是不使用注解代替sql映射文件【对于我们采取配置文件的部分】
  • 可以参考下面的这几部分代码:

// CityMapper接口
package com.atguigu.admin.mapper;

import com.atguigu.admin.bean.City;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

/**
 * @author Bonbons
 * @version 1.0
 */
@Mapper
public interface CityMapper {
    //不去编写sql映射文件了,而是直接在我们接口方法的上方通过注解来指定
    @Select("select * from city where id = #{id}")
    public City getById(Long id);

    //这个不采用注解的方式,与配置文件结合使用完成插入功能
    public void insert(City city);
}

// CityService类
package com.atguigu.admin.service;

import com.atguigu.admin.bean.City;
import com.atguigu.admin.mapper.CityMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author Bonbons
 * @version 1.0
 */
@Service
public class CityService {
    //注入我们的mapper
    @Autowired
    CityMapper cityMapper;

    public City getById(Long id){
        return cityMapper.getById(id);
    }

    public void saveCity(City city){
        cityMapper.insert(city);
    }
}

<!--CityMapper.xml-->
<?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.atguigu.admin.mapper.AccountMapper">
    <!--插入City数据-->
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        insert into city(`name`, `state`, `country`) values(#{name}, #{state}, #{country})
    </insert>
</mapper>
//IndexController控制器
//纯注解的方式 >> 查询
    @Autowired
    CityService cityService;
    @GetMapping("/city")
    @ResponseBody
    public City getCityById(@RequestParam("id") Long id){
        return cityService.getById(id);
    }

    //注解+配置文件 >> 添加
    @ResponseBody
    @PostMapping("/city")
    public City saveCity(City city){
        cityService.saveCity(city);
        return city;
    }
//普通Java对象类
package com.atguigu.admin.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author Bonbons
 * @version 1.0
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class City {
    private Long id;
    private String name;
    private String state;
    private String country;
}

我们也可以将第二种配置方式转为注解方式【内含配置项的注解用法】

@Insert("insert into city(`name`, `state`, `country`) values(#{name}, #{state}, #{country})")
@Options(useGeneratedKeys = true, keyProperty = "id")
public void insert(City city);
  • 补充知识点:
    • useGeneratedKeys、keyProperty 代表设置自增键
    • 用@Options注解指定选项
    • 主程序类使用@MapperScan(“com.atguigu.admin.mapper”) 注解扫描所有mapper,代替每个接口上使用@mapper注解
  • 总结注解和配置方式使用Mybatis的最佳实战:
    • 引入 mybatis-starter 的场景
    • 在SpringBoot的核心配置文件 application.yaml 中,指定 mapper-location 的位置 【sql映射文件】
    • 编写Mapper接口并标注@Mapper注解 【尽管上面可以通过包扫描代替标记@Mapper,但还是推荐使用@Mapper注解】
    • 对于接口中的写法有两种方式
      • 简单写法就是直接通过注解代替sql映射文件
      • 复杂写法就是编写sql文件,然后与我们的mapper接口方法关联上 【大多用于比较复杂的SQL】


4、整合MyBatis-Plus 完成CRUD

(1)什么是 MyBatis-Plus

  • 是MyBatis的增强工具,可以简化开发、提升效率
  • IDEA中有相关的插件:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装
  • 具体的开发指南可以参考我们的 MyBatisPlus 官网

(2)整合MyBatis-Plus

整体流程说明:

  • 准备数据库表【我使用的是mysql的navicat】

    • 创建表的脚本
    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)
    );
    
    • 创建几组对应数据的脚本
    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');
    
  • 引入mybatisPlus的starter

<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.4.1</version>
</dependency>
  • 编写Mapper接口
package com.atguigu.admin.mapper;

import com.atguigu.admin.bean.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

/**
 * @author Bonbons
 * @version 1.0
 * 让我们的接口直接继承MyBatisPlus提供的BaseMapper
 * 泛型为我们要操作的数据类型
 */
public interface UserMapper extends BaseMapper<User> {
}
  • 编写我们数据库表对应的POJO类
    • 此处为什么不直接删掉userName、password属性呢?
      • 因为在登录验证的时候要使用
package com.atguigu.admin.bean;

import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author Bonbons
 * @version 1.0
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    /**
     * 正常所有的属性都应该与数据库表中的字段对应上
     * 但是上面的userName和 password 我们暂时用不到,所以就通过注解暂时让他俩不在表中
     */
    @TableField(exist = false)
    private String userName;
    @TableField(exist = false)
    private String password;

    //MyBatisPlus整合用的User对象为下面四个字段
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
  • 编写测试方法【这个方法是写在我们的 主程序测试类中的】

请添加图片描述

@Autowired
UserMapper userMapper;
@Test
void testUserMapper(){
  User user = userMapper.selectById(1L);
  log.info("用户信息: {}", user);
}

请添加图片描述

  • 其实还应该配置数据源,我们在之前已经配置了 Druid 数据源此处使用的就是【直接贴上我的 application.yml】
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/smbms
    username: root
    password: 111111
    driver-class-name: com.mysql.jdbc.Driver

    druid:
      filters: stat,wall # 指定开启哪些功能[此处分别为 sql监控、防火墙]

      stat-view-servlet: # 监控页功能
        enabled: true
        login-password: admin # 监控登录密码
        login-username: admin # 监控登录账号
        reset-enable: false

      web-stat-filter: # web监控和uri监控
        enabled: true
        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
        url-pattern: /*
      aop-patterns: com.atguigu.admin.* # 监控这个包下的所有组件

      filter: # 对于上面拦截器/过滤器的详细配置
        stat:
          slow-sql-millis: 1000 # 指定超过1s的查询都是慢查询
          log-slow-sql: true # 是否开启慢查询日志记录功能
          enabled: true

        wall:
          enabled: true
          config:
            delete-allow: false # 禁止删表操作
  jdbc:
    template:
      query-timeout: 3

# 配置mybatis的规则
mybatis:
#  config-location: classpath:mybatis/mybatis-config.xml # 指定mybatis全局文件的位置
  mapper-locations: classpath:mybatis/mapper/*.xml # 指定mybatis的sql映射文件位置
  configuration:
    map-underscore-to-camel-case: true # 通过配置文件的方式开启驼峰映射

接下来我们对 mybatis-plus 场景的自动配置进行分析

  • 在starter中,自动配置是从 spring.factories 里面获取EnableAutoConfiguration 以启动要加载的类
    • 项目一启动 MybatisPlusAutoConfiguration 的自动配置就会生效
      • MybatisPlusProperties 配置项绑定【mybatis-plus:xxx 就是对mybatis-plus的定制】
      • SqlSessionFactory 自动配置好,在底层使用的是容器中默认的数据源
      • mapperLocations 自动配置好的。有默认值为 classpath*:/mapper/**/*.xml;【任意包的类路径下的所有mapper文件夹下任意路径下的所有xml都是sql映射文件】
      • 容器中也自动配置好了 SqlSessionTemplate
      • @Mapper 标注的接口也会被自动扫描;建议直接 @MapperScan(“com.atguigu.admin.mapper”) 批量扫描就行

补充的其他知识点:

  • @Bean标记一个方法,如果参数是一个对象,这个参数就会从容器中自动确定这个组件

  • Ctrl + F12 查看类中有哪些方法 【IDEA快捷键】

  • 只需要我们的Mapper继承 BaseMapper 就可以拥有crud能力 【当然我们如果有特殊的需求,也可以使用sql映射文件自定义】

    • 以下是BaseMapper中为我们提供的一些基本方法

    请添加图片描述

(3)CRUD功能

  • 零散知识点:

    • 对于我们定义的Mapper接口,继承BaseMapper之后,它如何知道我们关联的是数据库中的那张表呢?
      • 默认将泛型参数首字母小写为数据库中对应的表,比如此处为 BaseMapper<User> 默认对应数据库中的 user 表
      • 当然,我们也可以指定POJO类对应的表名,在我们的POJO类上添加注解@TableName(“实际表名”)即可【此处为User】
    • 遍历状态: stat.count 可以充当编号 直接用id也行
  • UserService

    /**
     * 继承顶级 Service
     */
    public interface UserService extends IService<User> {
    
    }
    
  • UserServiceImpl

    package com.atguigu.admin.service.impl;
    
    import com.atguigu.admin.bean.User;
    import com.atguigu.admin.mapper.UserMapper;
    import com.atguigu.admin.service.UserService;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import org.springframework.stereotype.Service;
    
    /**
     * @author Bonbons
     * @version 1.0
     */
    @Service
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    }
    
  • Controller

    @GetMapping("/dynamic_table")
    public String dynamic_table(Model model) {
        // 从数据库中查出user表中的用户进行展示
        List<User> list = userService.list();
        model.addAttribute("users", list);
        return "table/dynamic_table";
    }
    
  • 修改前端页面

    <tr>
        <th>#</th>
        <th>id</th>
        <th>name</th>
        <th>ages</th>
        <th>email</th>
        <th>操作</th>
    </tr>
    </thead>
    <tbody>
    <tr class="gradeX" th:each="user,stat:${users}">
        <td th:text="${stat.count}">Trident</td>
        <td th:text="${user.id}">id</td>
        <td th:text="${user.name}">Internet
            Explorer 4.0
        </td>
        <td th:text="${user.age}"></td>
        <td class="center hidden-phone">[[${user.email}]]</td>
        <td class="center hidden-phone">X</td>
    </tr>
    
  • 效果演示

请添加图片描述

演示分页功能的使用:

  • 编写一个分页插件的配置类
@Configuration
public class MyBatisConfig {

    /**
     * MybatisPlusInterceptor
     * @return
     */
    @Bean
    public MybatisPlusInterceptor paginationInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        // MyBatis-plus3.4版本以后的分页拦截器
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        // paginationInterceptor.setOverflow(false);
        paginationInnerInterceptor.setOverflow(true);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        // paginationInterceptor.setLimit(500);
        paginationInnerInterceptor.setMaxLimit(500L);
        mybatisPlusInterceptor.addInnerInterceptor(paginationInnerInterceptor);

        return mybatisPlusInterceptor;
    }
}
  • Controller中增加分页功能

    @GetMapping("/dynamic_table")
    public String dynamic_table(@RequestParam(value = "pn", defaultValue = "1") Integer pn, Model model) {
        // 从数据库中查出user表中的用户进行展示
        List<User> list = userService.list();
        //model.addAttribute("users", list);
        // 分页查询数据
        Page<User> userPage = new Page<>(pn, 2);
        // 分页查询的结果
        Page<User> page = userService.page(userPage, null);
        model.addAttribute("page", page);
        return "table/dynamic_table";
    }
    
  • 修改前端页面,演示分页效果

    <div class="row-fluid">
        <div class="span6">
            <div class="dataTables_info" id="dynamic-table_info">
                当前第 [[${page.current}]] 页
                总计 [[${page.pages}]] 页
                共 [[${page.total}]] 条记录
            </div>
        </div>
        <div class="span6">
            <div class="dataTables_paginate paging_bootstrap pagination">
                <ul>
                    <li class="prev disabled"><a href="#">← 前一页</a></li>
                    <li th:class="${num == page.current?'active':''}"
                        th:each="num:${#numbers.sequence(1,page.pages)}">
                        <a th:href="@{/dynamic_table(pn=${num})}">[[${num}]]</a>
                    </li>
                    <li class="next disabled"><a href="#">下一页 → </a></li>
                </ul>
            </div>
        </div>
    </div>
    

请添加图片描述
请添加图片描述

实现删除功能

  • 增加删除的按钮:

    <td>
        <a
           th:href="@{/user/delete/{id}(id=${user.id},pn=${page.current})}"
           class="btn btn-danger btn-sm"
           type="button">
            删除
        </a>
    </td>
    
  • 设置控制器方法:

    @GetMapping("/user/delete/{id}")
    public String deleteUser(@PathVariable("id") Long id,
                             @RequestParam(value = "pn", defaultValue = "1") Integer pn,
                             RedirectAttributes ra) {
        userService.removeById(id);
        ra.addAttribute("pn", pn);
        return "redirect:/dynamic_table";
    }
    

请添加图片描述


二、NoSQL

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

1、Redis自动配置

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

请添加图片描述

  • 分析一下Redis-start在底层为我们完成了哪些配置
    • 自动配置类为 RedisAutoConfiguration,指明了RedisProperties属性类(属性绑定)
      • 通过 spring.redis.xxx 完成对 redis 的配置 【在application.xml 中食用】
    • LettuceConnectionConfiguration、JedisConnectionConfiguration连接工厂也是准备好的
    • 为我们自动注入了两个操作Redis的模版:
      • RedisTemplate<Object, Object>StringRedisTemplate 【区别就是kv的类型不同,后者专门用于String】
      • 底层只要我们通过@Autowire注入了 StringRedisTemplate或RedisTemplate就可以操作Redis
  • 为了掩饰操作Redis,推荐去阿里云申请一个按时计费的Redis
    • 阿里云按量付费redis。经典网络
    • 申请redis的公网连接地址
    • 修改白名单 允许0.0.0.0/0 访问

2、RedisTemplate 与 Lettuce

  • 在我们SpringBoot的核心配置文件: application.properties 中对redis进行配置:
spring:
  redis:
    host: r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.com
    port: 6379
    password: lfy:Lfy123456
  • 然后编写测试方法,看是否能连接上我们的阿里云Redis
@Autowired
StringRedisTemplate redisTemplate;

@Test
void testRedis() {
    ValueOperations<String, String> operations = redisTemplate.opsForValue();

    operations.set("hello", "world");

    String hello = operations.get("hello");
    System.out.println(hello);
}
  • 上面采用的是系统默认的 lettuce 客户端,用于连接 RedisServer 的
    • 我们还可以通过jedis客户端连接我们的Redis
    • 那么我们该如何切换客户端呢?

3、切换到jedis

  • 导入Jedis的依赖
<dependency>
   <groupId>redis.clients</groupId>
   <artifactId>jedis</artifactId>
</dependency>
  • 通过核心配置文件,将redis的客户端类型设置为 jedis
spring:
  redis:
    host: r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.com
    port: 6379
    password: lfy:Lfy123456
    client-type: jedis
  • 通过测试方法,输出一下当前的客户端类型
@Autowired
RedisConnectionFactory redisConnectionFactory;

@Test
void testRedis(){
    // org.springframework.data.redis.connection.jedis.JedisConnectionFactory
    System.out.println(redisConnectionFactory.getClass());
}

4、利用Redis实现URL访问次数统计功能

  • 创建一个URL统计拦截器 【在控制器方法执行前执行】
@Component
public class RedisUrlCountInterceptor implements HandlerInterceptor {

    @Autowired
    StringRedisTemplate redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String uri = request.getRequestURI();

        // 默认每次访问当前uri就会计数+1
        redisTemplate.opsForValue().increment(uri);

        return true;
    }
}
  • 在配置类中将我们定义的拦截器注册到IOC容器中
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {

    @Autowired
    RedisUrlCountInterceptor redisUrlCountInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(redisUrlCountInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**",
                        "/js/**","/aa/**");
    }
}

请添加图片描述

  • 在我们的控制器方法中,利用 StringRedisTemplate 统计数据
@Slf4j
@Controller
public class IndexController {

	@Autowired
    StringRedisTemplate redisTemplate;
    
	@GetMapping("/main.html")
    public String mainPage(HttpSession session, Model model) {

        log.info("当前方法是:{}", "mainPage");

        ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();

        String s = opsForValue.get("/main.html"); // 获取main.html页面访问的次数
        String s1 = opsForValue.get("/sql");      // 获取sql请求访问的次数

        model.addAttribute("mainCount", s);
        model.addAttribute("sqlCount", s1);

        return "main";
    }
}
  • 修改对应页面的前端表单
<div class="col-md-6 col-xs-12 col-sm-6">
    <div class="panel purple">
        <div class="symbol">
            <i class="fa fa-gavel"></i>
        </div>
        <div class="state-value">
            <div class="value" th:text="${mainCount}">230</div>
            <div class="title">/main.html</div>
        </div>
    </div>
</div>
<div class="col-md-6 col-xs-12 col-sm-6">
    <div class="panel red">
        <div class="symbol">
            <i class="fa fa-tags"></i>
        </div>
        <div class="state-value">
            <div class="value" th:text="${sqlCount}">3490</div>
            <div class="title">/sql</div>
        </div>
    </div>
</div>

请添加图片描述

  • 补充知识点:Filter 和 Interceptor 几乎有相同的功能,那么有什么区别呢?
    • Filter 是Servlet的原生组件,脱离Spring应用也能使用
    • Interceptor 是Spring定义的接口,可以使用Spring的自动装配功能 [@Autowired]
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bow.贾斯汀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值