发布于 

SpringBoot数据访问

1、数据访问-数据库场景的自动配置分析与整合测试

导入JDBC场景

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

为什么导入JDBC场景,官方不导入驱动?

因为官方不知道我们接下来要操作什么数据库。

数据库版本要和驱动版本对应!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--默认版本:-->
<mysql.version>8.0.22</mysql.version>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!--<version>5.1.49</version>-->
</dependency>

<!--
想要修改版本
1、直接依赖引入具体版本(maven的就近依赖原则)
2、重新声明版本(maven的属性的就近优先原则)
-->
<properties>
<java.version>1.8</java.version>
<mysql.version>5.1.49</mysql.version>
</properties>

相关数据源配置类

  • DataSourceAutoConfiguration : 数据源的自动配置。
    • 修改数据源相关的配置:spring.datasource
    • 数据库连接池的配置,是自己容器中没有DataSource才自动配置的
    • 底层配置好的连接池是:HikariDataSource
  • DataSourceTransactionManagerAutoConfiguration: 事务管理器的自动配置。
  • JdbcTemplateAutoConfigurationJdbcTemplate的自动配置,可以来对数据库进行CRUD。
    • 可以修改前缀为spring.jdbc的配置项来修改JdbcTemplate
    • @Bean @Primary JdbcTemplate:Spring容器中有这个JdbcTemplate组件,使用@Autowired
  • JndiDataSourceAutoConfiguration: JNDI的自动配置。
  • XADataSourceAutoConfiguration: 分布式事务相关的。

修改配置项

1
2
3
4
5
6
7
spring:
datasource:
url: jdbc:mysql://localhost:3306/hotel?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
# type: com.zaxxer.hikari.HikariDataSource

单元测试数据源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Slf4j
@SpringBootTest
class BootWebAdminApplicationTests {

@Autowired
JdbcTemplate jdbcTemplate;

@Test
void contextLoads() {

Long aLong = jdbcTemplate.queryForObject("select count(*) from webmanager", Long.class);
log.info("记录总数:{}",aLong);
}

}

2、数据访问-自定义方式整合druid数据源

Druid是什么?

它是数据库连接池,它能够提供强大的监控和扩展功能。

Spring Boot整合第三方技术的两种方式:

  • 自定义
  • 找starter场景

自定义方式

添加依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.17</version>
</dependency>

配置Druid数据源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
public class MyConfig {

@Bean
@ConfigurationProperties("spring.datasource")//复用配置文件的数据源配置
public DataSource dataSource() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();

// druidDataSource.setUrl();
// druidDataSource.setUsername();
// druidDataSource.setPassword();

return druidDataSource;
}
}

配置Druid的监控页功能

  • Druid内置提供了一个StatViewServlet用于展示Druid的统计信息。官方文档 - 配置_StatViewServlet配置。这个StatViewServlet的用途包括:

    • 提供监控信息展示的html页面
    • 提供监控信息的JSON API
  • Druid内置提供一个StatFilter,用于统计监控信息。官方文档 - 配置_StatFilter

  • WebStatFilter用于采集web-jdbc关联监控的数据,如SQL监控、URI监控。官方文档 - 配置_配置WebStatFilter

  • Druid提供了WallFilter,它是基于SQL语义分析来实现防御SQL注入攻击的。官方文档 - 配置 wallfilter

1
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@Configuration
public class MyConfig {

@Bean
@ConfigurationProperties("spring.datasource")
public DataSource dataSource() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();

//加入监控和防火墙功能功能
druidDataSource.setFilters("stat,wall");

return druidDataSource;
}

/**
* 配置 druid的监控页功能
* @return
*/
@Bean
public ServletRegistrationBean statViewServlet(){
StatViewServlet statViewServlet = new StatViewServlet();
ServletRegistrationBean<StatViewServlet> registrationBean =
new ServletRegistrationBean<>(statViewServlet, "/druid/*");

//监控页账号密码:
registrationBean.addInitParameter("loginUsername","admin");
registrationBean.addInitParameter("loginPassword","123456");

return registrationBean;
}

