SpringBoot 08: SpringBoot综合使用 MyBatis, Dubbo, Redis
业务背景
Student表
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`phone` varchar(11) COLLATE utf8_bin DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
两个业务功能
针对上述student表, 综合应用springboot, mybatis, dubbo, redis实现如下两个业务功能
1. 注册学生
要求:
注册接口定义为:int saveStudent(Student student)
利用传入的学生的手机号注册,手机号必须唯一
如果已经存在了手机号, 注册失败, 返回2
如果手机号为空,注册失败,返回-1
注册成功,返回0
2. 查询学生
- 要求:
- 查询接口定义为:Student queryStudent(Integer id)
- 根据id查询目标学生
- 先到redis查询学生,如果redis没有,从数据库查询
- 如果数据库有,把查询到的学生放入到redis,返回该学生,后续再次查询这个学生应该从redis就能获取到
- 如果数据库也没有目标学生,返回空
其他要求
- 关于Dubbo
- 要求使用dubbo框架,addStudent, queryStudent是由服务提供者实现的
- 消费者可以是一个Controller,调用提供者的两个方法, 实现学生的注册和查询
- 关于前端页面
- 页面使用html, ajax, jquery
- 通过postman发送post请求,来注册学生
- 通过html页面上的form表单,提供文本框输入id, 进行查询
- html, jquery.js都放到springboot项目的resources/static目录中
编程实现
项目结构
- 分布式总体项目结构
- 公共接口项目结构
- 服务提供者项目结构
- 消费者项目结构
dubbo的公共接口项目
注意该项目为普通的maven项目即可
实体类
package com.example.demo.model;
import java.io.Serializable;
public class Student implements Serializable {
private static final long serialVersionUID = -3272421320600950226L;
private Integer id;
private String name;
private String phone;
private Integer age;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", phone='" + phone + '\'' +
", age=" + age +
'}';
}
//防止缓存穿透,可以获取默认学生(学生信息故意设置不合法,后期在redis中一眼就能看出来是异常数据),填充到redis中
public static Student getDefaultStudent(){
Student student = new Student();
student.setId(-1);
student.setName("-");
student.setPhone("-");
student.setAge(0);
return student;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Student(Integer id, String name, String phone, Integer age) {
this.id = id;
this.name = name;
this.phone = phone;
this.age = age;
}
public Student() {
}
}
提供的服务接口定义
package com.example.demo.service;
import com.example.demo.model.Student;
public interface StudentService {
//保存学生信息
int saveStudent(Student student);
//根据id,查询学生信息
Student queryStudent(Integer id);
}
dubbo的服务提供者项目
注意:该项目为springboot项目,且在起步依赖里要勾选web(web依赖可以不选), redis, mysql, mybatis的起步依赖
项目配置
- 额外在pom.xml里手动加入对公共接口项目以及dubbo和zookeeper的依赖
<!-- 公共项目依赖 -->
<dependency>
<groupId>com.example.demo</groupId>
<artifactId>demo-api</artifactId>
<version>1.0.0</version>
</dependency>
<!--dubbo依赖-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.8</version>
</dependency>
<!--zookeeper依赖-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<version>2.7.8</version>
<type>pom</type>
<exclusions>
<!-- 排除log4j的依赖-->
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
- 配置application.properties文件
########################### 配置dubbo
#配置提供的服务名称
dubbo.application.name=student-service-provider
#配置需要扫描的包
dubbo.scan.base-packages=com.example.demo.service
#配置注册中心
dubbo.registry.address=zookeeper://127.0.0.1:2181
########################### 配置redis
#redis服务的ip
spring.redis.host=127.0.0.1
#redis服务的端口
spring.redis.port=6379
########################### mybatis配置
#mybatis中mapper文件编译到的资源路径
mybatis.mapper-locations=classpath:mapper/*.xml
#mybatis日志输出
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
############################ 数据源配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://数据库服务器ip:3306/数据库名?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
spring.datasource.username=XXX
spring.datasource.password=YYY
dao层
- dao接口
package com.example.demo.dao;
import com.example.demo.model.Student;
import org.apache.ibatis.annotations.Param;
public interface StudentDao {
//以手机号作为查询条件,判断学生是否存在
Student queryStudentByPhone(@Param("phone") String phone);
//保存新创建的学生信息
int saveStudent(Student student);
//根据学生id,查询学生信息
Student queryStudentById(@Param("id") Integer id);
}
- dao接口对应的xml文件, 位于resources/mapper目录下,这里将dao接口和dao.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.example.demo.dao.StudentDao">
<!--
以学生手机号码为依据,判断学生是否已经存在
-->
<select id="queryStudentByPhone" parameterType="string" resultType="com.example.demo.model.Student">
select id, name, age, phone from student where phone = #{phone}
</select>
<!--
保存新创建的学生
-->
<insert id="saveStudent" parameterType="com.example.demo.model.Student">
insert into student(name, age, phone) values(#{name}, #{age}, #{phone})
</insert>
<!--
根据学生id,查询学生信息
-->
<select id="queryStudentById" parameterType="int" resultType="com.example.demo.model.Student">
select id, name, age, phone from student where id = #{id}
</select>
</mapper>
- 实现公共接口工程里对外提供的服务
package com.example.demo.service.impl;
import com.example.demo.dao.StudentDao;
import com.example.demo.model.Student;
import com.example.demo.service.StudentService;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import javax.annotation.Resource;
@DubboService(interfaceClass = StudentService.class, version = "1.0.0", timeout = 5000)
public class StudentServiceImpl implements StudentService {
@Resource
private StudentDao studentDao;
@Resource
private RedisTemplate redisTemplate;
//保存新创建的学生
@Override
public int saveStudent(Student student) {
int saveResult = 0;//表示保存学生信息的结果:1/添加成功 -1:手机号为空 2:手机号码重复
if(student.getPhone() == null){
saveResult = -1;
}else{
Student queryStudentResult = studentDao.queryStudentByPhone(student.getPhone());
if(queryStudentResult != null){
saveResult = 2;
}else{
//该学生尚未存在,保存到数据库中
saveResult = studentDao.saveStudent(student);
}
}
return saveResult;
}
@Override
public Student queryStudent(Integer id) {
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Student.class));
final String STUDENT_USER_KEY = "STUDENT:";
String key = STUDENT_USER_KEY + id;
//先尝试从缓存获取:按照key的格式来查
Student student = (Student) redisTemplate.opsForValue().get(key);
System.out.println("------- 从redis中查询数据 ----------> : " + student);
if(student == null){
//缓存中没有,需要到数据库查询:按照id格式来查询
student = studentDao.queryStudentById(id);
System.out.println("------- 从数据库中查询数据 ---------> : " + student);
if(student != null){
//数据库中有该数据,存一份数据到redis中:按照key的格式来存
redisTemplate.opsForValue().set(key, student);
}else{
//防止缓存穿透:对既未在缓存又未在数据库中的数据,设置默认值
redisTemplate.opsForValue().set(key, Student.getDefaultStudent());
}
}
return student;
}
}
- springboot主启动类上添加支持dubbo的注解并添加对dao接口扫描的注解
package com.example;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo
@MapperScan(basePackages = "com.example.demo.dao")
public class StudentserviceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(StudentserviceProviderApplication.class, args);
}
}
dubbo消费者项目
该项目为springboot项目,启动项依赖只要勾选web依赖
pom.xml中的额外依赖均与服务提供者相同
项目配置
- 配置application.properties
#springboot服务的基本配置
server.port=9090
server.servlet.context-path=/demo
#springboot中使用dubbo的配置
#消费者名称
dubbo.application.name=student-service-consumer
#配置需要扫描的包
dubbo.scan.base-packages=com.example.demo.controller
#配置注册中心
dubbo.registry.address=zookeeper://127.0.0.1:2181
- 同样在springboot的启动类上添加支持dubbo的注解
package com.example;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo
public class StudentConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(StudentConsumerApplication.class, args);
}
}
controller层
- 消费者与前端交互的controller层, 采用RESTful接口风格
package com.example.demo.controller;
import com.example.demo.model.Student;
import com.example.demo.service.StudentService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class StudentController {
@DubboReference(interfaceClass = StudentService.class, version = "1.0.0")
private StudentService studentService;
@PostMapping("/student/add")
public String addStudent(Student student){
int saveStudentResult = studentService.saveStudent(student);
String msg = "";
if(saveStudentResult == 1){
msg = "添加学生: " + student.getName() + " 成功";
}else if(saveStudentResult == -1){
msg = "手机号不能为空";
}else if(saveStudentResult == 2){
msg = "手机号: " + student.getPhone() + " 重复,请更换手机号后重试";
}
return msg;
}
@PostMapping("/student/query")
public String queryStudent(Integer id){
String msg = "";
Student student = null;
if(id != null && id > 0){
student = studentService.queryStudent(id);
if(student != null){
msg = "查询到的学生信息: " + student.toString();
}else{
msg = "未查询到相关信息";
}
}else{
msg = "输入的id范围不正确";
}
return msg;
}
}
前端页面
前端html页面和js文件位于resources/static目录下
可以借助postman便捷的发送post请求来添加学生
查询学生的请求可以借助如下query.html页面,通过ajax来发送查询请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>query.html</title>
<script src="../js/jquery-1.11.1-min.js"></script>
<script type="text/javascript">
$(function (){
$("#stuBtn").click(function (){
var id = $("#stuId").val();
$.ajax({
url: "/demo/student/query",
data:{"id":id},
type:"post",
dataType:"text",
success:function (data){
alert(data);
}
})
});
});
</script>
</head>
<body>
<input type="text" id="stuId"><br>
<input type="button" id="stuBtn" value="查询">
</body>
</html>
SpringBoot 08: SpringBoot综合使用 MyBatis, Dubbo, Redis的更多相关文章
- SpringBoot 入门教程:集成mybatis,redis
SrpingBoot相较于传统的项目具有配置简单,能快速进行开发的特点,花更少的时间在各类配置文件上,更多时间在具体业务逻辑上. SPringBoot采用纯注解方式进行配置,不喜欢xml配置的同学得仔 ...
- springboot整合mybatis,mongodb,redis
springboot整合常用的第三方框架,mybatis,mongodb,redis mybatis,采用xml编写sql语句 mongodb,对MongoTemplate进行了封装 redis,对r ...
- 从 0 使用 SpringBoot MyBatis MySQL Redis Elasticsearch打造企业级 RESTful API 项目实战
大家好!这是一门付费视频课程.新课优惠价 699 元,折合每小时 9 元左右,需要朋友的联系爱学啊客服 QQ:3469271680:我们每课程是明码标价的,因为如果售价为现在的 2 倍,然后打 5 折 ...
- SpringBoot | 第二十九章:Dubbo的集成和使用
前言 今年年初时,阿里巴巴开源的高性能服务框架dubbo又开始了新一轮的更新,还加入了Apache孵化器.原先项目使用了spring cloud之后,已经比较少用dubbo.目前又抽调回原来的行业应用 ...
- Springboot 2.0.4 整合Mybatis出现异常Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
在使用Springboot 2.0.4 整合Mybatis的时候出现异常Property 'sqlSessionFactory' or 'sqlSessionTemplate' are require ...
- JAVA - SpringBoot项目引用generator生成 Mybatis文件
JAVA - SpringBoot项目引用generator生成 Mybatis文件 在spring官网https://start.spring.io/自动生成springboot项目,这里选择项目 ...
- SpringBoot系列(五)Mybatis整合完整详细版
SpringBoot系列(五)Mybatis整合 目录 mybatis简介 项目创建 entity dao service serviceImpl mapper controller 1. Mybat ...
- SpringBoot数据访问之整合mybatis注解版
SpringBoot数据访问之整合mybatis注解版 mybatis注解版: 贴心链接:Github 在网页下方,找到快速开始文档 上述链接方便读者查找. 通过快速开始文档,搭建环境: 创建数据库: ...
- canal整合springboot实现mysql数据实时同步到redis
业务场景: 项目里需要频繁的查询mysql导致mysql的压力太大,此时考虑从内存型数据库redis里查询,但是管理平台里会较为频繁的修改增加mysql里的数据 问题来了: 如何才能保证mysql的数 ...
- springboot实现分布式锁(spring integration,redis)
Springboot实现分布式锁(Spring Integration+Redis) 一.在项目的pom.xml中添加相关依赖 1)Spring Integration依赖 <dependenc ...
随机推荐
- APICloud AVM框架 封装车牌号输入键盘组件
AVM(Application-View-Model)前端组件化开发模式基于标准Web Components组件化思想,提供包含虚拟DOM和Runtime的编程框架avm.js以及多端统一编译工具,完 ...
- KingbaseES DBLink 扩展介绍
DBLink 扩展插件功能与 Kingbase_FDW 类似,用于远程访问KingbaseES 数据库.相比于Kingbase_FDW,DBLink 功能更强大,可以执行DML,还可以通过 begin ...
- MariaDB数据库 主-从 部署
〇.前言 好久没碰数据库了 准备环境: centos7自带的MariaDB,没有的话下面是安装命令 yum install -y mariadb mariadb-server systemctl re ...
- 纯CSS实现“流星赶月”,祝大家中秋节快乐
明天就是中秋节了,就想着用CSS画一个月亮送给园友们吧.但是就画一个月亮也太简单了些,于是便加了一些星星点缀以及流星坠落的效果.这篇文章就用纯CSS为大家实现一个"流星赶月"的效果 ...
- Elasticsearch:定制分词器(analyzer)及相关性
转载自:https://elasticstack.blog.csdn.net/article/details/114278163 在许多的情况下,我们使用现有的分词器已经足够满足我们许多的业务需求,但 ...
- PAT (Basic Level) Practice 1025 反转链表 分数 25
给定一个常数 K 以及一个单链表 L,请编写程序将 L 中每 K 个结点反转.例如:给定 L 为 1→2→3→4→5→6,K 为 3,则输出应该为 3→2→1→6→5→4:如果 K 为 4,则输出应该 ...
- aws-cli命令-S3相关的操作及管理
在工作中,我们可能经常会将本地数据上传S3进行备份,或者将S3数据下载到本地 本文主要讲解下,工作中可能经常会用到的与S3相关的操作 1.将本地目录的数据同步到指定的S3位置,及s3资源管理 # 同步 ...
- Educational Codeforces Round 106 (Rated for Div. 2)
就ac了2题... A题一开始题意模模糊糊的似懂非懂,然后自己按样例推出了题意,简单题很容易ac了.还是自己的英语水平太菜了.... B题根据0和1的位置关系能看出来,因为0不能在1后面, 所以有00 ...
- P1073 [NOIP2009 提高组] 最优贸易 (最短路spfa)
本题就是在一条1-n的路径上找p,q(先经过p),使得q-p最大. 考虑建正反图,正图上求出d[x],表示1-x的路径经过的节点最小值,反图上则从n开始求出f[x],x-n的最大值,最后枚举断点i,取 ...
- P1399 [NOI2013] 快餐店 方法记录
原题题面P1399 [NOI2013] 快餐店 题目描述 小 T 打算在城市 C 开设一家外送快餐店.送餐到某一个地点的时间与外卖店到该地点之间最短路径长度是成正比的,小 T 希望快餐店的地址选在离最 ...