重走springboot(二)

pigpigpi發表於2020-11-10

注:本文內容來源於微信公眾號:狂神說

一.整合jdbc

1、我去新建一個專案測試:springboot-data-jdbc ; 引入相應的模組!基礎模組

2、專案建好之後,發現自動幫我們匯入瞭如下的啟動器:

pom.xml

<?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.3.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.kuang</groupId>
    <artifactId>springboot-jdbc</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-jdbc</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3、編寫yaml配置檔案連線資料庫;

spring:
  datasource:
    username: root
    password: 123456
    #?serverTimezone=UTC解決時區的報錯
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver

4、配置完這一些東西后,我們就可以直接去使用了,因為SpringBoot已經預設幫我們進行了自動配置;去測試類測試一下

@SpringBootTest
class SpringbootDataJdbcApplicationTests {
 
 
    //DI注入資料來源
    @Autowired
    DataSource dataSource;
 
 
    @Test
    public void contextLoads() throws SQLException {
        //看一下預設資料來源
        System.out.println(dataSource.getClass());
        //獲得連線
        Connection connection =   dataSource.getConnection();
        System.out.println(connection);
        //關閉連線
        connection.close();
    }
}

結果:我們可以看到他預設給我們配置的資料來源為 : class com.zaxxer.hikari.HikariDataSource , 我們並沒有手動配置

class com.zaxxer.hikari.HikariDataSource

HikariProxyConnection@1797112335 wrapping com.mysql.cj.jdbc.ConnectionImpl@4613311f

我們來全域性搜尋一下,找到資料來源的所有自動配置都在 :DataSourceAutoConfiguration檔案:

@Import(
    {Hikari.class, Tomcat.class, Dbcp2.class, Generic.class, DataSourceJmxConfiguration.class}
)
protected static class PooledDataSourceConfiguration {
    protected PooledDataSourceConfiguration() {
    }
}

這裡匯入的類都在 DataSourceConfiguration 配置類下,可以看出 Spring Boot 2.2.5 預設使用HikariDataSource 資料來源,而以前版本,如 Spring Boot 1.5 預設使用 org.apache.tomcat.jdbc.pool.DataSource 作為資料來源;

==HikariDataSource 號稱 Java WEB 當前速度最快的資料來源,相比於傳統的 C3P0 、DBCP、Tomcat jdbc 等連線池更加優秀;

可以使用 spring.datasource.type 指定自定義的資料來源型別,值為 要使用的連線池實現的完全限定名。==

關於資料來源我們並不做介紹,有了資料庫連線,顯然就可以 CRUD 運算元據庫了。但是我們需要先了解一個物件 JdbcTemplate

JDBCTemplate

1、有了資料來源(com.zaxxer.hikari.HikariDataSource),然後可以拿到資料庫連線(java.sql.Connection),有了連線,就可以使用原生的 JDBC 語句來運算元據庫;

2、即使不使用第三方第資料庫操作框架,如 MyBatis等,Spring 本身也對原生的JDBC 做了輕量級的封裝,即JdbcTemplate。

3、資料庫操作的所有 CRUD 方法都在 JdbcTemplate 中。

4、Spring Boot 不僅提供了預設的資料來源,同時預設已經配置好了 JdbcTemplate 放在了容器中,程式設計師只需自己注入即可使用

5、JdbcTemplate 的自動配置是依賴 org.springframework.boot.autoconfigure.jdbc 包下的 JdbcTemplateConfiguration 類

JdbcTemplate主要提供以下幾類方法:

  • execute方法:可以用於執行任何SQL語句,一般用於執行DDL語句;
  • update方法及batchUpdate方法:update方法用於執行新增、修改、刪除等語句;batchUpdate方法用於執行批處理相關語句;
  • query方法及queryForXXX方法:用於執行查詢相關語句;
  • call方法:用於執行儲存過程、函式相關語句。

測試

編寫一個Controller,注入 jdbcTemplate,編寫測試方法進行訪問測試;

package com.kuang.controller;
 
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
 
import java.util.Date;
import java.util.List;
import java.util.Map;
 
 
@RestController
@RequestMapping("/jdbc")
public class JdbcController {
 
 
    /**
     * Spring Boot 預設提供了資料來源,預設提供了 org.springframework.jdbc.core.JdbcTemplate
     * JdbcTemplate 中會自己注入資料來源,用於簡化 JDBC操作
     * 還能避免一些常見的錯誤,使用起來也不用再自己來關閉資料庫連線
     */
    @Autowired
    JdbcTemplate jdbcTemplate;
 
 
    //查詢employee表中所有資料
    //List 中的1個 Map 對應資料庫的 1行資料
    //Map 中的 key 對應資料庫的欄位名,value 對應資料庫的欄位值
    @GetMapping("/list")
    public List<Map<String, Object>> userList(){
        String sql = "select * from employee";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        return maps;
    }
    