/**
* WebStatFilter 用于采集web-jdbc关联监控的数据。
*/
@Bean
public FilterRegistrationBean webStatFilter(){
WebStatFilter webStatFilter = new WebStatFilter();

FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(webStatFilter);
filterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");

return filterRegistrationBean;
}

}

3、数据访问-druid数据源starter整合方式

引入依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.17</version>
</dependency>

分析自动配置

  • 扩展配置项 spring.datasource.druid
  • 自动配置类DruidDataSourceAutoConfigure
  • DruidSpringAopConfiguration.class, 监控SpringBean的;配置项:spring.datasource.druid.aop-patterns
  • DruidStatViewServletConfiguration.class, 监控页的配置。spring.datasource.druid.stat-view-servlet默认开启。
  • DruidWebStatFilterConfiguration.class,web监控配置。spring.datasource.druid.web-stat-filter默认开启。
  • DruidFilterConfiguration.class所有Druid的filter的配置:
1
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
spring:
datasource:
url: jdbc:mysql://localhost:3306/hotel?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver

druid:
filters: stat,wall #底层开启功能,stat(sql监控),wall(防火墙)
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/*'

aop-patterns: com.sirius.admin.* #监控SpringBean

filter:
stat:
slow-sql-millis: 1000
log-slow-sql: true
enabled: true

wall:
enabled: true

4、数据访问-整合MyBatis-配置版

引入依赖

1
2
3
4
5
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>

配置模式:

  • 全局配置文件

  • SqlSessionFactory:自动配置好了

  • SqlSession:自动配置了SqlSessionTemplate 组合了SqlSession

  • @Import(AutoConfiguredMapperScannerRegistrar.class)

  • Mapper: 只要我们写的操作MyBatis的接口标准了**@Mapper就会被自动扫描进来**

1
2
3
4
5
6
7
8
9
10
@EnableConfigurationProperties(MybatisProperties.class) : MyBatis配置项绑定类。
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration{
...
}

@ConfigurationProperties(prefix = "mybatis")
public class MybatisProperties{
...
}

配置文件

1
2
3
4
5
6
7
8
9
10
11
spring:
datasource:
url: jdbc:mysql://localhost:3306/hotel?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver

# 配置mybatis规则
mybatis:
config-location: classpath:mybatis/mybatis-config.xml #全局配置文件位置
mapper-locations: classpath:mybatis/*.xml #sql映射文件位置

mybatis-config.xml:

1
2
3
4
5
6
7
8
<?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>

<!-- 由于Spring Boot自动配置缘故,此处不必配置,只用来做做样。-->
</configuration>

Mapper接口

1
2
3
4
5
6
7
8
9
10
11
<?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.sirius.admin.mapper.AccountMapper">

<select id="getAccount" resultType="com.sirius.admin.bean.Account">
select * from account_tbl where id=#{id}
</select>

</mapper>

配置private Configuration configuration; 也就是配置mybatis.configuration相关的,就是相当于改mybatis全局配置文件中的值。(也就是说配置了mybatis.configuration,就不需配置mybatis全局配置文件了)

1
2
3
4
5
6
7
# 配置mybatis规则
mybatis:
mapper-locations: classpath:mybatis/mapper/*.xml
# 可以不写全局配置文件,所有全局配置文件的配置都放在configuration配置项中了。
# config-location: classpath:mybatis/mybatis-config.xml
configuration:
map-underscore-to-camel-case: true

小结:

  • 导入MyBatis官方Starter。
  • 编写Mapper接口,需@Mapper注解。
  • 编写SQL映射文件并绑定Mapper接口。
  • 在application.yaml中指定Mapper配置文件的所处位置,以及指定全局配置文件的信息 (建议:配置在mybatis.configuration)。

5、数据访问-整合MyBatis-注解版

注解与配置混合搭配,干活不累

1
2
3
4
5
6
7
8
9
10
11
12
import com.sirius.admin.bean.City;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface CityMapper {

@Select("select * from city where id=#{id}")
public City getById(Long id);

public void insert(City city);
}
1
2
3
4
5
6
7
8
9
10
11
<?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.sirius.admin.mapper.CityMapper">

<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into city('name','state','country') values(#{name},#{state},#{country})
</insert>

</mapper>
  • 简单DAO方法就写在注解上。复杂的就写在配置文件里。
  • 使用@MapperScan("com.lun.boot.mapper") 简化,Mapper接口就可以不用标注@Mapper注解。
1
2
3
4
5
6
7
8
9
@MapperScan("com.sirius.admin.mapper")
@SpringBootApplication
public class MainApplication {

public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}

}

6、数据访问-整合MyBatisPlus操作数据库

MyBatisPlus是什么

MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

创建数据库

1
2
3
4
5
6
7
8
9
10
DROP TABLE IF EXISTS user;

CREATE TABLE user
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);

添加数据

1
2
3
4
5
6
7
8
DELETE FROM user;

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

引入依赖

1
2
3
4
5
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
  • MybatisPlusAutoConfiguration配置类,MybatisPlusProperties配置项绑定。

  • SqlSessionFactory自动配置好,底层是容器中默认的数据源。

  • mapperLocations自动配置好的,有默认值classpath*:/mapper/**/*.xml,这表示任意包的类路径下的所有mapper文件夹下任意路径下的所有xml都是sql映射文件。 建议以后sql映射文件放在 mapper下。

  • 容器中也自动配置好了SqlSessionTemplate。

  • @Mapper 标注的接口也会被自动扫描,建议直接 @MapperScan(“com.sirius.admin.mapper”)批量扫描。

  • MyBatisPlus优点之一:只需要我们的Mapper继承MyBatisPlus的BaseMapper 就可以拥有CRUD能力,减轻开发工作。

