四时宝库

程序员的知识宝库

SpringBoot进阶学习下篇(springboot从入门到进阶系列官方小册)

4、SpringBoot整合MyBatis、Jpa、Redis

4.1、整合MyBatis

POM

<dependencies> 
    <!--lombok插件-->  
 <dependency> 
 <groupId>org.projectlombok</groupId> 
 <artifactId>lombok</artifactId> 
 </dependency> 
 
    <!--整合MyBatis依赖-->  
 <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> 
 <scope>runtime</scope> 
 </dependency> 
 
    <!--测试相关的依赖包-->  
 <dependency> 
 <groupId>org.springframework.boot</groupId> 
 <artifactId>spring-boot-starter-test</artifactId> 
 <scope>test</scope> 
 <exclusions> 
 <exclusion> 
 <groupId>org.junit.vintage</groupId> 
 <artifactId>junit-vintage-engine</artifactId> 
 </exclusion> 
 </exclusions> 
 </dependency> 
 <dependency> 
 <groupId>junit</groupId> 
 <artifactId>junit</artifactId> 
 <scope>test</scope> 
 </dependency> 
</dependencies> 

配置

# Mysql数据库连接配置 : com.mysql.cj.jdbc.Driver  
spring.datasource.url=jdbc:mysql://localhost:3306/demo?serverTimezone=UTC  
spring.datasource.username=root  
spring.datasource.password=root  
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver  
 
#开启驼峰命名匹配映射  
mybatis.configuration.map-underscore-to-camel-case=true  
 
