SpringBoot数据访问

SpringBoot 数据库访问 JDBC访问数据库 整合Druid数据源 整合MyBatis 整合JPA

Page content

SpringBoot访问数据库

springboot 默认支持很多数据源 有:Hikari,Tomcat, Dbcp2,Generic。 springboot2.X默认数据源为:Hikari。下面还会讲到切换数据源到阿里的Druid, 同时和springboot桥接进行数据库访问的方式也有很多:JDBC,Mybatis,JPA

源码:DataSourceAutoConfiguration –>PooledDataSourceConfiguration可以看到数据源的配置

@Configuration
@Conditional({DataSourceAutoConfiguration.PooledDataSourceCondition.class})
@ConditionalOnMissingBean({DataSource.class, XADataSource.class})
@Import({Hikari.class, Tomcat.class, Dbcp2.class, Generic.class, DataSourceJmxConfiguration.class})
protected static class PooledDataSourceConfiguration {
    protected PooledDataSourceConfiguration() {
    }
}

JDBC访问数据库

1. 准备工作

  • pom.xml

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
    </dependency>
    
  • application.yml

    spring:
    datasource:
    username: root
    password:
    url: jdbc:mysql://localhost:3306/jdbc
    driver-class-name: com.mysql.jdbc.Driver
    

2. 创建表

springboot创建表相关源码, 通过DataSourceInitializer拿到数据源,可以创建表结构或者表数据

//创建表结构
public boolean createSchema() {
    List<Resource> scripts = this.getScripts("spring.datasource.schema", this.properties.getSchema(), "schema");
    if (!scripts.isEmpty()) {
        if (!this.isEnabled()) {
            logger.debug("Initialization disabled (not running DDL scripts)");
            return false;
        }

        String username = this.properties.getSchemaUsername();
        String password = this.properties.getSchemaPassword();
        this.runScripts(scripts, username, password);
    }
    return !scripts.isEmpty();
}
//初始化表数据
public void initSchema() {
        List<Resource> scripts = this.getScripts("spring.datasource.data", this.properties.getData(), "data");
        if (!scripts.isEmpty()) {
            if (!this.isEnabled()) {
                logger.debug("Initialization disabled (not running data scripts)");
                return;
            }

            String username = this.properties.getDataUsername();
            String password = this.properties.getDataPassword();
            this.runScripts(scripts, username, password);
        }

    }
//执行sql语句
private void runScripts(List<Resource> resources, String username, String password) {
        if (!resources.isEmpty()) {
            ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
            populator.setContinueOnError(this.properties.isContinueOnError());
            populator.setSeparator(this.properties.getSeparator());
            if (this.properties.getSqlScriptEncoding() != null) {
                populator.setSqlScriptEncoding(this.properties.getSqlScriptEncoding().name());
            }

            Iterator var5 = resources.iterator();

            while(var5.hasNext()) {
                Resource resource = (Resource)var5.next();
                populator.addScript(resource);
            }

            DataSource dataSource = this.dataSource;
            if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
                dataSource = DataSourceBuilder.create(this.properties.getClassLoader()).driverClassName(this.properties.determineDriverClassName()).url(this.properties.determineUrl()).username(username).password(password).build();
            }

            DatabasePopulatorUtils.execute(populator, dataSource);
        }
    }
//获得脚本
private List<Resource> getScripts(String propertyName, List<String> resources, String fallback) {
        if (resources != null) {
            return this.getResources(propertyName, resources, true);
        } else {
            String platform = this.properties.getPlatform();
            List<String> fallbackResources = new ArrayList();
            fallbackResources.add("classpath*:" + fallback + "-" + platform + ".sql");
            fallbackResources.add("classpath*:" + fallback + ".sql");
            return this.getResources(propertyName, fallbackResources, false);
        }
    }

通过以上源码可以知道,想要执行创建表结构或者表数据的sql语句,默认将sql文件的名称修改为:

  • schema-*.sql
  • data-*.sql

或者在配置文件中直接指定:

spring:
  datasource:
    username: root
    password:
    url: jdbc:mysql://localhost:3306/jdbc
    driver-class-name: com.mysql.jdbc.Driver
    schema:
      - classpath:department.sql
    initialization-mode: always

目录

department.sql

DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `departmentName` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

直接运行Application,就可以生成表结构:

springboot生成表

3. 简单查询

表数据

表数据