    //新增一個使用者
    @GetMapping("/add")
    public String addUser(){
        //插入語句,注意時間問題
        String sql = "insert into employee(last_name, email,gender,department,birth)" +
                " values ('狂神說','24736743@qq.com',1,101,'"+ new Date().toLocaleString() +"')";
        jdbcTemplate.update(sql);
        //查詢
        return "addOk";
    }
 
 
    //修改使用者資訊
    @GetMapping("/update/{id}")
    public String updateUser(@PathVariable("id") int id){
        //插入語句
        String sql = "update employee set last_name=?,email=? where id="+id;
        //資料
        Object[] objects = new Object[2];
        objects[0] = "秦疆";
        objects[1] = "24736743@sina.com";
        jdbcTemplate.update(sql,objects);
        //查詢
        return "updateOk";
    }
 
 
    //刪除使用者
    @GetMapping("/delete/{id}")
    public String delUser(@PathVariable("id") int id){
        //插入語句
        String sql = "delete from employee where id=?";
        jdbcTemplate.update(sql,id);
        //查詢
        return "deleteOk";
    }
    
}

測試請求,結果正常;

到此,CURD的基本操作,使用 JDBC 就搞定了。

二.整合Druid

Druid簡介

Java程式很大一部分要運算元據庫,為了提高效能運算元據庫的時候,又不得不使用資料庫連線池。

Druid 是阿里巴巴開源平臺上一個資料庫連線池實現,結合了 C3P0、DBCP 等 DB 池的優點,同時加入了日誌監控。

Druid 可以很好的監控 DB 池連線和 SQL 的執行情況,天生就是針對監控而生的 DB 連線池。

Druid已經在阿里巴巴部署了超過600個應用,經過一年多生產環境大規模部署的嚴苛考驗。

Spring Boot 2.0 以上預設使用 Hikari 資料來源,可以說 Hikari 與 Driud 都是當前 Java Web 上最優秀的資料來源,我們來重點介紹 Spring Boot 如何整合 Druid 資料來源,如何實現資料庫監控。

Github地址:https://github.com/alibaba/druid/

配置資料來源

1、新增上 Druid 資料來源依賴。

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.21</version>
</dependency>

2、切換資料來源;之前已經說過 Spring Boot 2.0 以上預設使用 com.zaxxer.hikari.HikariDataSource 資料來源,但可以 通過 spring.datasource.type 指定資料來源。

spring:
  datasource:
    username: root
    password: 123456
    #?serverTimezone=UTC解決時區的報錯
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
 
    #Spring Boot 預設是不注入這些屬性值的,需要自己繫結
    #druid 資料來源專有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
 
    #配置監控統計攔截的filters,stat:監控統計、log4j:日誌記錄、wall:防禦sql注入
    #如果允許時報錯  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #則匯入 log4j 依賴即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

3、資料來源切換之後,在測試類中注入 DataSource,然後獲取到它,輸出一看便知是否成功切換;

4、切換成功!既然切換成功,就可以設定資料來源連線初始化大小、最大連線數、等待時間、最小連線數 等設定項;可以檢視原始碼

spring:
  datasource:
    username: root
    password: 123456
    #?serverTimezone=UTC解決時區的報錯
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
 
    #Spring Boot 預設是不注入這些屬性值的,需要自己繫結
    #druid 資料來源專有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
 
    #配置監控統計攔截的filters,stat:監控統計、log4j:日誌記錄、wall:防禦sql注入
    #如果允許時報錯  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #則匯入 log4j 依賴即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

5、匯入Log4j 的依賴

<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

6、現在需要程式設計師自己為 DruidDataSource 繫結全域性配置檔案中的引數,再新增到容器中,而不再使用 Spring Boot 的自動生成了;我們需要 自己新增 DruidDataSource 元件到容器中,並繫結屬性;

package com.kuang.config;
 
 
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
 