#配置mybatis的xml映射配置文件路径  
mybatis.mapper-locations=classpath:mapper/*.xml  
 
#配置mybatis映射配置文件中实体类别名  
mybatis.type-aliases-package=com.springboot.demo.pojo  

启动类

@SpringBootApplication  
@MapperScan("com.springboot.demo.mapper")  
public class Application {  
 public static void main(String[] args) {  
        SpringApplication.run(Application.class,args);  
    }  
}  

Pojo以及Mapper

@Data  
public class Person {  
 private Integer id;  
 private String name;  
 private Integer petId;  
}
 
@Mapper  
public interface PersonMapper {  
 
    @Select("select * from person where id = #{id}")  
    Person getById(Integer id);  
 
    Person selectById(Integer id);  
} 
 为了演示注解和xml方式都进行了配置
<?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.springboot.demo._01mybatisDemo.mapper.PersonMapper"> 
 <select id="selectById" resultType="com.springboot.demo._01mybatisDemo.pojo.Person"> 
        select * from person where id =#{id}  
 </select> 
</mapper> 

总结 :在使用Mapper接口的时候可以在接口上贴@Mapper或者在启动类上贴@MapperScan。

4.2整合JPA

POM

<dependencies> 
    <!--lombok插件-->  
 <dependency> 
 <groupId>org.projectlombok</groupId> 
 <artifactId>lombok</artifactId> 
 </dependency> 
    <!--SpringBoot整合jpa的包-->  
 <dependency> 
 <groupId>org.springframework.boot</groupId> 
 <artifactId>spring-boot-starter-data-jpa</artifactId> 
 </dependency> 
 
    <!--mysql驱动注册-->  
 <dependency> 
 <groupId>mysql</groupId> 
 <artifactId>mysql-connector-java</artifactId> 
 <scope>runtime</scope> 
 </dependency> 
 
    <!--测试相关的依赖包-->  
 <dependency> 
 <groupId>org.springframework.boot</groupId> 
 <artifactId>spring-boot-starter-test</artifactId> 
 <scope>test</scope> 
 <exclusions> 
 <exclusion> 
 <groupId>org.junit.vintage</groupId> 
 <artifactId>junit-vintage-engine</artifactId> 
 </exclusion> 
 </exclusions> 
 </dependency> 
 <dependency> 
 <groupId>junit</groupId> 
 <artifactId>junit</artifactId> 
 <scope>test</scope> 
 </dependency> 
 
</dependencies> 

配置

# Mysql数据库连接配置 : com.mysql.cj.jdbc.Driver  
spring.datasource.url=jdbc:mysql://localhost:3306/demo?serverTimezone=UTC  
spring.datasource.username=root  
spring.datasource.password=admin  
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver  

Pojo以及接口

@Data  
@Entity  
@Table(name = "person")  
public class Person {  
    @Id //表明映射主键id  
    @GeneratedValue(strategy = GenerationType.IDENTITY)  
 private Integer id;  
    @Column  
 private String name;  
    @Column(name = "pet_id")  
 private Integer petId;  
}  
public interface PersonRepository extends CrudRepository<Person,Integer> {  
 
}

4.3整合Redis

POM

<dependencies> 
    <!--lombok插件-->  
 <dependency> 
 <groupId>org.projectlombok</groupId> 
 <artifactId>lombok</artifactId> 
 </dependency> 
 
 
    <!--redis整合包-->  
 <dependency> 
 <groupId>org.springframework.boot</groupId> 
 <artifactId>spring-boot-starter-data-redis</artifactId> 
 </dependency> 
 
 
    <!--测试相关的依赖包-->  
 <dependency> 
 <groupId>org.springframework.boot</groupId> 
 <artifactId>spring-boot-starter-test</artifactId> 
 <scope>test</scope> 
 <exclusions> 
 <exclusion> 
 <groupId>org.junit.vintage</groupId> 
 <artifactId>junit-vintage-engine</artifactId> 
 </exclusion> 
 </exclusions> 
 </dependency> 
 <dependency> 
 <groupId>junit</groupId> 
 <artifactId>junit</artifactId> 
 <scope>test</scope> 
 </dependency> 
 <dependency> 
 <groupId>com.alibaba</groupId> 
 <artifactId>fastjson</artifactId> 
 <version>1.2.74</version> 
 </dependency> 
</dependencies> 

配置

  1. #redis服务器地址
  2. spring.redis.host=127.0.0.1
  3. #redis服务器连接端口
  4. spring.redis.port=6379
  5. #redis服务器连接密码
  6. spring.redis.password=

使用

方式1通过CRUD的方式使用

@AllArgsConstructor  
@NoArgsConstructor  
@Data  
//指定存储在redis中的数据类型  
@RedisHash(value = "dog")  
public class Dog {  
    @Id // 用来标识实体类主键  字符串形式的hashkey标识唯一的实体类对象id  
 private String id;  
    @Indexed//生成二级索引  
 private String name;  
} 
public interface DogRepository extends CrudRepository<Dog,String> {  
    List<Dog> findByName(String name);  
} 
测试
 @Autowired  
private DogRepository dogRepository;  
@Test  
public void testDogRepository() throws Exception {  
    dogRepository.save(new Dog("dog:1","旺财"));  
    List<Dog> dogs = dogRepository.findByName("旺财");  
    System.out.println(dogs);  
}  

这种是将Redis作为DB来使用不推荐使用。


方式二

@Autowired  
private RedisTemplate<String,String> redisTemplate;  
@Test  
public void testRedisTemplate() throws Exception {  
    redisTemplate.opsForValue().set("helloDog", JSONObject.toJSONString(new Dog("1","2")));  
    String helloDog = redisTemplate.opsForValue().get("helloDog");  
    Dog dog = JSONObject.parseObject(helloDog, Dog.class);  
    System.out.println(dog);  
}  

5、SprinBoot整合Thymeleaf模版引擎

5.1 SpringBoot对模板的支持

SpringBoot框架支持很多种模板,(FreeMarker、Thymeleaf、Mustache等),大大方便了前端的开发。

SpringBoot对Jsp不太支持,并且官方没有提供整合的配置,原因:

1、SpringBoot使用的是嵌入式Servlet web容器,默认使用jar的打包方式,jar包本身不支持jsp模板。

2、如果使用Undertow嵌入式容器部署SpringBoot项目的话,不支持Jsp模板。

3、SpringBoot默认提供了一个处理请求路径"/error"的统一错误处理器,来返回具体的异常信息,如果使用jsp模板的话无法对默认的错误信息进行覆盖,只能在指定的位置指定错误页面。

5.2 Thymeleaf模板引擎技术

5.2.1 Thymeleaf介绍

Thymeleaf是一种基于服务器端的java模板引擎技术,也是一个优秀的面向Java的XML、XHTML、HTML5页面模板,它具有丰富的标签语言、函数和表达式,在使用Spring Boot框架进行页面设计时,一般会选择Thymeleaf模板。

5.2.2Thymeleaf语法

在HTML页面上使用Thymeleaf标签,Thymeleaf 标签能够动态地替换掉静态内容,使页面动态展示。

常用的标签

th:标签说明th:insert布局标签,替换内容到引入的文件th:replace页面片段包含(类似JSP中的include标签)th:each元素遍历(类似JSP中的c:forEach标签)th:if条件判断,如果为真th:unless条件判断,如果为假th:switch条件判断,进行选择性匹配th:case条件判断,进行选择性匹配th:value属性值修改,指定标签属性值th:href用于设定链接地址th:src用于设定链接地址th:text用于指定标签显示的文本内容

标准表达式

1、变量表达式 ${...}

来获取到上下文中变量的值。

Demo:<p th:text="${title}">这是标题</p>

Thymeleaf内置的上下文对象有:

ctx上下文对象vars上下文变量locale上下文区域设置request(仅限Web Context)HttpServletRequest对象response(仅限Web Context)HttpServletResponse对象session(仅限Web Context)HttpSession对象servletContext(仅限Web Context)ServletContext对象

2、选择变量表达式 *{...}

也是获取到上下问的变量只不过是从业务对象中获取

Demo:

<div th:object="${book}"> <p>titile: <span th:text="*{title}">标题</span>.</p></div>


3、消息表达式 #{...}

消息表达式#{...}主要用于Thymeleaf模板页面国际化内容的动态替换和展示,使用消息表达式#

{...}进行国际化设置时,还需要提供一些国际化配置文件。后边会说明


4、链接表达式 @{...}

链接表达式@{...}一般用于页面跳转或者资源的引入,在Web开发中占据着非常重要的地位,并且使用

也非常频繁,Demo:

<a th:href="@{http://localhost:8080/order/details(orderId=${o.id})}">view</a><a th:href="@{/order/details(orderId=${o.id})}">view</a>


5、片段表达式 ~{...}

片段表达式~{...}用来标记一个片段模板,并根据需要移动或传递给其他模板。其中,最常见的用法是使用th:insert或th:replace属性插入片段。

<div th:insert="~{thymeleafDemo::title}"></div>


thymeleaf Demo

<!DOCTYPE html> 
<html lang="en" xmlns:th="http://www.thymeleaf.org"> 
<head> 
 <meta charset="UTF-8"> 
 <link rel="stylesheet" type="text/css" media="all"  
     href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" /> 
 <title>Title</title> 
</head> 
<body> 
 <p th:text="${hello}">HelloWorld</p> 
</body> 
</html> 

5.2.3Thymeleaf使用Demo

POM

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

配置


#模板缓存
spring.thymeleaf.cache = true
#模板编码
spring.thymeleaf.encoding=utf-8
#应用于模板的模板模式
spring.thymeleaf.mode = HTML5
#指定模板页面存放路径
spring.thymeleaf.prefix = classpath:/templates/
#指定模板页面名称的后缀
spring.thymeleaf.suffix = .html

#配置国际化文件基础名
spring.messages.basename=i18n.login


代码

@Controller
public class LoginController {
    @RequestMapping("/toLoginPage")
    public String toLoginPage(Model model){
        model.addAttribute("currentYear", Calendar.getInstance().get(Calendar.YEAR));
        return "login";
    }
}


HTML

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1,shrink-to-fit=no">
    <title>用户登录界面</title>
    <link th:href="@{/login/css/bootstrap.min.css}" rel="stylesheet">
    <link th:href="@{/login/css/signin.css}" rel="stylesheet">
</head>
<body class="text-center">
<!--  用户登录form表单 -->
<form class="form-signin">
    <img class="mb-4" th:src="@{/login/img/login.jpg}" width="72" height="72">
    <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">请登录</h1>
    <input type="text" class="form-control"
           th:placeholder="#{login.username}" required="" autofocus="">
    <input type="password" class="form-control"
           th:placeholder="#{login.password}" required="">
    <div class="checkbox mb-3">
        <label>
            <input type="checkbox" value="remember-me"> [[#{login.rememberme}]]
        </label>
    </div>
    <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.button}">登录</button>
    <p class="mt-5 mb-3 text-muted">? <span th:text="${currentYear}">2019</span>-<span th:text="${currentYear}+1">2020</span></p>
    <a class="btn btn-sm" th:href="@{/toLoginPage(l='zh_CN')}">中文</a>
    <a class="btn btn-sm" th:href="@{/toLoginPage(l='en_US')}">English</a>
</form>
</body>
</html>


5.2.4Thymeleaf设置本地化语言

	首先需要在resources目录下创建一个文件夹来存放默认的和翻译好的语言
login.properties,login_zh_CN.properties 【默认和中文的都一样】
login.tip=请登录
login.username=用户名
login.password=密码
login.button=登录
login.rememberme=记住我

login_en_US.properties【英文】
login.tip=please sign in
login.username=username
login.password=password
login.button=login
login.rememberme=remmeber me


配置

#配置国际化文件基础名
spring.messages.basename=language.login


Html中修改

<form class="form-signin">
    <img class="mb-4" th:src="@{/login/img/login.jpg}" width="72" height="72">
    <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">请登录</h1>
    <input type="text" class="form-control"
           th:placeholder="#{login.username}" required="" autofocus="">
    <input type="password" class="form-control"
           th:placeholder="#{login.password}" required="">
    <div class="checkbox mb-3">
        <label>
            <input type="checkbox" value="remember-me"> [[#{login.rememberme}]]
        </label>
    </div>
    <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.button}">登录</button>
    <p class="mt-5 mb-3 text-muted">? <span th:text="${currentYear}">2019</span>-<span th:text="${currentYear}+1">2020</span></p>
    <!-- 设置两个超连接传入参数每次点击的时候发送超连接进行刷新 -->
    <a class="btn btn-sm" th:href="@{/toLoginPage(setLanguage='zh_CN')}">中文</a>
    <a class="btn btn-sm" th:href="@{/toLoginPage(setLanguage='en_US')}">English</a>
</form>
</body>
</html>


设置本地化解析器

public class LanguageLocaleResolver implements LocaleResolver {
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        String setLanguage = request.getParameter("setLanguage");
        Locale locale;
        if (!StringUtils.isEmpty(setLanguage)) {
            String[] s = setLanguage.split("_");
            locale = new Locale(s[0],s[1]);
        }else{//为空的情况
            String header = request.getHeader("Accept-Language");
            String[] split = header.split(",");
            String[] split1 = split[0].split("-");
            locale = new Locale(split1[0],split1[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
    }
}


配置到Spring容器中进行覆盖 一定要是localeResolver才行

@Bean
public LocaleResolver localeResolver(){
    return new LanguageLocaleResolver();
}

总结:在进行本地化配置的时候一定要注意配置文件的命名 文件名_语言_国家

5.3SpringBoot缓存

SpringBoot支持很多缓存组件:


  1. Generic
  2. JCache (JSR-107) (EhCache 3、Hazelcast、Infinispan等)
  3. EhCache 2.x
  4. Hazelcast
  5. Infinispan
  6. Couchbase
  7. Redis
  8. Caffeine
  9. Simple


添加@EnableCaching之后默认会从这个9个缓存组件中去查找,没有的话默认使用Simple缓存组件。

5.3.1默认缓存使用

使用方法

在启动类上贴上@EnableCaching,在对应的service查询方法上贴上@Cacheable(cacheNames = "name")来开启缓存。

@SpringBootApplication
@EnableCaching
public class CacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(CacheApplication.class, args);
    }
}


//根据id查询person对象爱ing
@Cacheable(cacheNames = "person")
public Person findById(Integer id){
    Optional<Person> op = personRepository.findById(id);
    return op.isPresent() ? op.get() : null;
}


@Cacheable 注解

属性名说明value/cacheNames指定缓存空间的名称,必配属性。这两个属性二选一使用key指定缓存数据的key,默认使用方法参数值,可以使用SpEL表达式SpEL表达式:#root.mathodName ->获取当前方法名#root.mathod ->当前被调用的方法 #root.target ->当前被调用的目标对象#root.targetClass ->当前被调用的目标对象类#root.args[0] 第一个参数, #root.args[1] 第二个参数... ->当前被调用的方法的参数列表#参数名, 也可以使用 #p0 #a0 0是参数的下标索引 ->根据参数名字取出值#result ->当前方法的返回值keyGenerator指定缓存数据的key的生成器,与key属性二选一使用cacheManager指定缓存管理器cacheResolver指定缓存解析器,与cacheManager属性二选一使用condition指定在符合某条件下,进行数据缓存unless指定在符合某条件下,不进行数据缓存sync指定是否使用异步缓存。默认false

执行原理

Cacheable通过动态代理的方式进行缓存,方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取,(CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建;

@Cacheable在不引入任何的依赖的情况下默认使用的是SimpleCacheConfiguration配置的ConcurrentMapCacheManager缓存组件,将缓存结果存储在cacheMap中。使用一个key,默认就是方法的参数,如果多个参数或者没有参数,是按照某种策略生成的,默认是使用KeyGenerator生成的。

使用SimpleKeyGenerator生成key策略:

参数个数key没有参数new SimpleKey()有一个参数参数值多个参数new SimpleKey(params)

@CachePut注解

目标方法执行完之后生效, @CachePut被使用于修改操作比较多,哪怕缓存中已经存在目标值了,但是这个

注解保证这个方法依然会执行,执行之后的结果被保存在缓存中

@CacheEvict注解

@CacheEvict注解是由Spring框架提供的,可以作用于类或方法(通常用在数据删除方法上),该注解

的作用是删除缓存数据。@CacheEvict注解的默认执行顺序是,先进行方法调用,然后将缓存进行清

除。

5.3.2Redis缓存

1、注解方式使用redis缓存

在使用注解的方式使用redis缓存的时候只需要导入redis的包和配置redis,SpringBoot默认就会走redis缓存,使用方法和默认缓存的使用方法一致。

POM

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>


配置

#Redis配置
# Redis服务地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
spring.redis.database=1
# 对基于注解的Redis缓存数据统一设置有效期为1分钟,单位毫秒
spring.cache.redis.time-to-live=60000


原理是在引入redis配置之后,系统直接就会使用RedisCacheConfiguration对缓存进行配置,使用RedisCacheManager来管理缓存,使用方式也是通过@Cacheable等注解来实现的。


修改默认的序列化方式为json方式

通过查看RedisCacheConfiguration 类的源码发现默认是jdk的序列化方式


修改默认的序列化方式

@Configuration
public class RedisAnnoConfig {

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        // 分别创建String和JSON格式序列化对象,对缓存数据key和value进行转换
        RedisSerializer<String> strSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jacksonSeial =
                new Jackson2JsonRedisSerializer(Object.class);

        // 解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);

        // 定制缓存数据序列化方式及时效
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofDays(1))//缓存时间为1天
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(strSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(jacksonSeial))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager
                .builder(redisConnectionFactory).cacheDefaults(config).build();
        return cacheManager;
    }
}


2、使用RedisTemplate进行缓存

直接注入RedisTemplate进行操作,但是有个问题,注入的redisTemplate在保存数据的时候还是jdk默认的序列化方式因此需要修改序列化方式。RedisAutoConfiguration类来分析。

修改RedisTemplate序列化方式,思路是直接覆盖自动注册的redisTemplate实例。

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<Object,Object> redisTemplate(
                      RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        //设置序列化方式
        Jackson2JsonRedisSerializer jackSonSerializer = 
				new Jackson2JsonRedisSerializer(Object.class);
        redisTemplate.setDefaultSerializer(jackSonSerializer);

        //解决查询缓存转换异常问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackSonSerializer.setObjectMapper(om);
        // 设置RedisTemplate模板API的序列化方式为JSON
        redisTemplate.setDefaultSerializer(jackSonSerializer);

        return redisTemplate;
    }
}

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接