# spring boot数据访问
# 导入依赖
因为是一个数据相关的,所以我们就需要导入一个与数据访问相关的starts
spring-boot-starter-data-jdbc
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
2
3
4
5
# 分析starter
此starter会导入
我们除了导入场景以外,还需要导入驱动依赖,因为spring也不知道我们要操作哪种数据库
# 自动配置类分析
因为导入的是一个spring-boot-starter-data-jdbc的starter,所以我们就需要去自动配置依赖中,找jdbc的自动配置类
因为导入一个starter后,spring启动的时候,就会自动导入与此starter相关的配置
# 1.DataSourceAutoConfiguration
此配置是哟和数据源相关的
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
DataSourceInitializationConfiguration.InitializationSpecificCredentialsDataSourceInitializationConfiguration.class,
DataSourceInitializationConfiguration.SharedCredentialsDataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {}
2
3
4
5
6
7
8
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
是响应式编程
可以看出,其和配置文件绑定,所以修改DataSource可以在配置文件中,进行修改@ConfigurationProperties(prefix = "spring.datasource")
从DataSourcePoolMetadataProvidersConfiguration配置类中,可以看出,如果导入这个starter,那么默认使用的数据源是HikariDataSource
,因为其他的数据源都爆红,没有导入
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class)
static class HikariPoolDataSourceMetadataProviderConfiguration {
@Bean
DataSourcePoolMetadataProvider hikariPoolDataSourceMetadataProvider() {
return (dataSource) -> {
HikariDataSource hikariDataSource = DataSourceUnwrapper.unwrap(dataSource, HikariConfigMXBean.class,
HikariDataSource.class);
if (hikariDataSource != null) {
return new HikariDataSourcePoolMetadata(hikariDataSource);
}
return null;
};
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 2.DataSourceTransactionManagerAutoConfiguration
此配置是和事务相关的
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ JdbcTemplate.class, TransactionManager.class })
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration {}
2
3
4
5
此配置也是和@ConfigurationProperties(prefix = "spring.datasource")绑定
此配置类,会创建一个DataSourceTransactionManager的bean
# 3.JdbcTemplateAutoConfiguration
@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
@ConditionalOnSingleCandidate(DataSource.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties(JdbcProperties.class)
@Import({ DatabaseInitializationDependencyConfigurer.class, JdbcTemplateConfiguration.class,
NamedParameterJdbcTemplateConfiguration.class })
public class JdbcTemplateAutoConfiguration {
}
2
3
4
5
6
7
8
9
和配置文件@ConfigurationProperties(prefix = "spring.jdbc")绑定
@Bean
@Primary
JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
JdbcProperties.Template template = properties.getTemplate();
jdbcTemplate.setFetchSize(template.getFetchSize());
jdbcTemplate.setMaxRows(template.getMaxRows());
if (template.getQueryTimeout() != null) {
jdbcTemplate.setQueryTimeout((int) template.getQueryTimeout().getSeconds());
}
return jdbcTemplate;
}
从这里可以看出,JdbcTemplateAutoConfiguration在容器中,加入一个JdbcTemplate,所以我们可以使用自动注入的方式,并且数据源使用容器中的数据源
2
3
4
5
6
7
8
9
10
11
12
13
14
# 4.XADataSourceAutoConfiguration
这是一个分布式事务配置
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties(DataSourceProperties.class)
@ConditionalOnClass({ DataSource.class, TransactionManager.class, EmbeddedDatabaseType.class })
@ConditionalOnBean(XADataSourceWrapper.class)
@ConditionalOnMissingBean(DataSource.class)
public class XADataSourceAutoConfiguration implements BeanClassLoaderAware {}
2
3
4
5
6
7
因为我们不是一个分布式项目,所以没有这里的javax.transaction.TransactionManager,所以这个不会被导入
# 修改配置项
我们导入starter后,还需要配置数据源,不然启动会出现错误,会提示我们没有配置
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
2
3
4
5
6
7
8
9
10
11
12
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springdb
username: root
password: 123456
2
3
4
5
6
启动之后,我们就可以看到当前使用的数据源
# 整合druid数据源
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true)
static class Hikari {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
if (StringUtils.hasText(properties.getName())) {
dataSource.setPoolName(properties.getName());
}
return dataSource;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
因为默认使用的数据源是Hikari,但是使用有一个条件,@ConditionalOnMissingBean(DataSource.class),所以我们可以直接在容器中,加入我们自己的DataSource,这样就可以替代原来的数据源了,从而使用druid的数据源
@Configuration
public class DataSourceConf {
@Bean
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl();
dataSource.setPassword();
dataSource.setUsername();
}
}
但是我们可以将此配置和配置文件的spring.datasource前缀进行绑定,因为配置文件修改DataSource都和DruidDataSource对应有set方法
2
3
4
5
6
7
8
9
10
11
12
13
那么现在我们就已经成功替换到druid的数据源了
# 配置statviewservlet
druid提供了一个用于监控的页面,我们可以配置之后,进行使用
详细配置看druid官方https://github.com/alibaba/druid
@Configuration
public class DataSourceConf {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DruidDataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
try {
dataSource.setFilters("stat");
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return dataSource;
}
@Bean
public ServletRegistrationBean statViewServlet() {
StatViewServlet viewServlet = new StatViewServlet();
ServletRegistrationBean bean = new ServletRegistrationBean(viewServlet,"/druid/*");
bean.addInitParameter("loginUsername","chuchen");
bean.addInitParameter("loginPassword","123456");
return bean;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 使用druid的starter
依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
2
3
4
5
6
# 依赖分析
因为这是一个starter,所以我们可以直接分析这个依赖的自动配置DruidDataSourceAutoConfigure
@Configuration
@ConditionalOnClass(DruidDataSource.class)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
@Import({DruidSpringAopConfiguration.class,
DruidStatViewServletConfiguration.class,
DruidWebStatFilterConfiguration.class,
DruidFilterConfiguration.class})
public class DruidDataSourceAutoConfigure {}
2
3
4
5
6
7
8
9
需要在此@AutoConfigureBefore(DataSourceAutoConfiguration.class)加载之前,就加载此自动配置项,因为如何在DataSourceAutoConfiguration之后才加载的话,那么就会使用默认的数据源,不会使用druid的数据源
DruidSpringAopConfiguration.class
监控springbean的配置项
DruidStatViewServletConfiguration.class
和StatView监控页面相关
spring.datasource.druid.stat-view-servlet.enabled = true 开启监控页面
1druid: stat-view-servlet: enabled: true #开启监控页面 login-password: admin #登录密码 login-username: admin #登录用户名 reset-enable: true #启动重置按钮,true表示重置
1
2
3
4
5
6DruidWebStatFilterConfiguration.class
web监控页面
@ConditionalOnWebApplication @ConditionalOnProperty(name = "spring.datasource.druid.web-stat-filter.enabled", havingValue = "true") public class DruidWebStatFilterConfiguration {}
1
2
3DruidFilterConfiguration.class
所有Druid自己filter的配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/db_account
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
druid:
aop-patterns: com.atguigu.admin.* #监控SpringBean
filters: stat,wall # 底层开启功能,stat(sql监控),wall(防火墙)
stat-view-servlet: # 配置监控页功能
enabled: true
login-username: admin
login-password: admin
resetEnable: false
web-stat-filter: # 监控web
enabled: true
urlPattern: /*
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
filter:
stat: # 对上面filters里面的stat的详细配置
slow-sql-millis: 1000
logSlowSql: true
enabled: true
wall:
enabled: true
config:
drop-table-allow: false
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
# 整合mybatis
# 引入starter
https://github.com/mybatis/spring-boot-starter
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
2
3
4
5
# 依赖分析
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean {}
2
3
4
5
6
使用mybatis时,我们一般都会使用SqlSessionFactory,SqlSession
全局配置文件
SqlSessionFactory: 自动配置好了
@Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {}
1
2
3SqlSession:自动配置了 SqlSessionTemplate 组合了SqlSession
@Bean @ConditionalOnMissingBean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {}
1
2
3@Import(AutoConfiguredMapperScannerRegistrar.class);
Mapper: 只要我们写的操作MyBatis的接口标准了 @Mapper 就会被自动扫描进来
# 实例
我们引入mybatis的starter后,我们还需要写dao层,service层,还需要写一个mapper的映射文件
- dao
@Mapper
public interface StudentMapper {
public Student getStudentById(int id);
}
service
@Service
public class StudentSer {
@Autowired
StudentMapper mapper;
public Student getStudentById(int id) {
return mapper.getStudentById(id);
}
}
controller:
@RestController
public class StudentController {
@Autowired
StudentSer studentSer;
@GetMapping("/student")
public Student getStudent(@RequestParam("id") int id) {
Student studentById = studentSer.getStudentById(id);
return studentById;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
mapper
<?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="vin.cco.qsyyke.mapper.StudentMapper"> <select id="getStudentById" resultType="vin.cco.qsyyke.entity.Student" > select * from student where id = #{id} </select> </mapper>
1
2
3
4
5
6
7
8
9
我们可以只写mapper的映射文件,mybatis的主配置文件,可以不用写,因为mybatis的配置都已经配置好了,数据源,sqlsessionfactory,sqlsession等等,只需要映射文件就行
但是需要在配置文件中,执行映射文件的路径
修改映射文件路径
mybatis: mapper-locations: classpath:mybatis/mapper/StudentMapper.xml
1
2
# 修改驼峰命名
这个是因为,我们没有开启下面这项功能
mybatis:
mapper-locations: classpath:mybatis/mapper/StudentMapper.xml
configuration:
map-underscore-to-camel-case: true
2
3
4
# 注解方式
我们也可以不用xml文件,可以直接使用注解的方式写sql
@Insert("insert into student (name_id,age) values(#{nameId},#{age})")
public void insert(Student student);
2
因为表中的字段id是一个自增,我们不用写,但是这样的话,返回给我们的id字段就是0,可以使用一个注解解决
@Insert("insert into student (name_id,age) values(#{nameId},#{age})")
@Options(useGeneratedKeys = true,keyProperty = "id")
public void insert(Student student);
@PostMapping("/student")
public Student insert(Student student) {
studentSer.insert(student);
return student;
}
2
3
4
5
6
7
8
9
那么插入成功,返回的时候,就会将插入这条数据之后,id的值,赋值给student对象中的id字段
# 注解,xml混合方式
注解,xml混合方法就是,在一个mapper接口类中,其中的部分sql执行方法我们可以使用注解的方法,另外的方法可以使用xml的方式(需要在配置文件中,指明mapper.xml文件的位置),但是推荐一直使用xml方式,这样便于维护
# 技巧
因为我们每一个dao层中的接口类,都需要写一个@Mapper
注解,我们可以直接在主类上,写一个注解,@MapperScan("vin.cco.qsyyke")
,此注解值就是所有dao接口所在的包
# 整合mybatis-plus
# 依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
2
3
4
5
# 分析依赖
后面那些灰色是,省略的,也就是和我们pom文件相冲突的包
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisPlusProperties.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class})
public class MybatisPlusAutoConfiguration implements InitializingBean {}
2
3
4
5
6
SqlSessionFactory
@Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource){}
1
2
3SqlSession
@Bean @ConditionalOnMissingBean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {}
1
2
3
自动配置其实和mybatis一样
配置文件
private String[] mapperLocations = new String[]{"classpath*:/mapper/**/*.xml"};
1Locations of MyBatis mapper files.
也就是默认的mapper的映射文件是在,静态路径下的mapper下的所有文件夹中的xml问价都是mapper的映射路径
# 实例使用
官方文档快速开始 | MyBatis-Plus (baomidou.com) (opens new window)
public interface UserMapper extends BaseMapper<User> {
}
2
3
在BaseMapper<T>
接口中,定义许多基础的增删改查操作,我们可以不用自己写
因为我们没有写过sql,实例都是使用baseMapper,其规定,使用的数据表的表名就是和实体类名一样
,但是如果后期对数据表更新更改表名,需要使用@TableName("user")
该注解,指明该实体类对应的表名
# 报错
org.springframework.jdbc.BadSqlGrammarException:
### Error querying database. Cause: java.sql.SQLSyntaxErrorException: Unknown column 'username' in 'field list'
2
出现这个原因是因为,这两个字段在数据库中不存在造成的
使用mybatis-plus必须要保证实体类中的字段在对应数据表中,存在,但是可以使用解决
@TableField(exist = false)
private String username;
@TableField(exist = false)
private String password;
private Long id;
private String name;
private Integer age;
private String email;
2
3
4
5
6
7
8
9
10
@TableField(exist = false)加上此注解,就表明该字段在对应表中,不存在,就可以解决这个问题
解决之后,成功输出结果
# 基本crud
基本的使用,查看官网CRUD 接口 | MyBatis-Plus (baomidou.com) (opens new window)
# 整合Redis
# 引入starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2
3
4
# 分析依赖
如何使官方整合的starter,那么他的配置包,都是在下
# RedisAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {}
2
3
4
5
LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class是两个连接工厂,但是通过配置文件可以发现,默认是使用的是Lettuce
需要配置
@ConditionalOnProperty(name = "spring.redis.client-type", havingValue = "lettuce", matchIfMissing = true)
- LettuceConnectionConfiguration添加的bean
- DefaultClientResources
- LettuceConnectionFactory
# RedisRepositoriesAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(EnableRedisRepositories.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnProperty(prefix = "spring.data.redis.repositories", name = "enabled", havingValue = "true",
matchIfMissing = true)
@ConditionalOnMissingBean(RedisRepositoryFactoryBean.class)
@Import(RedisRepositoriesRegistrar.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisRepositoriesAutoConfiguration {
}
2
3
4
5
6
7
8
9
10
11
启动此配置需要配置spring.data.redis.repositories.enabled=true
# RedisReactiveAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ ReactiveRedisConnectionFactory.class, ReactiveRedisTemplate.class, Flux.class })
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisReactiveAutoConfiguration {}
2
3
4
在数据访问中,以xxTemplate
形式的对象,都是执行sql语句,比如JdbcTemplate,ReactiveRedisTemplate,ReactiveStringRedisTemplate
导入bean
ReactiveRedisTemplate<Object, Object>
因为Redis中,都是以key和vlue的形式存储,此template执行都是object类型
ReactiveStringRedisTemplate
键和值都是String类型的数据
# 操作Redis
在操作Redis之前,应该配置Redis的连接(这里使用阿里云的Redis)
spring:
redis:
host: redis1202.redis.rds.aliyuncs.com
username: qsyyke
password: qsyyke:Cqy19981202
2
3
4
5
因为下面两个组件都已经在容器中,所以我们可以直接从容器中拿到
@Autowired
StringRedisTemplate redisTemplate;
@Autowired
ReactiveRedisTemplate reactiveRedisTemplate;
2
3
4
5
可以使用下面的方式操作Redis
ValueOperations<String, String> ops = redisTemplate.opsForValue();
ops.set("chuchen","chuchen");
2
# 切换连接工厂
Type of client to use. By default, auto-detected according to the classpath.
private ClientType clientType;
2
从上面可以看出,Redis的默认连接工厂是根据环境变的,也就是如果只提供了lettuce依赖,那么就使用它,如果只提供了Jedis,那么就使用它,但是如果两个都提供,那么我们可以在配置文件中,进行选择,导入spring的redis-stadter,就会导入有lettuce,所以可以默认使用的是lettuce
jedis依赖
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.1</version>
</dependency>
2
3
4
5
6
← 指标监控 spring boot 注意点 →