6. Spring Boot 整合Mybatis
6.1 总步骤
- 在 pom 中导入三个依赖:MyBatis 与 Spring Boot 整合依赖、MySQL 驱动依赖、Druid 依赖
- 将 dao 目录注册为资源目录
- 在 Dao 接口上添加@Mapper 注解
- 在主配置文件中注册三类信息:映射文件、实体类别名、数据源
6.2 需求
完成一个简单的注册功能。
6.3 定义工程
复制 《内嵌tomcat使用JSP页面》中的06-jsp 工程,并重命名为 07-mybatis。
6.3.1 修改 pom 文件
导入三个依赖:mybatis 与 Spring Boot 整合依赖、mysql 驱动依赖与 Druid 数据源依赖。
<!--mybatis 与 spring boot 整合依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--mysql 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- druid 驱动 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
6.3.2 修改 SomeHandler
6.3.3 定义 Service 接口及实现类
(1) 定义 Service 接口
(2) 定义 Servivce 实现类
6.3.4 定义实体类及 DB 表
(1) 定义实体类
(2) 定义 DB 表
在 DB 的 test 数据库中定义 student 表。
6.3.5 定义 Dao 接口
Dao 接口上要添加@Mapper 注解。
6.3.6 定义映射文件
此时映射文件和Mapper接口在同一个包下,所以需要注册资源目录:
6.3.7 注册资源目录
在 pom 文件中将 dao 目录注册为资源目录。
<build>
<resources>
<!--注册 dao 包下 mybatis 映射文件为资源目录-->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
6.3.8 修改主配置文件
在主配置文件中主要完成以下几件工作:
- 注册映射文件
- 注册实体类别名
- 注册数据源
7. Spring Boot 整合事务
若工程直接或间接依赖于 spring-tx,则框架会自动注入 DataSourceTransactionManager事务管理器;若依赖于 spring-boot-data-jpa,则会自动注入 JpaTransactionManager。
7.1 定义工程
复制 07-mybatis 工程,并重命名为 08-transaction。
当前工程完成在对用户注册时一次插入到 DB 中两条注册信息。若在插入过程中有发生异常,则已完成插入的记录回滚。
7.2 修改启动类
这一步其实是非必须的
7.3 修改 Service 实现类
注解@Transactional:
/**
* Defines zero (0) or more exception {@link Class classes}, which must be
* subclasses of {@link Throwable}, indicating which exception types must cause
* a transaction rollback.
* 定义0(0)或多个异常{@link Class classes},这些异常必须是{@link Throwable}的子类,
* 指示哪些异常类型必须导致事务回滚。
* <p>By default, a transaction will be rolling back on {@link RuntimeException}
* and {@link Error} but not on checked exceptions (business exceptions). See
* {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)}
* for a detailed explanation.
* 默认情况下,事务将回滚{@link RuntimeException}和{@link Error},
* 但不会回滚已检查的异常(业务异常)。
*
* <p>This is the preferred way to construct a rollback rule (in contrast to
* {@link #rollbackForClassName}), matching the exception class and its subclasses.
* <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}.
* @see #rollbackForClassName
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)
*/
Class<? extends Throwable>[] rollbackFor() default {
};
如果@Transactional不指定rollbackFor属性,那么默认只会回滚运行时异常:
8. Spring Boot 整合日志
8.1 logback 日志技术介绍
Spring Boot 中使用的日志技术为 logback。其与 Log4J 都出自同一人,性能要优于 Log4J,是 Log4J 的替代者。
在 Spring Boot 中若要使用 logback,则需要具有 spring-boot-starter-logging 依赖,而该依赖被 spring-boot-starter-web 所依赖,即不用直接导入 spring-boot-starter-logging 依赖。
8.2 spring boot 中使用 logback
在 Spring Boot 中使用 logback 日志,有两种方式。
(1) 添加配置属性
只需在核心配置文件中添加如下配置即可。
(2) 添加配置文件
该文件名为 logback.xml,且必须要放在 src/main/resources 类路径下。
9. Spring Boot 整合Redis(SSRM 整合应用)
9.1 实现步骤
- 在 pom 中添加 Redis 与 Spring Boot 整合依赖
- 在配置文件中注册 Redis 连接信息
- 实体类实现序列化接口
在启动类上添加@EnableCaching
- 在查询方法上添加@Cacheble,在增删改方法上添加@CacheEvict
- 若使用 API 方式操作 Redis,则需要注入 RedisTemplate,然后通过 RedisTemplate 获取到Redis 操作对象后就可以对 Redis 进行操作了。
9.2 Redis 数据分类
使用 Redis 缓存的数据可以划分为两类:
- DB 更新后,Redis 缓存中的数据就要马上清除,以保证将来缓存中的数据与 DB 中的数据的绝对一致性,这是一类数据;
- 还有一类,对数据准确性要求不是很高,只要与 DB 中的数据差别不大就可以,所以这类数据一般会为其设置过期时效。
9.3 定义工程
复制 08-transaction 工程,并重命名为 09-redisCache。
当前工程完成让用户在页面中输入要查询学生的 id,其首先会查看 Redis 缓存中是否存在,若存在,则直接从 Redis 中读取;若不存在,则先从 DB 中查询出来,然后再存放到 Redis缓存中。但用户也可以通过页面注册学生,一旦有新的学生注册,则需要将缓存中的学生信息清空。根据id查询出的学生信息要求必须是实时性的,其适合使用注解方式的Redis缓存。
同时,通过页面还可以查看到总学生数,但对其要求是差不多就行,无需是实时性的。对于 Spring Boot 工程,其适合使用 API 方式的 Redis 缓存,该方式方便设置缓存的到期时限。
9.3.1 修改 pom 文件
在 pom 文件中添加 Spring Boot 与 Redis 整合依赖。
9.3.2 修改主配置文件
在主配置文件中添加如下内容:
9.3.3 修改实体类 Student
由于要将查询的实体类对象缓存到 Redis,Redis 要求实体类必须序列化。所以需要实体类实现序列化接口。
9.3.4 修改代码
(1) 修改 index 页面
(2) 修改 Controller 类
在其中添加两个处理器方法。
(3) 修改 Service 接口
在 Service 接口中添加一个业务方法。
(4) 修改 Service 接口实现类
Redis在高并发情况下可能会存在哪些问题?
1) 缓存穿透:当从DB中查询结果为null时有可能会引发缓存穿透问题。
其解决方案是为这些为null的结果赋予一个默认值2) 缓存雪崩:当缓存中的某些缓存在同一很短的时段内几乎同时到期,此时就可能会引发缓存雪崩问题。
其解决方案是,提前规划好系统中所有缓存的到期时间。3) 热点缓存:当某一个缓存的有效期到达时其可能会引发热点缓存问题。
其解决方案是,双重检测锁机制
(5) 修改 Dao 接口
在其中添加两个方法。
(6) 修改映射文件
可以使用简单类名作为别名。
9.3.5 启动 Redis
9.4 双重检测的线程安全问题
/**
* 通过当前类的getInstance()方法可以获取到一个单例的Student对象
* 注意:当前的代码与当前的应用程序没有任何关系
*
* 当前代码中存在线程安全问题:
* 解决方案有三种:
* 1)在方法签名上添加synchronized,使方法变为同步方法
* 2)在存在线程安全问题的成员变量声明前添加volatile
* 3)若存在线程安全问题的成员变量为Integer、Long、Boolean等,可以将它们
* 定义为AtomicXxx类型
*
*/
@Component // 当前类是单例的
public class StudentFactory {
private volatile Student student;
public Student getInstance() {
if(student == null) {
synchronized (this) {
if(student == null) {
// 以下语句的底层实现由三步构成:
// 1)申请堆空间m
// 2)使用对象的初始化数据初始化堆空间m
// 3)将student引用指向堆空间m
// 这三步的顺序没法保证一定是1->2->3
// 当然1肯定先执行,因为2和3都依赖1,但是2和3的顺序是不一定的
// 有可能出现 1->3->2,这个时候第一个线程执行到1->3的时候
// 另一个线程进来就会发现student!=null,直接返回student
// 但是student还没有初始化,此时student的属性是null和0
// 解决方案见类上面注释
student = new Student("张三", 33);
}
}
}
return student;
}
}