Controller

/**
 * @program: spring-boot-data-jdbc
 * @description: 通过JDBC的方式操作数据库
 * @author: YuanChangYue
 * @create: 2019-08-20 12:11
 */
@Controller
public class JDBCController {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @GetMapping("/deps")
    @ResponseBody
    public List<Map<String, Object>> queryList() {
        return jdbcTemplate.queryForList("select * from department");
    }

}

查询结果

查询结果

整合Druid数据源

1. 添加maven依赖

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

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

注意:需要添加 log4j 的依赖,否者会报错

报错-没有log4j

2. application.yml

spring:
  datasource:
    username: root
    password:
    url: jdbc:mysql://localhost:3306/jdbc
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

3. 测试类检测是否切换成功

package springbootdatajdbc.demo;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {

    @Autowired
    DataSource dataSource;

    @Test
    public void contextLoads() throws SQLException {
        System.out.println("dataSource.getClass() = " + dataSource.getClass());
        Connection connection = dataSource.getConnection();
        System.out.println("connection = " + connection);
        connection.close();
    }
}

Druid数据源

4. Druid数据源其他配置

spring:
  datasource:
    username: root
    password:
    url: jdbc:mysql://localhost:3306/jdbc
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    #   数据源其他配置
    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.slowSqlMillis=500

5. Druid的配置类

package springbootdatajdbc.demo.config;

import com.alibaba.druid.pool.DruidDataSource;
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;

/**
 * @program: spring-boot-data-jdbc
 * @description: Druid配置类
 * @author: YuanChangYue
 * @create: 2019-08-20 12:40
 */
@Configuration
public class DruidConfig {

    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druid() {
        return new DruidDataSource();
    }

  /**
     * 配置后台监控
     * 1 . 配置管理后台的Servlet
     */
    @Bean
    public ServletRegistrationBean statViewServlet() {
        ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
        Map<String, String> initParams = new HashMap<>();
        initParams.put("loginUsername", "admin");
        initParams.put("loginPassword", "123456");
        initParams.put("allow", "");

        bean.setInitParameters(initParams);
        return bean;
    }

  /**
     * 2 . 配置监听web的filter
     */
  @Bean
  public FilterRegistrationBean webStatFilter() {
    FilterRegistrationBean<WebStatFilter> bean = new FilterRegistrationBean<>(new WebStatFilter());
    Map<String, String> initParams = new HashMap<>();
    initParams.put("exclusions", "*.js,*.css,/druid/*");
    bean.setInitParameters(initParams);
    bean.setUrlPatterns(Arrays.asList("/*"));
    return bean;
  }
}

最后访问 : http://localhost:8080/druid

Druid登录页

Druid主页

整合MyBatis

1. 添加依赖

<?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.1.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>spring-boot-data-mybatis</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</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-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>

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

        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </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>
        </dependency>
    </dependencies>

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

</project>


mybatis-spring-boot-starter

2. 创建实体类

package springbootdatamybatis.demo.bean;

/**
 * @program: spring-boot-data-mybatis
 * @description:
 * @author: YuanChangYue
 * @create: 2019-08-20 13:43
 */
public class Department {
    private Integer Id;
    private String departmentName;

    public Integer getId() {
        return Id;
    }

    public void setId(Integer id) {
        Id = id;
    }

    public String getDepartmentName() {
        return departmentName;
    }

    public void setDepartmentName(String departmentName) {
        this.departmentName = departmentName;
    }
}

package springbootdatamybatis.demo.bean;

/**
 * @program: spring-boot-data-mybatis
 * @description:
 * @author: YuanChangYue
 * @create: 2019-08-20 13:46
 */
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Integer gender;
    private int dId;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    public int getdId() {
        return dId;
    }

    public void setdId(int dId) {
        this.dId = dId;
    }
}

3. 注解版

package springbootdatamybatis.demo.mapper;

import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
import springbootdatamybatis.demo.bean.Department;

@Repository
@Mapper
public interface DepartmentMapper {

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

    @Delete("delete from department where id = #{id}")
    public int deleteDepById(Integer id);
  
    @Options(useGeneratedKeys = true, keyProperty = "id")
    @Insert("insert into department(departmentName) values(#{departmentName})")
    public int insertDept(Department department);

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

}


这里为了方便,没有加入serive层

