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> </dependency>
<properties> <java.version>1.8</java.version> <mysql.version>5.1.49</mysql.version> </properties>
|
相关数据源配置类
DataSourceAutoConfiguration
: 数据源的自动配置。
- 修改数据源相关的配置:
spring.datasource
。
- 数据库连接池的配置,是自己容器中没有DataSource才自动配置的。
- 底层配置好的连接池是:
HikariDataSource
。
DataSourceTransactionManagerAutoConfiguration
: 事务管理器的自动配置。
JdbcTemplateAutoConfiguration
: JdbcTemplate
的自动配置,可以来对数据库进行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
|
单元测试数据源
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整合第三方技术的两种方式:
自定义方式
添加依赖:
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();
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; }
@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; }
@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-view-servlet: enabled: true login-username: admin login-password: admin reset-enable: false
web-stat-filter: enabled: true url-pattern: /* exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
aop-patterns: com.sirius.admin.*
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: config-location: classpath:mybatis/mybatis-config.xml mapper-locations: classpath:mybatis/*.xml
|
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> </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: mapper-locations: classpath:mybatis/mapper/*.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提供的IService
,ServiceImpl
,减轻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(); 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
| <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>
<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环境搭建
- 购买阿里云按量付费redis,经典网络
- 申请redis的公网链接地址
- 修改白名单,允许
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>
<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(); 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 {
@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
| @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"; }
|