热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SpringBoot08:SpringBoot综合使用MyBatis,Dubbo,Redis

业务背景Student表CREATETABLE`student`(`id`int(11)NOTNULLAUTO_INCREMENT,`name`varchar(255)COLLATEutf8_binDEFAULTNULL,`phon

业务背景

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目录中

编程实现

项目结构

  • 分布式总体项目结构

image

  • 公共接口项目结构

image

  • 服务提供者项目结构

image

  • 消费者项目结构

image

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的依赖
        
        
            com.example.demo
            demo-api
            1.0.0
        

        
        
            org.apache.dubbo
            dubbo-spring-boot-starter
            2.7.8
        


        
        
            org.apache.dubbo
            dubbo-dependencies-zookeeper
            2.7.8
            pom
            
                
                
                    slf4j-log4j12
                    org.slf4j
                
            
        
  • 配置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文件分开管理



    
    

    
    
        insert into student(name, age, phone) values(#{name}, #{age}, #{phone})
    

    
    


  • 实现公共接口工程里对外提供的服务
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来发送查询请求




    
    
    
    




推荐阅读
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • PDO MySQL
    PDOMySQL如果文章有成千上万篇,该怎样保存?数据保存有多种方式,比如单机文件、单机数据库(SQLite)、网络数据库(MySQL、MariaDB)等等。根据项目来选择,做We ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 2021最新总结网易/腾讯/CVTE/字节面经分享(附答案解析)
    本文分享作者在2021年面试网易、腾讯、CVTE和字节等大型互联网企业的经历和问题,包括稳定性设计、数据库优化、分布式锁的设计等内容。同时提供了大厂最新面试真题笔记,并附带答案解析。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • springmvc学习笔记(十):控制器业务方法中通过注解实现封装Javabean接收表单提交的数据
    本文介绍了在springmvc学习笔记系列的第十篇中,控制器的业务方法中如何通过注解实现封装Javabean来接收表单提交的数据。同时还讨论了当有多个注册表单且字段完全相同时,如何将其交给同一个控制器处理。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • Python SQLAlchemy库的使用方法详解
    本文详细介绍了Python中使用SQLAlchemy库的方法。首先对SQLAlchemy进行了简介,包括其定义、适用的数据库类型等。然后讨论了SQLAlchemy提供的两种主要使用模式,即SQL表达式语言和ORM。针对不同的需求,给出了选择哪种模式的建议。最后,介绍了连接数据库的方法,包括创建SQLAlchemy引擎和执行SQL语句的接口。 ... [详细]
  • 本文介绍了OpenStack的逻辑概念以及其构成简介,包括了软件开源项目、基础设施资源管理平台、三大核心组件等内容。同时还介绍了Horizon(UI模块)等相关信息。 ... [详细]
  • Java和JavaScript是什么关系?java跟javaScript都是编程语言,只是java跟javaScript没有什么太大关系,一个是脚本语言(前端语言),一个是面向对象 ... [详细]
  • Centos下安装memcached+memcached教程
    本文介绍了在Centos下安装memcached和使用memcached的教程,详细解释了memcached的工作原理,包括缓存数据和对象、减少数据库读取次数、提高网站速度等。同时,还对memcached的快速和高效率进行了解释,与传统的文件型数据库相比,memcached作为一个内存型数据库,具有更高的读取速度。 ... [详细]
author-avatar
ID张蕾
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有