第六章 Spring Boot 与数据访问

数据访问简介

JDBC
MyBatis
SpringDataJPA

JDBC&自动配置原理

依赖

<!--数据库访问-->
<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>

配置数据源
支持的数据源

HikariDataSource
org.apache.tomcat.jdbc.pool.DataSource
org.apache.commons.dbcp2.BasicDataSource
自定义 DataSource

DataSourceInitializer

自动运行建表语句
运行插入数据的 sql 语句

文件名命名规则

scheme-*.sql
data-*.sql

或者指定文件名

application.yml

spring:
  # 配置数据源
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/data
    driver-class-name: com.mysql.cj.jdbc.Driver

    # 始终执行初始化
    initialization-mode: ALWAYS
    # 指定自动建表sql
    schema:
      - classpath:schema-all.sql

schema-all.sql

create table if not exists `person` (
    `id` int,
    `name` varchar(255)
);
-- 注意需要有分号结尾

查询数据

package com.example.demo.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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;

/**
 * 人物数据接口
 */
@RestController
@RequestMapping("/person")
public class PersonController {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @GetMapping("/list")
    public List<Map<String, Object>> list(){
        String sql = "select * from person";
        List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
        return list;
    }
}

整合 Druid&配置数据源监控

Druid 能够提供强大的监控和扩展功能

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,去掉后监控见面sql无法统计,wall用于防火墙
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMills=500

依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.21</version>
</dependency>

配置

spring:
  # 配置数据源
  datasource:
    # 自定义数据源
    type: com.alibaba.druid.pool.DruidDataSource
    # 统计sql
    filters: stat,wall

自定义配置类

package com.example.demo.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.ResourceServlet;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DruidConfig {
    // 加载druid的自定义参数
    @ConfigurationProperties(prefix="spring.datasource")
    @Bean
    public DataSource druid(){
        return new DruidDataSource();
    }

    // 配置Druid监控
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        Map<String, String> params = new HashMap<>();
        params.put(ResourceServlet.PARAM_NAME_USERNAME, "admin");
        params.put(ResourceServlet.PARAM_NAME_PASSWORD, "123456");

        bean.setInitParameters(params);
        return bean;
    }

    // 配置filter
    @Bean
    public FilterRegistrationBean webStatFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new WebStatFilter());
        bean.setUrlPatterns(Arrays.asList("/*"));

        Map<String, String> params = new HashMap<>();
        params.put(WebStatFilter.PARAM_NAME_EXCLUSIONS, "*.js,*.css");

        bean.setInitParameters(params);

        return bean;
    }

}

后台监控地址

http://localhost:8080/druid/

整合 MyBatis(一)-基础环境搭建

mybatis-spring-boot-starter

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

整合 MyBatis(二)-注解版 MyBatis

src/main/java/com/example/demo/mapper/DepartmentMapper.java

package com.example.demo.mapper;

import com.example.demo.pojo.Department;
import org.apache.ibatis.annotations.*;


// 指定这是一个操作数据库的mapper
// @Mapper
public interface DepartmentMapper {

    @Select("select * from department where id = #{id}")
    public Department getById(Integer id);

    @Delete("delete from department where id = #{id}")
    public int deleteById(Integer id);

    // 插入数据后自增主键自动设置到department
    @Options(useGeneratedKeys = true, keyProperty = "id")
    @Insert("insert into department(name) values(#{name}) ")
    public int insert(Department department);

    @Update("update department set name = #{name} where id = #{id} ")
    public int update(Department department);
}

src/main/java/com/example/demo/controller/DepartmentController.java

package com.example.demo.controller;


import com.example.demo.mapper.DepartmentMapper;
import com.example.demo.pojo.Department;
import org.springframework.beans.factory.annotation.Autowired;
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;

@RestController
public class DepartmentController {

    @Autowired
    private DepartmentMapper departmentMapper;

    @GetMapping("/dept/{id}")
    public Department getDepartment(@PathVariable("id") Integer id){
        return departmentMapper.getById(id);
    }

    @GetMapping("/dept")
    public Department insertDepartment(Department department){
        departmentMapper.insert(department);
        return department;
    }
}

src/main/java/com/example/demo/DemoApplication.java

package com.example.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


// 自动扫描mapper接口,不用每个mapper都添加@Mapper注解
@MapperScan(value = {"com.example.demo.mapper"})
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

整合 MyBatis(二)-配置版 MyBatis

文档:
https://mybatis.org/mybatis-3/zh/index.html

application.yml

mybatis:
  # 指定全局配置文件路径
  config-location: classpath:mybatis/mybatis-config.xml
  # 指定mapper文件路径
  mapper-locations: classpath:mybatis/mapper/*.xml

src/main/resources/mybatis/mybatis-config.xml

<?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>

src/main/resources/mybatis/mapper/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.example.demo.mapper.EmployeeMapper">

    <select id="getById" resultType="com.example.demo.pojo.Employee">
    select * from employee where id = #{id}
  </select>

    <insert id="insert">
        insert into employee (name, age, sex, birth, department_id)
        values (#{name}, #{age}, #{sex}, #{birth}, #{department_id})
    </insert>

    <delete id="deleteById">
        delete from employee where id = #{id}
    </delete>

</mapper>

src/main/java/com/example/demo/mapper/EmployeeMapper.java

package com.example.demo.mapper;

import com.example.demo.pojo.Employee;

// @Mapper 或@MapperScan 将接口扫描装配到容器中
public interface EmployeeMapper {

    public Employee getById(Integer id);

    public int deleteById(Integer id);

    public void insert(Employee employee);
}

src/main/java/com/example/demo/controller/DepartmentController.java

package com.example.demo.controller;


import com.example.demo.mapper.DepartmentMapper;
import com.example.demo.mapper.EmployeeMapper;
import com.example.demo.pojo.Department;
import com.example.demo.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
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;

@RestController
public class DepartmentController {
    @Autowired
    private EmployeeMapper employeeMapper;

    @GetMapping("/getEmp/{id}")
    public Employee getEmployee(@PathVariable("id") Integer id){
        return employeeMapper.getById(id);
    }

}

SpringData JPA

SpringData 为我们提供使用同一的 API 来对数据访问层进行操作

JPA: Java Persistence API

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <version>2.3.1.RELEASE</version>
</dependency>

配置文件

spring:
  jpa:
    hibernate:
      # 更新或创建表结构
      ddl-auto: update
    # 控制台打印sql
    show-sql: true

JAP:ORM Object Relation Mapping

编写实体类与数据表进行映射

package com.example.demo.entity;

import javax.persistence.*;

// 使用JPA注解配置映射关系
@Entity // 实体类
@Table(name = "tbl_user") // 指定表名
public class User {

    @Id // 主键
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增
    private Integer id;

    @Column(name = "last_name", length = 50)
    private String lastName;

    @Column // 默认类名=属性名
    private String email;
}

创建 repository

package com.example.demo.repository;

import com.example.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

// 继承JpaRepository来完成对数据库的操作
public interface UserRepository extends JpaRepository<User, Integer> {
}

Controller

package com.example.demo.controller;

import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.Optional;

@RestController
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @GetMapping("/user/{id}")
    public User getUser(@PathVariable("id") Integer id){
        Optional<User> user = userRepository.findById(id);

        if(user.isPresent()){
            return user.get();
        } else{
            return null;
        }
    }

    @GetMapping("/user")
    public User insertUser(User user){
        User savedUser = userRepository.save(user);
        return savedUser;
    }
}