package springbootdatamybatis.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import springbootdatamybatis.demo.bean.Department;
import springbootdatamybatis.demo.mapper.DepartmentMapper;

/**
 * @program: spring-boot-data-mybatis
 * @description: 部门控制器
 * @author: YuanChangYue
 * @create: 2019-08-20 13:56
 */
@Controller
public class DeptController {

    @Autowired
    DepartmentMapper departmentMapper;

    @GetMapping("/dept/{id}")
    @ResponseBody
    public Department getDep(@PathVariable("id") Integer id) {
        return departmentMapper.getDepById(id);
    }

    @GetMapping("/dept")
    @ResponseBody
    public Department insertDept(Department department) {
        departmentMapper.insertDept(department);
        return department;
    }
}
package springbootdatamybatis.demo.config;

import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @program: spring-boot-data-mybatis
 * @description: Mybatis 配置类
 * @author: YuanChangYue
 * @create: 2019-08-20 15:47
 */
@Configuration
public class MybatisConfig {

    /**
     *  自定义配置 支持驼峰 
     */
    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> configuration.setMapUnderscoreToCamelCase(true);
    }
}

dept-querybyid

dept-insert

4. mapper配置版

package springbootdatamybatis.demo.mapper;

import org.springframework.stereotype.Repository;
import springbootdatamybatis.demo.bean.Employee;

@Repository
public interface EmployeeMapper {

    public Employee getEmpById(Integer id);
    
}
<?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="springbootdatamybatis.demo.mapper.EmployeeMapper">

    <select id="getEmpById" resultType="springbootdatamybatis.demo.bean.Employee">
        select * from employee where id  = #{id}
    </select>

</mapper>
<?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>
package springbootdatamybatis.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import springbootdatamybatis.demo.bean.Employee;
import springbootdatamybatis.demo.mapper.EmployeeMapper;

/**
 * @program: spring-boot-data-mybatis
 * @description:
 * @author: YuanChangYue
 * @create: 2019-08-20 16:20
 */
@Controller
public class EmpController {

    @Autowired
    EmployeeMapper employeeMapper;

    @ResponseBody
    @GetMapping("/emp/{id}")
    public Employee getEmpById(@PathVariable("id") Integer id) {
        return employeeMapper.getEmpById(id);
    }
}
spring:
  datasource:
    #   数据源基本配置
    username: root
    password:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis
    type: com.alibaba.druid.pool.DruidDataSource
    #   数据源其他配置
    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.slowSqlMillis=500

#    schema:
#      - classpath:sql/department.sql
#      - classpath:sql/employee.sql
#    initialization-mode: always

mybatis:
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*

emp-queryById

整合 SpringData JPA

1. 实体类

package com.changyue.springbootdatajpa.entity;

import javax.persistence.*;

/**
 * @program: spring-boot-data-jpa
 * @description: 用户
 * @author: YuanChangYue
 * @create: 2019-08-20 16:36
 */
@Entity
@Table(name = "tbl_user")
@JsonIgnoreProperties(value = {"hibernateLazyInitializer", "handler"})
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "last_name", length = 50)
    private String lastName;
    @Column
    private String email;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmial() {
        return email;
    }

    public void setEmial(String emial) {
        this.email = emial;
    }
}

2. Dao接口

package com.changyue.springbootdatajpa.repository;


import com.changyue.springbootdatajpa.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * 继承JpaRepository
 */
public interface UserRepository extends JpaRepository<User, Integer> {

}

3. 配置

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/jpa
    username: root
    password:
    driver-class-name: com.mysql.jdbc.Driver
    
  jpa:
    hibernate:
      #更新和创建表结构
      ddl-auto: update
    #控制台显示sql
    show-sql: true	

完成以上操作,会根据实体类在数据库中创建表

jpa-create-table

4. 简单查询

package com.changyue.springbootdatajpa.controller;

import com.changyue.springbootdatajpa.entity.User;
import com.changyue.springbootdatajpa.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;

/**
 * @program: spring-boot-data-jpa
 * @description:
 * @author: YuanChangYue
 * @create: 2019-08-20 16:52
 */
@RestController
public class UserController {
    @Autowired
    UserRepository userRepository;

    /**
     * 简单通过id查询出use信息
     *
     * @return user json
     */
    @GetMapping("/user/{id}")
    public User getUserById(@PathVariable("id") Integer id) {
        return userRepository.getOne(id);
    }
}

user-queryById