import javax.sql.DataSource;
 
 
@Configuration
public class DruidConfig {
 
 
    /*
       將自定義的 Druid資料來源新增到容器中,不再讓 Spring Boot 自動建立
       繫結全域性配置檔案中的 druid 資料來源屬性到 com.alibaba.druid.pool.DruidDataSource從而讓它們生效
       @ConfigurationProperties(prefix = "spring.datasource"):作用就是將 全域性配置檔案中
       字首為 spring.datasource的屬性值注入到 com.alibaba.druid.pool.DruidDataSource 的同名引數中
     */
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }
 
 
}

7、去測試類中測試一下;看是否成功!

@SpringBootTest
class SpringbootDataJdbcApplicationTests {
 
 
    //DI注入資料來源
    @Autowired
    DataSource dataSource;
 
 
    @Test
    public void contextLoads() throws SQLException {
        //看一下預設資料來源
        System.out.println(dataSource.getClass());
        //獲得連線
        Connection connection =   dataSource.getConnection();
        System.out.println(connection);
 
 
        DruidDataSource druidDataSource = (DruidDataSource) dataSource;
        System.out.println("druidDataSource 資料來源最大連線數:" + druidDataSource.getMaxActive());
        System.out.println("druidDataSource 資料來源初始化連線數:" + druidDataSource.getInitialSize());
 
 
        //關閉連線
        connection.close();
    }
}

配置Druid資料來源監控

Druid 資料來源具有監控的功能,並提供了一個 web 介面方便使用者檢視,類似安裝 路由器 時,人家也提供了一個預設的 web 頁面。

所以第一步需要設定 Druid 的後臺管理頁面,比如 登入賬號、密碼 等;配置後臺管理;

//配置 Druid 監控管理後臺的Servlet;
//內建 Servlet 容器時沒有web.xml檔案,所以使用 Spring Boot 的註冊 Servlet 方式
@Bean
public ServletRegistrationBean statViewServlet() {
    ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
 
 
    // 這些引數可以在 com.alibaba.druid.support.http.StatViewServlet 
    // 的父類 com.alibaba.druid.support.http.ResourceServlet 中找到
    Map<String, String> initParams = new HashMap<>();
    initParams.put("loginUsername", "admin"); //後臺管理介面的登入賬號
    initParams.put("loginPassword", "123456"); //後臺管理介面的登入密碼
 
 
    //後臺允許誰可以訪問
    //initParams.put("allow", "localhost"):表示只有本機可以訪問
    //initParams.put("allow", ""):為空或者為null時,表示允許所有訪問
    initParams.put("allow", "");
    //deny:Druid 後臺拒絕誰訪問
    //initParams.put("kuangshen", "192.168.1.20");表示禁止此ip訪問
 
 
    //設定初始化引數
    bean.setInitParameters(initParams);
    return bean;
}

配置完畢後,我們可以選擇訪問 :http://localhost:8080/druid/login.html

訪問成功即可!

配置 Druid web 監控 filter 過濾器

//配置 Druid 監控 之  web 監控的 filter
//WebStatFilter:用於配置Web和Druid資料來源之間的管理關聯監控統計
@Bean
public FilterRegistrationBean webStatFilter() {
    FilterRegistrationBean bean = new FilterRegistrationBean();
    bean.setFilter(new WebStatFilter());
 
 
    //exclusions:設定哪些請求進行過濾排除掉,從而不進行統計
    Map<String, String> initParams = new HashMap<>();
    initParams.put("exclusions", "*.js,*.css,/druid/*,/jdbc/*");
    bean.setInitParameters(initParams);
 
 
    //"/*" 表示過濾所有請求
    bean.setUrlPatterns(Arrays.asList("/*"));
    return bean;
}

平時在工作中,按需求進行配置即可,主要用作監控!

三.整合mybatis

1、匯入 MyBatis 所需要的依賴

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
</dependency>

2、配置資料庫連線資訊(不變)

spring:
  datasource:
    username: root
    password: 123456
    #?serverTimezone=UTC解決時區的報錯
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    #Spring Boot 預設是不注入這些屬性值的,需要自己繫結
    #druid 資料來源專有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #配置監控統計攔截的filters,stat:監控統計、log4j:日誌記錄、wall:防禦sql注入
    #如果允許時報錯  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #則匯入 log4j 依賴即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

3、測試資料庫是否連線成功!

4、建立實體類,匯入 Lombok!

Department.java

package com.kuang.pojo;
 
 
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
 
 
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Department {
 
 
    private Integer id;
    private String departmentName;
 
 
}

5、建立mapper目錄以及對應的 Mapper 介面

DepartmentMapper.java

