路还在继续,梦还在期许
导入 spring boot 官方 JDBC 启动场景。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jdbcartifactId>
dependency>
JDBC场景依赖分析

为什么springboot官方没有导入数据库驱动?
官方不知道接下要操作什么数据库。
mysql驱动版本
用户手动导入要操作的数据库驱动,官方会自动版本仲裁,用户也可以自己指定版本。
自动版本仲裁
<mysql.version>8.0.22mysql.version>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
用户自定义版本
<properties>
<java.version>1.8java.version>
<mysql.version>5.1.49mysql.version>
properties>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.49version>
dependency>
分析 spring boot 的 JDBC 启动场景自动配置哪些属性。
位置:org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")// 容器中没有这种组件才配置
@EnableConfigurationProperties(DataSourceProperties.class)// 开启配置文件绑定功能
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
}
配置文件修改数据源相关的配置的前缀:spring.datasource
数据库连接池的配置,是自己容器中没有 DataSource 才自动配置的
底层配置好的连接池是:HikariDataSource
@Configuration(proxyBeanMethods = false)
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {
}
位置:org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
Spring官方轻量级操作数据库工具,可以来对数据库进行crud。
位置:org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration
可以修改这个配置项@ConfigurationProperties(prefix = “spring.jdbc”) 来修改JdbcTemplate
@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;
}
如果数据源是在容器中配置的,可以使用jndi技术。
位置:org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration
JndiDataSourceAutoConfiguration: jndi的自动配置
位置:org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration
XADataSourceAutoConfiguration: 分布式事务相关的
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis?userUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT%2B8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
spring boot 给用户自动配好了JdbcTemplate,可以直接使用。
配置文件修改配置的前缀(prefix = “spring.jdbc”)
@Slf4j
@SpringBootTest
public class SpringBootTestApplicationTests {
private JdbcTemplate jdbcTemplate;
@Autowired
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Test
void contestLoads() {
Long aLong = jdbcTemplate.queryForObject("select count(*) from t_user", Long.class);
System.out.println(aLong);
log.info("记录总数:{}", aLong);
}
}
阿里的Druid数据源有对数据源的全套解决方案。
整合第三方技术的两种方式
方式一:全手动自定义方式。
方式二:导入官方的导入starter场景,改配置文件。
手动导入的方式,这里只是为了理解和学习,生产环境不会这样用,都是直接导入starter场景,改配置文件使用。
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.16version>
dependency>
使用spring的原生解决方案
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxActive" value="20" />
<property name="initialSize" value="1" />
<property name="maxWait" value="60000" />
<property name="minIdle" value="1" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="20" />
使用spring boot的解决方案
写一个自己的配置类,在java配置类的方法上标注注解:@ConfigurationProperties(“spring.datasource”),就可以引入yml配置文件属性。
@Configuration
public class MyDataSourceConfig {
// 主动向容器中添加DataSource,spring boot就不会在自动为容器添加数据源了
@ConfigurationProperties("spring.datasource")
@Bean
public DataSource dataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
return druidDataSource;
}
}
测试
@Slf4j
@SpringBootTest
class SpringBootDataApplicationTests {
private DataSource dataSource;
@Autowired
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
@Test
void testDataSource() {
log.info("数据源类型:{}",dataSource.getClass());
}
}
StatViewServlet的用途包括:
spring实现方式
<servlet>
<servlet-name>DruidStatViewservlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>DruidStatViewservlet-name>
<url-pattern>/druid/*url-pattern>
servlet-mapping>
spring boot配置方式
// 在容器中放servlet,配置druid的监控页功能
@Bean
public ServletRegistrationBean statViewServlet() {
StatViewServlet statViewServlet = new StatViewServlet();
ServletRegistrationBean<StatViewServlet> statViewServletServletRegistrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");
return statViewServletServletRegistrationBean;
}
用于统计监控信息:如SQL监控、URI监控
spring实现方式
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="filters" value="stat,slf4j" />
bean>
spring boot配置方式
@ConfigurationProperties("spring.datasource")
@Bean
public DataSource dataSource() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
//加入监控
druidDataSource.setFilters("stat");
return druidDataSource;
}
系统中所有filter:
| 别名 | Filter类名 |
|---|---|
| default | com.alibaba.druid.filter.stat.StatFilter |
| stat | com.alibaba.druid.filter.stat.StatFilter |
| mergeStat | com.alibaba.druid.filter.stat.MergeStatFilter |
| encoding | com.alibaba.druid.filter.encoding.EncodingConvertFilter |
| log4j | com.alibaba.druid.filter.logging.Log4jFilter |
| log4j2 | com.alibaba.druid.filter.logging.Log4j2Filter |
| slf4j | com.alibaba.druid.filter.logging.Slf4jLogFilter |
| commonlogging | com.alibaba.druid.filter.logging.CommonsLogFilter |
慢SQL记录配置
<bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter">
<property name="slowSqlMillis" value="10000" />
<property name="logSlowSql" value="true" />
bean>
WebStatFilter用于采集web-jdbc关联监控的数据。
spring实现方式
<filter>
<filter-name>DruidWebStatFilterfilter-name>
<filter-class>com.alibaba.druid.support.http.WebStatFilterfilter-class>
<init-param>
<param-name>exclusionsparam-name>
<param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*param-value>
init-param>
filter>
<filter-mapping>
<filter-name>DruidWebStatFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
spring boot配置方式
/**
* WebStatFilter用于采集web-jabc关联监控的数据
*/
@Bean
public FilterRegistrationBean webStatFilter() {
WebStatFilter webStatFilter = new WebStatFilter();
FilterRegistrationBean<WebStatFilter> webStatFilterFilterRegistrationBean = new FilterRegistrationBean<>(webStatFilter);
webStatFilterFilterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
// 需要排除的拦截路径
webStatFilterFilterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return webStatFilterFilterRegistrationBean;
}
spring实现方式
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
...
<property name="filters" value="wall"/>
bean>
spring boot配置方式
// 主动向容器中添加DataSource,spring boot就不会在自动为容器添加数据源了
@ConfigurationProperties("spring.datasource")
@Bean
public DataSource dataSource() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
//加入监控,加入防火墙
druidDataSource.setFilters("stat,wall");
return druidDataSource;
}
spring实现方式
<servlet>
<servlet-name>DruidStatViewservlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServletservlet-class>
<init-param>
<param-name>resetEnableparam-name>
<param-value>trueparam-value>
init-param>
<init-param>
<param-name>loginUsernameparam-name>
<param-value>druidparam-value>
init-param>
<init-param>
<param-name>loginPasswordparam-name>
<param-value>druidparam-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>DruidStatViewservlet-name>
<url-pattern>/druid/*url-pattern>
servlet-mapping>
spring boot配置方式
// 在容器中放servlet,配置druid的监控页功能
@Bean
public ServletRegistrationBean statViewServlet() {
StatViewServlet statViewServlet = new StatViewServlet();
ServletRegistrationBean<StatViewServlet> statViewServletServletRegistrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");
// 添加初始化参数,账号和密码
statViewServletServletRegistrationBean.addInitParameter("loginUsername","admin");
statViewServletServletRegistrationBean.addInitParameter("loginPassword","123456");
return statViewServletServletRegistrationBean;
}
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.17version>
dependency>
private static final String FILTER_STAT_PREFIX = "spring.datasource.druid.filter.stat";
private static final String FILTER_CONFIG_PREFIX = "spring.datasource.druid.filter.config";
private static final String FILTER_ENCODING_PREFIX = "spring.datasource.druid.filter.encoding";
private static final String FILTER_SLF4J_PREFIX = "spring.datasource.druid.filter.slf4j";
private static final String FILTER_LOG4J_PREFIX = "spring.datasource.druid.filter.log4j";
private static final String FILTER_LOG4J2_PREFIX = "spring.datasource.druid.filter.log4j2";
private static final String FILTER_COMMONS_LOG_PREFIX = "spring.datasource.druid.filter.commons-log";
private static final String FILTER_WALL_PREFIX = "spring.datasource.druid.filter.wall";
注意:配置文件包含中文注释有可能项目启动会报错,报错就删除中文注释
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis?userUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT%2B8
username: root
password: root
type: com.zaxxer.hikari.HikariDataSource
druid: # 与druid相关的配置
aop-patterns: pers.tianyu.spring_boot_data.* # 监控指定包下所有组件
filters: stat,wall # 开启底层功能,stat(sql监控),wall(防火墙)
filter: # 对上面filters里面的stat的详细配置
stat:
enabled: true # 开启
slow-sql-millis: 1000 # 慢查询时间
log-slow-sql: true # 日志记录慢查询
wall:
enabled: true # 开启
stat-view-servlet: # 配置监控页功能
enabled: true # 开启
login-username: admin # 用户名
login-password: admin # 密码
reset-enable: false # 不可重置
web-stat-filter: # 监控web
enabled: true # 开启
url-pattern: /* # 监控路径
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*' # 排除路径
使用优先找到starter场景,SpringBoot官方的Starter场景命名:spring-boot-starter- *,第三方的Starter场景: *-spring-boot-starter。
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.4version>
dependency>

spring实现方式mybatis
spring boot实现方式mybatis
mybatis自动配置类
@EnableConfigurationProperties(MybatisProperties.class) // MyBatis配置项绑定类。
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration{}
进入MyBatis配置项绑定类
@ConfigurationProperties(prefix = "mybatis")
public class MybatisProperties
配置文件前缀: mybatis 开始,可以修改所有mybatis配置。
# 配置mybatis规则
mybatis:
config-location: classpath:mybatis/mybatis-config.xml #全局配置文件位置
mapper-locations: classpath:mybatis/mapper/*.xml #sql映射文件位置
@Mapper
public interface AccountMapper {
public Account getAcct(@Param("id") Long id);
}
Mapper接口—>绑定Xml
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="pers.tianyu.spring_boot_data.mapper.AccountMapper">
<select id="getAcct" resultType="account">
select id, user_id, money
from account_tbl
where id = #{id};
select>
mapper>
配置 private Configuration configuration;
mybatis.configuration下面的所有,就是相当于改mybatis全局配置文件中的值
# 配置mybatis规则
mybatis:
# config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
configuration: # 指定mybatis全区配置文件中的相关配置项
map-underscore-to-camel-case: true
可以不写全局;配置文件,所有全局配置文件的配置都放在configuration配置项中即可
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
@Mapper
public interface CityMapper {
@Insert("INSERT INTO city(name,state,country) VALUES(#{name},#{state},#{country})")
@Options(useGeneratedKeys = true,keyProperty = "id")
void insert(City city);
@Select("SELECT id, name, state, country FROM city WHERE id = #{id}")
City findByid(Long id);
}
@Mapper
public interface CityMapper {
@Select("select * from city where id=#{id}")
public City getById(Long id);
// 这个方法写在mapper.xml文件里
public void insert(City city);
}
最佳实战:
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
建议安装 MybatisX 插件
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.1version>
dependency>
优点:
注意:
删除
<td>
<a th:href="@{/user/delete/{id}(id=${user.id},pn=${users.current})}"
class="btn btn-danger btn-sm" type="button">删除a>
td>
导航栏
<div class="span6">
<div class="dataTables_paginate paging_bootstrap pagination">
<ul>
<li th:if="${users.hasPrevious eq true}"><a th:href="@{/dynamic_table(pn=${users.getCurrent - 1})}">← 前一页a>li>
<li th:class="${num == users.current?'active':''}"
th:each="num:${#numbers.sequence(1,users.pages)}">
<a th:href="@{/dynamic_table(pn=${num})}">[[${num}]]a>
li>
<li th:if="${users.hasNext eq true}"><a th:href="@{/dynamic_table(pn=${users.getCurrent + 1})}">下一页 → a>li>
ul>
div>
div>
@GetMapping("/user/delete/{id}")
public String deleteUser(@PathVariable("id") Long id,
@RequestParam(value = "pn", defaultValue = "1") Integer pn,
RedirectAttributes ra) {
userService.removeById(id);
ra.addAttribute("pn", pn);
return "redirect:/dynamic_table";
}
@GetMapping("/dynamic_table")
public String dynamicTable(@RequestParam(value = "pn", defaultValue = "1") Integer pn, Model model) {
// 查询数据库数据
List<User> users = userService.list();
// 分页查询数据
Page<User> userPage = new Page<>(pn, 5);
// 分页查询结果
Page<User> page = userService.page(userPage, null);
// 存入域对象
model.addAttribute("users", page);
return "table/dynamic_table";
}
实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
接口
public interface UserService extends IService<User> {
}
public interface UserMapper extends BaseMapper<User> {
}
@Configuration
public class MybatisPlusConfig{
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置
* MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 向spring boot注入分页拦截器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页拦截器
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
// 设置请求的页面大于最大页后操作,true调回首页,false继续请求,默认false
paginationInnerInterceptor.setOverflow(true);
// 设置最大单页限制数量,默认500条,-1 不受限制
paginationInnerInterceptor.setMaxLimit(500L);
// 添加拦截器
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseDeprecatedExecutor(false);
}
}
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。
它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。
Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
引入redis开发场景
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>

位置:org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration 自动配置类。
RedisProperties 属性类 --> spring.redis.xxx是对redis的配置。
连接工厂是准备好的,LettuceConnectionConfiguration、JedisConnectionConfiguration。
自动注入了RedisTemplate
自动注入了StringRedisTemplate;默认 k:v都是String
底层只要我们使用 StringRedisTemplate、RedisTemplate就可以操作redis
1、阿里云按量付费redis
2、申请redis的公网连接地址
3、修改白名单 允许0.0.0.0/0 访问或写自己的公网ip
配置文件
spring:
redis:
host: r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.com
port: 6379
password: lfy:Lfy123456 # 默认账号写password,创建的账号写 user:password
在使用 RedisTemplate 的时候,默认使用的是 Lettuce 客户端。
测试
@Test
public void testRedis() {
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
operations.set("hello","world");
String hello = operations.get("hello");
System.out.println(hello);
}
不想使用 Lettuce 客户端,可以切换到 Jedis 客户端。
切换到 Jedis 客户端,只需要导入 jedis 的jar 包就可以,spring boot会自动判断,存在jar包,就自动切换。
依赖坐标
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
dependency>
配置文件
spring:
redis:
host: r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.com
port: 6379
password: lfy:Lfy123456 # 默认账号写password,创建的账号写 user:password
clientType: jedis # 使用jedis连接工厂\lettuce
jedis:
pool:
max-active: 10 # 最大线程数
测试
@Test
public void testRedisConnectionFactory(){
System.out.println(redisConnectionFactory.getClass());
}
@Component
public class RedisUrlCountInterceptor implements HandlerInterceptor {
private StringRedisTemplate stringRedisTemplate;
@Autowired
public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String uri = request.getRequestURI();
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
// 默认 +1
operations.increment(uri);
return true;
}
}
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
private RedisUrlCountInterceptor redisUrlCountInterceptor;
@Autowired
public void setRedisUrlCountInterceptor(RedisUrlCountInterceptor redisUrlCountInterceptor) {
this.redisUrlCountInterceptor = redisUrlCountInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加登录拦截
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") // 拦截所有请求
.excludePathPatterns("/", "/login", "/css/**", "/fonts/**", "/images/**", "/js/**"); // 放行的请求
// 添加AIP调用统计拦截
registry.addInterceptor(redisUrlCountInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/", "/login", "/css/**", "/fonts/**", "/images/**", "/js/**");
}
}
// 去main页
@GetMapping("/main.html")
public String mainPage(Model model) {
log.info("当前方法是:{}", "mainPage");
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
String mainNum = operations.get("/main.html");
String sqlNum = operations.get("/sql");
model.addAttribute("mainNum", mainNum);
model.addAttribute("sqlNum", sqlNum);
return "main";
}