7、数据访问-CRUD实验-数据列表展示

使用MyBatis Plus提供的IServiceServiceImpl,减轻Service层开发工作。

1
2
3
4
5
6
7
package com.sirius.admin.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.sirius.admin.bean.User;

public interface UserService extends IService<User> {
}
1
2
3
4
5
6
7
8
9
10
11
12
package com.sirius.admin.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sirius.admin.bean.User;
import com.sirius.admin.mapper.UserMapper;
import com.sirius.admin.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

}

添加分页插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.sirius.admin.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBatisConfig {

@Bean
public MybatisPlusInterceptor paginationInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//分页拦截器
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
//设置请求的页面大于最大页后操作,true调回首页,false继续请求,默认false
paginationInnerInterceptor.setOverflow(true);
//设置最大单页限制数量,默认500条,-1:不受限制
paginationInnerInterceptor.setMaxLimit(500L);
mybatisPlusInterceptor.addInnerInterceptor(paginationInnerInterceptor);
return mybatisPlusInterceptor;
}
}

前端

1
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
33
34
35
36
37
38
39
40
41
42
43
<table  class="display table table-bordered table-striped" id="dynamic-table">
<thead>
<tr>
<th>#</th>
<th>id</th>
<th>name</th>
<th>age</th>
<th>email</th>
<th>操作</th>
</tr>
</thead>
<tbody role="alert" aria-live="polite" aria-relevant="all">
<tr class="gradeX odd" th:each="user,stat:${page.records}">
<td th:text="${stat.count}"></td>
<td th:text="${user.id}"></td>
<td th:text="${user.name}"></td>
<td th:text="${user.age}"></td>
<td class="center hidden-phone">[[${user.email}]]</td>
<td>
<a th:href="@{/user/delete/{id}(id=${user.id},pn=${page.current})}" class="btn btn-danger btn-sm" type="button">删除</a>
</td>
</tr>
</tfoot>
</table>
<div class="row">
<div class="col-lg-6">
<div class="dataTables_info" id="editable-sample_info">当前第 [[${page.current}]] 页 总计 [[${page.pages}]] 页 共 [[${page.total}]] 条记录</div>
</div>
<div class="col-lg-6">
<div class="dataTables_paginate paging_bootstrap pagination">
<ul>
<li class="prev disabled">
<a href="#">← 前一页</a>
</li>
<li th:class="${num == page.current?'active':''}" th:each="num:${#numbers.sequence(1,page.pages)}">
<a th:href="@{/dynamic_table(pn=${num})}">[[${num}]]</a>
</li>
<li class="next"><a href="#">后一页 </a></li>
</ul>
</div>
</div>
</div>
</div>