package com.kuang.mapper;

import com.kuang.pojo.Department;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

//@Mapper : 表示本類是一個 MyBatis 的 Mapper
@Mapper
@Repository
public interface DepartmentMapper {


    // 獲取所有部門資訊
    List<Department> getDepartments();


    // 通過id獲得部門
    Department getDepartment(Integer id);


}

6、對應的Mapper對映檔案

DepartmentMapper.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.kuang.mapper.DepartmentMapper">


    <select id="getDepartments" resultType="Department">
       select * from department;
    </select>


    <select id="getDepartment" resultType="Department" parameterType="int">
       select * from department where id = #{id};
    </select>


</mapper>

7、maven配置資源過濾問題

<resources>
    <resource>
        <directory>src/main/java</directory>
        <includes>
            <include>**/*.xml</include>
        </includes>
        <filtering>true</filtering>
    </resource>
</resources>

8、編寫部門的 DepartmentController 進行測試!

@RestController
public class DepartmentController {
    
    @Autowired
    DepartmentMapper departmentMapper;
    
    // 查詢全部部門
    @GetMapping("/getDepartments")
    public List<Department> getDepartments(){
        return departmentMapper.getDepartments();
    }
 
 
    // 查詢全部部門
    @GetMapping("/getDepartment/{id}")
    public Department getDepartment(@PathVariable("id") Integer id){
        return departmentMapper.getDepartment(id);
    }
    
}

啟動專案訪問進行測試!

我們增加一個員工類再測試下,為之後做準備

1、新建一個pojo類 Employee ;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
 
 
    private Integer id;
    private String lastName;
    private String email;
    //1 male, 0 female
    private Integer gender;
    private Integer department;
    private Date birth;
 
 
    private Department eDepartment; // 冗餘設計
 
 
}

2、新建一個 EmployeeMapper 介面

//@Mapper : 表示本類是一個 MyBatis 的 Mapper
@Mapper
@Repository
public interface EmployeeMapper {
 
 
    // 獲取所有員工資訊
    List<Employee> getEmployees();
 
 
    // 新增一個員工
    int save(Employee employee);
 
 
    // 通過id獲得員工資訊
    Employee get(Integer id);
 
 
    // 通過id刪除員工
    int delete(Integer id);
 
 
}

3、編寫 EmployeeMapper.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.kuang.mapper.EmployeeMapper">
 
 
    <resultMap id="EmployeeMap" type="Employee">
        <id property="id" column="eid"/>
        <result property="lastName" column="last_name"/>
        <result property="email" column="email"/>
        <result property="gender" column="gender"/>
        <result property="birth" column="birth"/>
        <association property="eDepartment"  javaType="Department">
            <id property="id" column="did"/>
            <result property="departmentName" column="dname"/>
        </association>
    </resultMap>
 
 
    <select id="getEmployees" resultMap="EmployeeMap">
        select e.id as eid,last_name,email,gender,birth,d.id as did,d.department_name as dname
        from department d,employee e
        where d.id = e.department
    </select>
 
 
    <insert id="save" parameterType="Employee">
        insert into employee (last_name,email,gender,department,birth)
        values (#{lastName},#{email},#{gender},#{department},#{birth});
    </insert>
 
 
    <select id="get" resultType="Employee">
        select * from employee where id = #{id}
    </select>
 
 
    <delete id="delete" parameterType="int">
        delete from employee where id = #{id}
    </delete>
 
 
</mapper>

4、編寫EmployeeController類進行測試

@RestController
public class EmployeeController {
 
 
    @Autowired
    EmployeeMapper employeeMapper;
 
 
    // 獲取所有員工資訊
    @GetMapping("/getEmployees")
    public List<Employee> getEmployees(){
        return employeeMapper.getEmployees();
    }
 
 
    @GetMapping("/save")
    public int save(){
        Employee employee = new Employee();
        employee.setLastName("kuangshen");
        employee.setEmail("qinjiang@qq.com");
        employee.setGender(1);
        employee.setDepartment(101);
        employee.setBirth(new Date());
        return employeeMapper.save(employee);
    }
 
 
    // 通過id獲得員工資訊
    @GetMapping("/get/{id}")
    public Employee get(@PathVariable("id") Integer id){
        return employeeMapper.get(id);
    }
 
 
    // 通過id刪除員工
    @GetMapping("/delete/{id}")
    public int delete(@PathVariable("id") Integer id){
        return employeeMapper.delete(id);
    }
 
 
}

相關文章