文章目录
1. SpringBoot 与数据库连接
- 创建一个SpringBoot项目,选择starter的时候,多选两个,jdbc、mysqlql
- 配置数据库连接信息
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://47.94.231.234:3306/jdbc
- 测试能否连接上数据库
@SpringBootTest
class SpringBoot06DataJdbcApplicationTests {
@Autowired
HikariDataSource dataSource;
@Test
void contextLoads() throws SQLException {
System.out.println(dataSource.getClass());
Connection connection = dataSource.getConnection();
System.out.println(connection);
}
}
springboot默认是使用com.zaxxer.hikari.HikariDataSource
作为数据源,2.0以下是用org.apache.tomcat.jdbc.pool.DataSource
作为数据源;
数据源的相关配置都在DataSourceProperties
里面;
2. 自动配置原理
jdbc的相关配置都在org.springframework.boot.autoconfigure.jdbc
包下
参考DataSourceConfiguration
,根据配置创建数据源,默认使用Hikari连接池;可以使用spring.datasource.type
指定自定义的数据源类型;
springboot默认支持的连池:
- org.apache.commons.dbcp2.BasicDataSource
- com.zaxxer.hikari.HikariDataSource
- org.apache.tomcat.jdbc.pool.DataSource
自定义数据源类型(在配置文件中指定spring.datasource.type
):
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnMissingBean({DataSource.class})
@ConditionalOnProperty(
name = {"spring.datasource.type"}
)
static class Generic {
Generic() {
}
@Bean
DataSource dataSource(DataSourceProperties properties) {
//使用DataSourceBuilder创建数据源,利用反射创建响应type的数据源,并且绑定相关属性
return properties.initializeDataSourceBuilder().build();
}
}
容器中没有DataSource.class
的话,会进行注册,就会执行properties.initializeDataSourceBuilder().build();
initializeDataSourceBuilder(),会创建一个DataSourceBuilder
对象,并且根据配置文件中指定的属性进行绑定,之后执行build()
创建数据源
public DataSourceBuilder<?> initializeDataSourceBuilder() {
return DataSourceBuilder.create(this.getClassLoader()).type(this.getType()).driverClassName(this.determineDriverClassName()).url(this.determineUrl()).username(this.determineUsername()).password(this.determinePassword());
}
DataSourceBuilder:绑定相关属性
public <D extends DataSource> DataSourceBuilder<D> type(Class<D> type) {
this.type = type;
return this;
}
public DataSourceBuilder<T> url(String url) {
this.properties.put("url", url);
return this;
}
public DataSourceBuilder<T> driverClassName(String driverClassName) {
this.properties.put("driverClassName", driverClassName);
return this;
}
。。。。
build() : 根据type创建数据源
public T build() {
Class<? extends DataSource> type = this.getType();
DataSource result = (DataSource)BeanUtils.instantiateClass(type);
this.maybeGetDriverClassName();
this.bind(result);
return result;
}
如果没有指定的话,type就是com.zaxxer.hikari.HikariDataSource
getType
private Class<? extends DataSource> getType() {
Class<? extends DataSource> type = this.type != null ? this.type : findType(this.classLoader);
if (type != null) {
return type;
} else {
throw new IllegalStateException("No supported DataSource type found");
}
}
findType
private static final String[] DATA_SOURCE_TYPE_NAMES = new String[]{"com.zaxxer.hikari.HikariDataSource", "org.apache.tomcat.jdbc.pool.DataSource", "org.apache.commons.dbcp2.BasicDataSource"};
public static Class<? extends DataSource> findType(ClassLoader classLoader) {
String[] var1 = DATA_SOURCE_TYPE_NAMES;
int var2 = var1.length;
int var3 = 0;
while(var3 < var2) {
String name = var1[var3];
try {
return ClassUtils.forName(name, classLoader);
} catch (Exception var6) {
++var3;
}
}
return null;
}
启动应用,执行sql
SpringBoot在创建连接池后还会运行预定义的SQL脚本文件,具体参考
org.springframework.boot.autoconfigure.jdbc.DataSourceInitializationConfiguration
配置类,
在该类中注册了dataSourceInitializerPostProcessor
下面是获取schema脚本文件的方法
List<Resource> scripts = this.getScripts("spring.datasource.data", this.properties.getData(), "data");
可以看出,如果我们没有在配置文件中配置脚本的具体位置,就会在classpath下找schema-all.sql和schema.sql platform获取的是all,platform可以在配置文件中修改
具体查看createSchema()方法和initSchema()方法
initSchema()方法获取的是data-all.sql,data.sql
我们也可以在配置文件中配置sql文件的位置
spring:
datasource:
schema:
- classpath:department.sql
测试
在类路径下创建schema.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;
程序启动后发现表并没有被创建,DEBUG查看以下,发现在运行之前会有一个判断
上面方法也不知道在干什么,反正就是只要是NEVER和EMBEDDED就为true,而DataSourceInitializationMode枚举类中除了这两个就剩下ALWAYS了,可以在配置文件中配置为ALWAYS
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://47.94.231.234:3306/jdbc
initialization-mode: always
一般来说,schema.sql:建表语句,data.sql:插入数据,不这样写也是可以的。
项目每次启动都会执行一次sql
3. 整合druid数据源
选择哪个数据库连接池
- DBCP2 是 Appache 基金会下的项目,是最早出现的数据库连接池 DBCP 的第二个版本。
- C3P0 最早出现时是作为 Hibernate 框架的默认数据库连接池而进入市场。
- Druid 是阿里巴巴公司开源的一款数据库连接池,其特点在于有丰富的附加功能。
- HikariCP 相较而言比较新,它最近两年才出现,据称是速度最快的数据库连接池。最近更是被 Spring 设置为默认数据库连接池。
不选择 C3P0 的原因:
C3P0 的 Connection 是异步释放。这个特性会导致释放的在某些情况下 Connection 实际上 still in use ,并未真正释放掉,从而导致连接池中的 Connection 耗完,等待状况。
Hibernate 现在对所有数据库连接池一视同仁,官方不再指定『默认』数据库连接池。因此 C3P0 就失去了『官方』光环。
不选择 DBCP2 的原因:
相较于 Druid 和 HikariCP,DBCP2 没有什么特色功能/卖点。基本上属于 能用,没毛病 的情况,地位显得略有尴尬。
- 在 Spring Boot 项目中加入
druid-spring-boot-starter
依赖点击查询最新版本
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.20</version>
</dependency>
- 在配置文件中指定数据源类型
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://47.94.231.234:3306/jdbc
initialization-mode: always
type: com.alibaba.druid.pool.DruidDataSource
- 测试使用的数据源
@SpringBootTest
class SpringBoot06DataJdbcApplicationTests {
@Autowired
DataSource dataSource;
@Test
void contextLoads() throws SQLException {
System.out.println(dataSource.getClass());
Connection connection = dataSource.getConnection();
System.out.println(connection);
}
- 配置参数
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://47.94.231.234:3306/jdbc
initialization-mode: always
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,slf4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
@Configuration
public class DruidConfig {
//注册DruidDataSource,并进行参数绑定
@ConfigurationProperties(prefix="spring.datasource")
@Bean
public DruidDataSource druidDataSource(){
return new DruidDataSource();
}
}
后台页面,访问http://localhost:8080/druid/login.html
会显示登陆页面
输入在配置文件中设置的账号和密码
4. 整合MyBatis
4.1 引入依赖
<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>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!--引入druid数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.20</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
依赖关系
4.2项目构建
- 在resources下创建sql目录,在sql目录中创建department.sql和employee.sql,可项目启动时创建表
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;
employee.sql
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`lastName` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`gender` int(2) DEFAULT NULL,
`d_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
-
创建实体类
- Employee
- Department
-
配置文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://47.94.231.234:3306/mybatis
#只有配置这个,才可以在项目启动的时候,执行sql文件
initialization-mode: always
type: com.alibaba.druid.pool.DruidDataSource
#数据源其他配置
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,slf4j
# 配置web监控的filter,默认配置也和下面相同(除用户名密码,enabled默认false外),其他可以不配
web-stat-filter:
enabled: true
url-pattern: /*
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
#配置管理后台的servlet
stat-view-servlet:
enabled: true
url-pattern: /druid/*
login-username: root
login-password: root
allow: 127.0.0.1
# schema:
# - classpath:sql/department.sql
# - classpath:sql/employee.sql
- MyBatis增删改查(注解版)
@Mapper //指定这是操作数据库的mapper
public interface DepartmentMapper {
@Select("select * from department where id=#{id}")
public Department getDeptById(int id);
@Delete("delete from department where id=#{id}")
public int deleteDEptById(int id);
@Update("update department set departmentName=#{departmentName} where id=#{id}")
public int updateDept(Department department);
//返回自增id,并与department的id属性进行绑定
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into department(departmentName) values(#{departmentName})")
public int insertDept(Department department);
}
- 创建Controller
@RestController
public class DeptController {
@Autowired
private DepartmentMapper departmentMapper;
@GetMapping("/dept/{id}")
public Department getDepartment(@PathVariable("id") int id) {
return departmentMapper.getDeptById(id);
}
@GetMapping("/dept")
public Department insert(Department department) {
departmentMapper.insertDept(department);
return department;
}
}
-
访问:
http://localhost:8080/dep?departmentName=PeppaPig
添加一条数据 -
访问:
http://localhost:8080/dep/1
获取数据
4.3 Mybatis 配置
当表的列名和实体类中的属性名不一样的时候,访问:http://localhost:8080/dep/1
获取数据,departmentName 就是null
{"id":2,"departmentName":null}
由于列表和属性名不一致,所以就没有封装进去,我们表中的列名和实体类属性名都是遵循驼峰命名规则的,可以开启mybatis的开启驼峰命名配置
#开启mybatis的驼峰命名配置
mybatis:
configuration:
map-underscore-to-camel-case: true
然后重启项目,重新插入数据,再查询就发现可以封装进去了
也可以通过向spring容器中注入org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer
的方法设置mybatis参数
@Configuration
public class MyBatisConfig {
@Bean
public ConfigurationCustomizer customizer() {
return new ConfigurationCustomizer() {
@Override
public void customize(org.apache.ibatis.session.Configuration configuration) {
configuration.setMapUnderscoreToCamelCase(true);
}
};
}
}
4.4 Mapper 扫描
使用@mapper注解的类可以被扫描到容器中,但是每个Mapper都要加上这个注解就是一个繁琐的工作,能不能直接扫描某个包下的所有Mapper接口呢,当然可以,在springboot启动类上加上@MapperScan
//配置包扫描,扫描mapper下的所有接口
@MapperScan(value="com.ewen.springboot.mapper")
@SpringBootApplication
public class SpringBoot06DataMybatisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBoot06DataMybatisApplication.class, args);
}
}
4.5 使用xml配置文件
- 创建mybatis全局配置文件mybatis_config.xml,这里是在resource目录下创建
mydatis/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>
<!--配置别名-->
<typeAliases>
<package name="com.ewen.springboot"/>
</typeAliases>
</configuration>
- 创建EmployeeMapper接口
//@Mapper 在启动类中已经使用了@MapperScan
public interface EmployeeMapper {
public Employee selectById(Integer id);
public int insert(Employee employee);
}
- 创建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.ewen.springboot.mapper.EmployeeMapper">
<select id="selectById" parameterType="Integer" resultType="com.ewen.springboot.bean.Employee">
SELECT * FROM employee WHERE id = #{id};
</select>
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO employee(lastName, email,gender,d_id) VALUES (#{lastName},#{email},
#{gender},#{d_id});
</insert>
</mapper>
- 配置文件(application.yaml)中指定配置文件和映射文件的位置
mybatis:
config-location: classpath:mybatis/mybatis_config.xml
mapper-locations: classpath:mybatis/mapper/EmployeeMapper.xml
目录结构如下
5. 整合SpringData JPA
待续。。。。
如有不足之处,欢迎指正,谢谢!