1 简介
- 对于数据访问层,无论是SQL还是NoSQL,SpringBoot默认采用整合SpringData的方式进行统一处理,添加大量自动配置,屏蔽了很多设置 。引入各种xxxTemplate、xxxRepository来简化我们对数据访问层的操作。
- 对于我们来说只需要进行简单的设置即可。
2 整合基本JDBC和数据源
2.1 导入相关jar包的maven坐标
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope></dependency>
2.2 配置application.yml
spring: datasource: username: root password: 123456 url: jdbc:mysql://192.168.179.100:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true driver-class-name: com.mysql.jdbc.Driver
- 默认使用的数据源是org.apache.tomcat.jdbc.pool.DataSource。
- 数据源的相关配置都在DataSourceProperties里面。
- 自动配置原理。
参考org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration,根据配置创建数据源,默认使用Tomcat连接池,可以使用spring.datasource.type指定自定义的数据源类型。
abstract class DataSourceConfiguration { /** * Tomcat Pool DataSource configuration. */ @ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class) @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource", matchIfMissing = true) static class Tomcat extends DataSourceConfiguration { @Bean @ConfigurationProperties(prefix = "spring.datasource.tomcat") public org.apache.tomcat.jdbc.pool.DataSource dataSource( DataSourceProperties properties) { org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource( properties, org.apache.tomcat.jdbc.pool.DataSource.class); DatabaseDriver databaseDriver = DatabaseDriver .fromJdbcUrl(properties.determineUrl()); String validationQuery = databaseDriver.getValidationQuery(); if (validationQuery != null) { dataSource.setTestOnBorrow(true); dataSource.setValidationQuery(validationQuery); } return dataSource; } } /** * Hikari DataSource configuration. */ @ConditionalOnClass(HikariDataSource.class) @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true) static class Hikari extends DataSourceConfiguration { @Bean @ConfigurationProperties(prefix = "spring.datasource.hikari") public HikariDataSource dataSource(DataSourceProperties properties) { return createDataSource(properties, HikariDataSource.class); } } /** * DBCP DataSource configuration. * * @deprecated as of 1.5 in favor of DBCP2 */ @ConditionalOnClass(org.apache.commons.dbcp.BasicDataSource.class) @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.commons.dbcp.BasicDataSource", matchIfMissing = true) @Deprecated static class Dbcp extends DataSourceConfiguration { @Bean @ConfigurationProperties(prefix = "spring.datasource.dbcp") public org.apache.commons.dbcp.BasicDataSource dataSource( DataSourceProperties properties) { org.apache.commons.dbcp.BasicDataSource dataSource = createDataSource( properties, org.apache.commons.dbcp.BasicDataSource.class); DatabaseDriver databaseDriver = DatabaseDriver .fromJdbcUrl(properties.determineUrl()); String validationQuery = databaseDriver.getValidationQuery(); if (validationQuery != null) { dataSource.setTestOnBorrow(true); dataSource.setValidationQuery(validationQuery); } return dataSource; } } /** * DBCP DataSource configuration. */ @ConditionalOnClass(org.apache.commons.dbcp2.BasicDataSource.class) @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.commons.dbcp2.BasicDataSource", matchIfMissing = true) static class Dbcp2 extends DataSourceConfiguration { @Bean @ConfigurationProperties(prefix = "spring.datasource.dbcp2") public org.apache.commons.dbcp2.BasicDataSource dataSource( DataSourceProperties properties) { return createDataSource(properties, org.apache.commons.dbcp2.BasicDataSource.class); } } //其他略}
SpringBoot默认支持org.apache.tomcat.jdbc.pool.DataSource、HikariDataSource、org.apache.commons.dbcp.BasicDataSource和org.apache.commons.dbcp2.BasicDataSource数据源。
自定义数据源类型:
abstract class DataSourceConfiguration { /** * Generic DataSource configuration. */ @ConditionalOnMissingBean(DataSource.class) @ConditionalOnProperty(name = "spring.datasource.type") static class Generic { @Bean public DataSource dataSource(DataSourceProperties properties) { return properties.initializeDataSourceBuilder().build(); } } //其他略}
DataSourceInitializer是ApplicationListener,其作用是:
- ①运行建表语句,sql脚本的规则是schema-*.sql。
- ②运行插入数据的语句,sql脚本的规则是data-*.sql。
@Configuration@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })@EnableConfigurationProperties(DataSourceProperties.class)@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })public class DataSourceAutoConfiguration { @Bean @ConditionalOnMissingBean public DataSourceInitializer dataSourceInitializer(DataSourceProperties properties, ApplicationContext applicationContext) { return new DataSourceInitializer(properties, applicationContext); } //其他略 }
自动配置了JdbcTemplate和NamedParameterJdbcTemplate,用来操作数据库。
package org.springframework.boot.autoconfigure.jdbc;import javax.sql.DataSource;import org.springframework.boot.autoconfigure.AutoConfigureAfter;import org.springframework.boot.autoconfigure.EnableAutoConfiguration;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;import org.springframework.jdbc.core.JdbcOperations;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;@Configuration@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })@ConditionalOnSingleCandidate(DataSource.class)@AutoConfigureAfter(DataSourceAutoConfiguration.class)public class JdbcTemplateAutoConfiguration { private final DataSource dataSource; public JdbcTemplateAutoConfiguration(DataSource dataSource) { this.dataSource = dataSource; } @Bean @Primary @ConditionalOnMissingBean(JdbcOperations.class) public JdbcTemplate jdbcTemplate() { return new JdbcTemplate(this.dataSource); } @Bean @Primary @ConditionalOnMissingBean(NamedParameterJdbcOperations.class) public NamedParameterJdbcTemplate namedParameterJdbcTemplate() { return new NamedParameterJdbcTemplate(this.dataSource); }}
2.3 整合数据源Druid
2.3.1 引入druid
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.23</version></dependency>
spring: datasource: username: root password: 123456 url: jdbc:mysql://192.168.64.101:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource # 数据源的其他配置 druid: # 初始化 initial-size: 5 # 最小 min-idle: 5 # 最大 max-active: 20 # 连接等待超时时间 max-wait: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 time-between-eviction-runs-millis: 60000 # 配置一个连接在池中最小生存的时间,单位是毫秒 min-evictable-idle-time-millis: 300000 validation-query: SELECT 1 FROM DUAL test-on-borrow: false test-while-idle: true test-on-return: false # 打开PSCache,并且指定每个连接上PSCache的大小 pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20 # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 filters: stat,wall,log4j #合并多个DruidDataSource的监控数据 use-global-data-source-stat: true # 通过connectProperties属性来打开mergeSql功能;慢SQL记录 connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # 配置DruidStatFilter web-stat-filter: enabled: true url-pattern: "/*" exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" # 配置DruidStatViewServlet stat-view-servlet: enabled: true url-pattern: "/druid/*" # IP白名单(没有配置或者为空,则允许所有访问)# allow: # IP黑名单 (存在共同时,deny优先于allow)# deny: # 禁用HTML页面上的“Reset All”功能 reset-enable: false # 登录名 login-username: admin # 登录密码 login-password: 123456
3 整合Mybatis
3.1 导入相关jar包的Maven坐标
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.23</version></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version></dependency>
3.2 sql脚本
DROP TABLE IF EXISTS `employee`;CREATE TABLE `employee` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `gender` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
3.3 创建JavaBean
package com.sunxiaping.springboot.domain;public class Employee { private Integer id; private String name; private String gender; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } @Override public String toString() { return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", gender='" + gender + '\'' + '}'; }}
3.4 注解版
package com.sunxiaping.springboot.mapper;import com.sunxiaping.springboot.domain.Employee;import org.apache.ibatis.annotations.Insert;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Options;//指定这是一个操作数据库的Mapper@Mapperpublic interface EmployeeMapper { @Options(useGeneratedKeys = true,keyProperty = "id") @Insert("INSERT into `employee` (`name`,`gender`) values (#{name},#{gender})") void save(Employee employee);}
- 在SpringBoot的启动类上加上@MapperScan注解,指定Mapper接口所在的包名
package com.sunxiaping.springboot;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.web.servlet.ServletComponentScan;/** * 主程序,用来启动SpringBoot应用 */@SpringBootApplication@ServletComponentScan//指定扫描的Mapper接口的包名@MapperScan(basePackages = "com.sunxiaping.springboot.mapper")public class WebApplication { public static void main(String[] args) { SpringApplication.run(WebApplication.class, args); }}
package com.sunxiaping.springboot.config;import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class MybatisConfig { @Bean public ConfigurationCustomizer configurationCustomizer() { return (configuration)->{ //开启驼峰命名 configuration.setMapUnderscoreToCamelCase(true); }; }}
package com.sunxiaping.springboot;import com.sunxiaping.springboot.domain.Employee;import com.sunxiaping.springboot.mapper.EmployeeMapper;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;@RunWith(SpringRunner.class)@SpringBootTestpublic class SpringbootApplicationTests { @Autowired private EmployeeMapper employeeMapper; @Test public void test() { Employee employee = new Employee(); employee.setName("张三"); employee.setGender("男"); employeeMapper.save(employee); System.out.println("employee = " + employee); }}
3.5 配置版
package com.sunxiaping.springboot.mapper;import com.sunxiaping.springboot.domain.Employee;import org.apache.ibatis.annotations.Mapper;//指定这是一个操作数据库的Mapper@Mapperpublic interface EmployeeMapper { void save( Employee employee);}
<?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>
<?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.sunxiaping.springboot.mapper.EmployeeMapper"> <insert id="save" parameterType="com.sunxiaping.springboot.domain.Employee" useGeneratedKeys="true" keyProperty="id"> INSERT INTO `employee`(`name`,`gender`) VALUES (#{name},#{gender}) </insert></mapper>
spring: # 数据源 datasource: username: root password: 123456 url: jdbc:mysql://192.168.64.101:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource # 数据源的其他配置 druid: # 初始化 initial-size: 5 # 最小 min-idle: 5 # 最大 max-active: 20 # 连接等待超时时间 max-wait: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 time-between-eviction-runs-millis: 60000 # 配置一个连接在池中最小生存的时间,单位是毫秒 min-evictable-idle-time-millis: 300000 validation-query: SELECT 1 FROM DUAL test-on-borrow: false test-while-idle: true test-on-return: false # 打开PSCache,并且指定每个连接上PSCache的大小 pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20 # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 filters: stat,wall,log4j #合并多个DruidDataSource的监控数据 use-global-data-source-stat: true # 通过connectProperties属性来打开mergeSql功能;慢SQL记录 connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # 配置DruidStatFilter web-stat-filter: enabled: true url-pattern: "/*" exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" # 配置DruidStatViewServlet stat-view-servlet: enabled: true url-pattern: "/druid/*" # IP白名单(没有配置或者为空,则允许所有访问) # allow: # IP黑名单 (存在共同时,deny优先于allow) # deny: # 禁用HTML页面上的“Reset All”功能 reset-enable: false # 登录名 login-username: admin # 登录密码 login-password: 123456mybatis: mapper-locations: classpath:com/sunxiaping/springboot/mapper/*.xml config-location: classpath:mybatis-config.xml
package com.sunxiaping.springboot;import com.sunxiaping.springboot.domain.Employee;import com.sunxiaping.springboot.mapper.EmployeeMapper;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;@RunWith(SpringRunner.class)@SpringBootTestpublic class SpringbootApplicationTests { @Autowired private EmployeeMapper employeeMapper; @Test public void test() { Employee employee = new Employee(); employee.setName("张三"); employee.setGender("男"); employeeMapper.save(employee); System.out.println("employee = " + employee); }}
4 整合JPA
4.1 导入相关jar包的Maven坐标
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.23</version></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId></dependency>
4.2 编写实体类
package com.sunxiaping.springboot.domain;import javax.persistence.*;@Entity@Table(name = "`employee`")public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(name = "`name`") private String name; @Column(name = "`gender`") private String gender; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } @Override public String toString() { return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", gender='" + gender + '\'' + '}'; }}
4.3 编写application.yml
spring: # 数据源 datasource: username: root password: 123456 url: jdbc:mysql://192.168.64.101:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource # 数据源的其他配置 druid: # 初始化 initial-size: 5 # 最小 min-idle: 5 # 最大 max-active: 20 # 连接等待超时时间 max-wait: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 time-between-eviction-runs-millis: 60000 # 配置一个连接在池中最小生存的时间,单位是毫秒 min-evictable-idle-time-millis: 300000 validation-query: SELECT 1 FROM DUAL test-on-borrow: false test-while-idle: true test-on-return: false # 打开PSCache,并且指定每个连接上PSCache的大小 pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20 # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 filters: stat,wall,log4j #合并多个DruidDataSource的监控数据 use-global-data-source-stat: true # 通过connectProperties属性来打开mergeSql功能;慢SQL记录 connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # 配置DruidStatFilter web-stat-filter: enabled: true url-pattern: "/*" exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" # 配置DruidStatViewServlet stat-view-servlet: enabled: true url-pattern: "/druid/*" # IP白名单(没有配置或者为空,则允许所有访问) # allow: # IP黑名单 (存在共同时,deny优先于allow) # deny: # 禁用HTML页面上的“Reset All”功能 reset-enable: false # 登录名 login-username: admin # 登录密码 login-password: 123456 jpa: show-sql: true open-in-view: true hibernate: ddl-auto: update
4.3 编写Repository
package com.sunxiaping.springboot.repository;
import com.sunxiaping.springboot.domain.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface EmployeeRepository extends JpaRepository<Employee,Integer>, JpaSpecificationExecutor<Employee> {
}
4.4 测试
package com.sunxiaping.springboot;
import com.sunxiaping.springboot.domain.Employee;
import com.sunxiaping.springboot.repository.EmployeeRepository;
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 java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootApplicationTests {
@Autowired
private EmployeeRepository employeeRepository;
@Test
public void test() {
System.out.println("employeeRepository = " + employeeRepository);
List<Employee> employeeList = employeeRepository.findAll();
System.out.println("employeeList = " + employeeList);
}
}