SpringBootWeb开发-8

上一级页面:ssm-spring-boot速成学习索引

前言

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

Druid官网

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的监控页功能

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;
}

}

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

官方文档 - Druid Spring Boot 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
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";

配置示例

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
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

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

MyBatis的GitHub仓库

MyBatis官方

starter的命名方式

  1. SpringBoot官方的Starter:spring-boot-starter-*
  2. 第三方的: *-spring-boot-starter

引入依赖

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:
username: root
password: 1234
url: jdbc:mysql://localhost:3306/my
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
<?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.lun.boot.mapper.UserMapper">

<select id="getUser" resultType="com.lun.boot.bean.User">
select * from user where id=#{id}
</select>
</mapper>
1
2
3
4
5
6
7
import com.lun.boot.bean.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
public User getUser(Integer id);
}

POJO

1
2
3
4
5
6
public class User {
private Integer id;
private String name;

//getters and setters...
}

DB

1
2
3
4
5
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

Controller and Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
public class UserController {

@Autowired
private UserService userService;

@ResponseBody
@GetMapping("/user/{id}")
public User getUser(@PathVariable("id") Integer id){

return userService.getUser(id);
}

}
1
2
3
4
5
6
7
8
9
10
11
@Service
public class UserService {

@Autowired
private UserMapper userMapper;//IDEA下标红线,可忽视这红线

public User getUser(Integer id){
return userMapper.getUser(id);
}

}

配置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)。

64、数据访问-整合MyBatis-注解配置混合版

你可以通过Spring Initializr添加MyBatis的Starer。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Mapper
public interface UserMapper {
public User getUser(Integer id);

@Select("select * from user where id=#{id}")
public User getUser2(Integer id);

public void saveUser(User user);

@Insert("insert into user(`name`) values(#{name})")
@Options(useGeneratedKeys = true, keyProperty = "id")
public void saveUser2(User user);

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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.lun.boot.mapper.UserMapper">

<select id="getUser" resultType="com.lun.boot.bean.User">
select * from user where id=#{id}
</select>

<insert id="saveUser" useGeneratedKeys="true" keyProperty="id">
insert into user(`name`) values(#{name})
</insert>

</mapper>
  • 简单DAO方法就写在注解上。复杂的就写在配置文件里。

  • 使用@MapperScan("com.lun.boot.mapper") 简化,Mapper接口就可以不用标注@Mapper注解。

1
2
3
4
5
6
7
8
9
@MapperScan("com.lun.boot.mapper")
@SpringBootApplication
public class MainApplication {

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

}

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

IDEA的MyBatis的插件 - MyBatisX

MyBatisPlus官网

MyBatisPlus官方文档

MyBatisPlus是什么

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


添加依赖:

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

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

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

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

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

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

1
2
3
4
5
6
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lun.hellomybatisplus.model.User;

public interface UserMapper extends BaseMapper<User> {

}

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

官方文档 - CRUD接口

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

1
2
3
4
5
6
7
8
9
10
11
import com.lun.hellomybatisplus.model.User;
import com.baomidou.mybatisplus.extension.service.IService;

import java.util.List;

/**
* Service 的CRUD也不用写了
*/
public interface UserService extends IService<User> {
//此处故意为空
}
1
2
3
4
5
6
7
8
9
10
11
12
13
import com.lun.hellomybatisplus.model.User;
import com.lun.hellomybatisplus.mapper.UserMapper;
import com.lun.hellomybatisplus.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {
//此处故意为空
}

与下一节联合在一起

67、数据访问-CRUD实验-分页数据展示

与下一节联合在一起

68、数据访问-CRUD实验-删除用户完成

添加分页插件:

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
@Configuration
public class MyBatisConfig {


/**
* MybatisPlusInterceptor
* @return
*/
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join

//这是分页拦截器
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setOverflow(true);
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
44
<table class="display table table-bordered table-striped" id="dynamic-table">
<thead>
<tr>
<th>#</th>
<th>name</th>
<th>age</th>
<th>email</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr class="gradeX" th:each="user: ${users.records}">
<td th:text="${user.id}"></td>
<td>[[${user.name}]]</td>
<td th:text="${user.age}">Win 95+</td>
<td th:text="${user.email}">4</td>
<td>
<a th:href="@{/user/delete/{id}(id=${user.id},pn=${users.current})}"
class="btn btn-danger btn-sm" type="button">删除</a>
</td>
</tr>
</tfoot>
</table>

<div class="row-fluid">
<div class="span6">
<div class="dataTables_info" id="dynamic-table_info">
当前第[[${users.current}]]页 总计 [[${users.pages}]]页 共[[${users.total}]]条记录
</div>
</div>
<div class="span6">
<div class="dataTables_paginate paging_bootstrap pagination">
<ul>
<li class="prev disabled"><a href="#">← 前一页</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 class="next disabled"><a href="#">下一页 → </a></li>
</ul>
</div>
</div>
</div>

#numbers表示methods for formatting numeric objects.link

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
@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){
//表格内容的遍历

//从数据库中查出user表中的用户进行展示

//构造分页参数
Page<User> page = new Page<>(pn, 2);
//调用page进行分页
Page<User> userPage = userService.page(page, null);

model.addAttribute("users",userPage);

return "table/dynamic_table";
}

69、数据访问-准备阿里云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的配置。
  • 连接工厂LettuceConnectionConfigurationJedisConnectionConfiguration是准备好的。
  • 自动注入了RedisTemplate<Object, Object>xxxTemplate
  • 自动注入了StringRedisTemplate,key,value都是String
  • 底层只要我们使用StringRedisTemplateRedisTemplate就可以操作Redis。

外网Redis环境搭建

  1. 阿里云按量付费Redis,其中选择经典网络

  2. 申请Redis的公网连接地址。

  3. 修改白名单,允许0.0.0.0/0访问。

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

相关Redis配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
redis:
# url: redis://lfy:Lfy123456@r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.com:6379
host: r-bp1nc7reqesxisgxpipd.redis.rds.aliyuncs.com
port: 6379
password: lfy:Lfy123456
client-type: jedis
jedis:
pool:
max-active: 10
# lettuce:# 另一个用来连接redis的java框架
# pool:
# max-active: 10
# min-idle: 5

测试Redis连接:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@SpringBootTest
public class Boot05WebAdminApplicationTests {

@Autowired
StringRedisTemplate redisTemplate;


@Autowired
RedisConnectionFactory redisConnectionFactory;

@Test
void testRedis(){
ValueOperations<String, String> operations = redisTemplate.opsForValue();

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

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

System.out.println(redisConnectionFactory.getClass());
}

}

Redis Desktop Manager:可视化Redis管理软件。

URL统计拦截器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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();

//默认每次访问当前uri就会计数+1
redisTemplate.opsForValue().increment(uri);

return true;
}
}

注册URL统计拦截器:

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

@Autowired
RedisUrlCountInterceptor redisUrlCountInterceptor;


@Override
public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(redisUrlCountInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**",
"/js/**","/aa/**");
}
}

Filter、Interceptor 几乎拥有相同的功能?

  • Filter是Servlet定义的原生组件,它的好处是脱离Spring应用也能使用。

  • Interceptor是Spring定义的接口,可以使用Spring的自动装配等功能。

调用Redis内的统计数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Slf4j
@Controller
public class IndexController {

@Autowired
StringRedisTemplate redisTemplate;

@GetMapping("/main.html")
public String mainPage(HttpSession session,Model model){

log.info("当前方法是:{}","mainPage");

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";
}
}

SpringBootWeb开发-9

spring-boot-单元测试

参考、引用、致谢