控制层

1
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
@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 dynamic_table(@RequestParam(value = "pn",defaultValue = "1")Integer pn, Model model){

List<User> list = userService.list();

//分页查询数据
Page<User> userPage = new Page<>(pn, 2);

//分页查询的结果
Page<User> page = userService.page(userPage, null);

long current = page.getCurrent();
long pages = page.getPages();
long total = page.getTotal();
List<User> records = page.getRecords();
model.addAttribute("page",page);
return "table/dynamic_table";
}

8、数据访问-准备阿里云Redis环境

导入依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!--导入jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
  • RedisAutoConfiguration自动配置类,RedisProperties 属性类 –> spring.redis.xxx是对redis的配置。
  • 连接工厂LettuceConnectionConfiguration、JedisConnectionConfiguration是准备好的。
  • 自动注入了RedisTemplate<Object, Object>,xxxTemplate。
  • 自动注入了StringRedisTemplate,key,value都是String
  • 底层只要我们使用StringRedisTemplate、RedisTemplate就可以操作Redis。

redis环境搭建

  1. 购买阿里云按量付费redis,经典网络
  2. 申请redis的公网链接地址
  3. 修改白名单,允许0.0.0.0/0访问。

9、数据访问-Redis操作与统计小实验

redis相关配置

1
2
3
spring:  
redis:
url: redis://:1368921075Cpg@r-2vcxoq6x4u60k8tf2zpd.redis.cn-chengdu.rds.aliyuncs.com:6379
1
2
3
4
5
6
7
8
9
@Test
void testRedis(){
ValueOperations<String, String> operations = redisTemplate.opsForValue();

operations.set("hello","world");

String hello = operations.get("hello");
System.out.println(hello);
}

切换到jedis

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!--导入jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
1
2
3
4
spring:
redis:
url: redis://:1368921075Cpg@r-2vcxoq6x4u60k8tf2zpd.redis.cn-chengdu.rds.aliyuncs.com:6379
client-type: jedis

URL统计拦截器:

1
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
package com.sirius.admin.interceptor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class RedisUrlCountInterceptor implements HandlerInterceptor {

@Autowired
StringRedisTemplate redisTemplate;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

String uri = request.getRequestURI();
//默认每次访问当前url就会+1
redisTemplate.opsForValue().increment(uri);

return true;
}
}

注册URL统计拦截器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {

/**
* Filter、Interceptor几乎都有相同的功能,那么用哪个?
* 1、Filter是Servlet定义的原生组件。好处:脱离Spring应用也能使用
* 2、Interceptor是Spring定义的接口。可以使用Spring的自动装配等功能
*/
@Autowired
RedisUrlCountInterceptor redisUrlCountInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(redisUrlCountInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/","/login","/css/**","/js/**","/fonts/**","/images/**");
}
}

调用Redis内的统计数据:

1
2
3
4
5
6
7
8
9
10
11
//去main页面
@GetMapping("/main.html")
public String mainPage(HttpSession session,Model model){
ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
String s = opsForValue.get("/main.html");
String s1 = opsForValue.get("/sql");

model.addAttribute("mainCount",s);
model.addAttribute("sqlCount",s1);
return "main";
}

本站由 Cccccpg 使用 Stellar 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。