SpringBoot2.X整合ClickHouse项目实战-从零搭建整合(三)

news2024/9/23 19:27:57

一、ClickHouse+SpringBoot2.X+MybatisPlus整合搭建

二、需求描述和数据库准备

 三、ClickHouse统计SQL编写实战和函数使用

四、ClickHouse+SpringBoot2.X案例-基础模块搭建 

controller/request层

mapper层 

 model层

 service层

 五、ClickHouse+SpringBoot2.X案例-数据统计接口

service层

mapper层

一、ClickHouse+SpringBoot2.X+MybatisPlus整合搭建

1.在线创建项目 https://start.spring.io/

 idea导入刚下载下来的项目

 在pom.xml中增加ClickHouse依赖

       <dependency>
            <groupId>ru.yandex.clickhouse</groupId>
            <artifactId>clickhouse-jdbc</artifactId>
            <version>0.1.55</version>
        </dependency>
                
        <!--mybatis plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
             <version>3.4.0</version>
        </dependency>

 数据库连接配置

server.port=8080
spring.datasource.driver-class-name=ru.yandex.clickhouse.ClickHouseDriver
spring.datasource.url=jdbc:clickhouse://11x.xxx.xx.24x:8123/default
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
logging.level.root=INFO

二、需求描述和数据库准备

指定商品,统计指定日期范围内,各个省市的访问的pv数量,如7天内有各个城市访问某个商品分布

指定商品,多天内的访问曲线变化图,如,1~7号商品访问量波动图

建表语句:

CREATE TABLE default.visit_stats
(
    `product_id` UInt64,//商品id
    `is_new` UInt16,//是否是新用户 1新用户 0老用户
    `province` String,//省名称
    `city` String,//市名称
    `pv` UInt32,//轻度聚合后的访问量
    `visit_time` DateTime //访问时间
)
ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(visit_time)
ORDER BY (
 product_id,
 is_new,
 province,
 city
 );

插入sql:

INSERT into visit_stats values
('1','1','广东','广州',14323,'2023-01-01 12:11:13'),
('1','0','广东','广州',4232,'2023-02-12 16:16:13'),
('1','1','广东','佛山',54323,'2023-03-06 16:11:13'),
('1','0','广东','东莞',42341,'2023-03-02 16:12:13'),
('1','1','广东','梅州',52422,'2023-03-09 12:11:13'),
('2','1','广东','广州',14323,'2021-03-01 12:11:13'),
('2','0','广东','深圳',425232,'2023-04-12 16:16:13'),
('2','1','广东','佛山',543323,'2022-06-06 16:11:13'),
('2','0','广东','东莞',42341,'2021-05-02 16:12:13'),
('2','1','广东','梅州',52422,'2022-01-09 12:11:13'),
('3','1','北京','北京',13132,'2023-01-01 12:11:13'),
('3','0','广东','广州',533232,'2022-02-16 16:16:13'),
('4','1','浙江','杭州',663643,'2023-12-06 12:11:13'),
('4','0','广东','东莞',4142,'2023-11-02 16:12:13'),
('5','1','湖南','长沙',52123,'2022-01-09 12:11:13'),
('4','0','湖南','衡阳',4142,'2024-05-02 16:12:13'),
('5','1','广东','中山',52123,'2024-01-09 12:11:13'),
('2','1','上海','上海',14323,'2021-03-01 12:11:13'),
('5','0','浙江','宁波',425232,'2023-04-12 16:16:13'),
('3','1','广东','佛山',543323,'2022-06-06 16:11:13'),
('2','0','湖南','长沙',42341,'2021-05-02 16:12:13'),
('2','1','广东','深圳',52422,'2022-01-09 12:11:13')

 三、ClickHouse统计SQL编写实战和函数使用

统计需求:某个商品再时间范围内地区访问分布-城市级别,天级别

select province,city, sum(pv) pv_count  
from visit_stats where  product_id =1 
and toYYYYMMDD(visit_time) BETWEEN '20200101' and '20241212' 
group by province,city order by pv_count desc

函数:
求和:sum(pv) 
年格式:select toYear(toDateTime('2024-12-11 11:12:13')) 
日期格式化:select toYYYYMMDD(toDateTime('2024-12-11 11:12:13')) 
日期时间格式化:select toYYYYMMDDhhmmss(toDateTime('2024-12-11 11:12:13')) 
周格式化,1~7,当前时间是本周第几天,下面是周三结果是3,周日结果是7
select toDayOfWeek(toDateTime('2024-12-11 11:12:13')) 

小时格式化,提取时间里面的小时,比如 2023-12-29 10:05:10,格式化后是【10】点
select toHour(toDateTime('2024-12-11 11:12:13')) 

分钟格式化,提取时间里面的分钟,比如 2023-12-29 10:05:10,格式化后是【5】分钟
select toMinute(toDateTime('2024-12-11 11:12:13')) 

秒格式化,提取时间里面的秒
select toSecond(toDateTime('2024-12-11 11:12:13')) 

获取当前日期时间
select now()

获取当前日期
select today()

某个商品,多天内的访问曲线图, 天级别

 select 
  toYYYYMMDD(visit_time) date_time_str, 
  sum(pv) pv_count from visit_stats
 where product_id =2 
 and toYYYYMMDD(visit_time) BETWEEN '20200101' and '20241212' 
 group by date_time_str ORDER BY date_time_str desc

 

 所用函数:

逻辑判断:
SELECT if(cond, then, else)
例子:SELECT if(1, plus(3, 3), plus(6, 8))

如果条件 cond 的计算结果为非零值,则返回表达式 then 的结果,并且跳过表达式 else 的结果
如果 cond 为零或 NULL,则将跳过 then 表达式的结果,并返回 else 表达式的结果

字符串拼接(不能双引号):
select concat('我','上班的时候','没有摸鱼~')


最大、最小、平均值:
select max(pv), min(pv), avg(pv) from visit_stats

四、ClickHouse+SpringBoot2.X案例-基础模块搭建 

controller/request层

package net.wnnck.demo.controller.request;

public class VisitRecordPageRequest {

    private long productId;

    private int page;

    private int size;

    public long getProductId() {
        return productId;
    }

    public void setProductId(long productId) {
        this.productId = productId;
    }

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }
}


package net.wnnck.demo.controller;

import net.wnnck.demo.controller.request.VisitRecordPageRequest;
import net.wnnck.demo.model.JsonData;
import net.wnnck.demo.service.VisitStatsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;
@RestController
@RequestMapping("/api/v1/data")
public class DataController {

    @Autowired
    private VisitStatsService visitStatsService;

    @RequestMapping("page")
    public JsonData queryVisitRecord(@RequestBody VisitRecordPageRequest pageRequest){

        Map<String, Object> map = visitStatsService.pageVisitRecord(pageRequest);
        return JsonData.buildSuccess(map);

    }


}

mapper层 

package net.wnnck.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.wnnck.demo.model.VisitStatsDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface VisitStatsMapper extends BaseMapper<VisitStatsDO> {

    /**
     * 统计总条数
     * @param productId
     * @return
     */
    int countTotal(@Param("productId") long productId);

    /**
     * 分页
     * @param from
     * @param size
     * @return
     */
    List<VisitStatsDO> pageVisitRecord(@Param("productId")Long productId  , @Param("from") int from, @Param("size") int size);


}

resources/mapper/VisitStatsMapper.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="net.wnnck.demo.mapper.VisitStatsMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="net.wnnck.demo.model.VisitStatsDO">
        <result column="product_id" property="productId"/>
        <result column="is_new" property="isNew"/>
        <result column="province" property="province"/>
        <result column="city" property="city"/>
        <result column="pv" property="pv"/>
        <result column="visit_time" property="visitTime"/>
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        product_id,is_new,province,city,pv,visit_time
    </sql>

    <!--统计总条数-->
    <select id="countTotal" resultType="java.lang.Integer">
        select count(1) from visit_stats  where   product_id=#{productId}
    </select>

    <!--分页查找-->
    <select id="pageVisitRecord" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from visit_stats where product_id=#{productId}
        order by visit_time desc limit #{from},#{size}
    </select>

</mapper>

 model层

package net.wnnck.demo.model;

public class JsonData {
    /**
     * 状态码 0 表示成功
     */

    private Integer code;
    /**
     * 数据
     */
    private Object data;
    /**
     * 描述
     */
    private String msg;


    public JsonData(){

    }

    public JsonData(Integer code, Object data, String msg) {
        this.code = code;
        this.data = data;
        this.msg = msg;
    }

    /**
     * 成功,不传入数据
     *
     * @return
     */
    public static JsonData buildSuccess() {
        return new JsonData(0, null, null);
    }

    /**
     * 成功,传入数据
     *
     * @param data
     * @return
     */
    public static JsonData buildSuccess(Object data) {
        return new JsonData(0, data, null);
    }

    /**
     * 失败,传入描述信息
     *
     * @param msg
     * @return
     */
    public static JsonData buildError(String msg) {
        return new JsonData(-1, null, msg);
    }


    /**
     * 自定义状态码和错误信息
     *
     * @param code
     * @param msg
     * @return
     */
    public static JsonData buildCodeAndMsg(int code, String msg) {
        return new JsonData(code, null, msg);
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}


package net.wnnck.demo.model;

public class VisitStatsDO {

    /**
     * 商品
     */
    private Long productId;

    /**
     * 访问时间
     */
    private String visitTime;


    /**
     * 1是新访客,0是老访客
     */
    private Integer isNew;

    /**
     * 访问量
     */
    private Integer pv;

    /**
     * 省份
     */
    private String province;

    /**
     * 城市
     */
    private String city;

    /**
     * ========度量值=========
     */
    private Long pvCount=0L;

    /**
     * 时间的字符串映射,天、小时
     */
    private String dateTimeStr;

    public Long getProductId() {
        return productId;
    }

    public void setProductId(Long productId) {
        this.productId = productId;
    }

    public String getVisitTime() {
        return visitTime;
    }

    public void setVisitTime(String visitTime) {
        this.visitTime = visitTime;
    }

    public Integer getIsNew() {
        return isNew;
    }

    public void setIsNew(Integer isNew) {
        this.isNew = isNew;
    }

    public Integer getPv() {
        return pv;
    }

    public void setPv(Integer pv) {
        this.pv = pv;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public Long getPvCount() {
        return pvCount;
    }

    public void setPvCount(Long pvCount) {
        this.pvCount = pvCount;
    }

    public String getDateTimeStr() {
        return dateTimeStr;
    }

    public void setDateTimeStr(String dateTimeStr) {
        this.dateTimeStr = dateTimeStr;
    }
}

 service层

package net.wnnck.demo.service;

import net.wnnck.demo.controller.request.VisitRecordPageRequest;

import java.util.Map;

public interface VisitStatsService {


    Map<String,Object> pageVisitRecord(VisitRecordPageRequest pageRequest);
}
package net.wnnck.demo.service.impl;

import net.wnnck.demo.controller.request.VisitRecordPageRequest;
import net.wnnck.demo.mapper.VisitStatsMapper;
import net.wnnck.demo.model.VisitStatsDO;
import net.wnnck.demo.service.VisitStatsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Service
public class VisitStatsServiceImpl implements VisitStatsService {

    @Autowired
    private VisitStatsMapper visitStatsMapper;

    @Override
    public Map<String, Object> pageVisitRecord(VisitRecordPageRequest pageRequest) {

        Map<String,Object> data = new HashMap<>(3);

        Long productId = pageRequest.getProductId();
        int page = pageRequest.getPage();
        int size = pageRequest.getSize();

        int count = visitStatsMapper.countTotal(productId);

        int from = (page -1) * size;

        List<VisitStatsDO> visitStatsDOS = visitStatsMapper.pageVisitRecord(productId, from, size);

        data.put("total",count);
        data.put("current_page",page);
        data.put("data",visitStatsDOS);

        /**
         * 计算总页数
         */
        int totalPage;
        if(count % size == 0){
            totalPage = count / size;
        }else {
            totalPage = count / size +1 ;
        }

        data.put("total_page",totalPage);
        return data;
    }
}

启动后可正常访问,表示基础环境已搭建好。 文末有贴代码下载地址~

 五、ClickHouse+SpringBoot2.X案例-数据统计接口

 第三节的需求sql整合mapper

service层

    @Override
    public List<VisitStatsDO> queryVisitTrend(VisitTrendQueryRequest queryRequest) {

        Long productId = queryRequest.getProductId();

        String type = queryRequest.getType();

        List<VisitStatsDO> list = null;

        if(type.equalsIgnoreCase("region")){

            list = visitStatsMapper.queryRegionTrendWithMultiDay(productId,queryRequest.getStartTime(),queryRequest.getEndTime());

        } else if(type.equalsIgnoreCase("day")){

            list = visitStatsMapper.queryVisitTrendWithMultiDay(productId,queryRequest.getStartTime(),queryRequest.getEndTime());
        }

        return list;
    }

mapper层

<select id="queryRegionTrendWithMultiDay" resultMap="BaseResultMap">
        select province ,city, sum(pv) pv_count from visit_stats
where product_id = #{productId} and toYYYYMMDD(visit_time) BETWEEN #{startTime} and #{endTime}
GROUP BY province ,city  order by pv_count desc
</select>


 <select id="queryVisitTrendWithMultiDay" resultMap="BaseResultMap">
  select toYYYYMMDD(visit_time) date_time_str,sum(pv) pv_count from visit_stats
  where product_id = #{productId} and toYYYYMMDD(visit_time) BETWEEN #{startTime} and #{endTime}
  GROUP BY date_time_str order by date_time_str desc
 </select>

 #时间范围内地区访问分布-城市级别

 多天内的访问曲线图pv

代码下载地址:

链接:https://pan.baidu.com/s/1g8dHKiZMQIhJTmuCO814hw?pwd=ex2x 
提取码:ex2x 

ClickHouse快速安装-可视化工具连接-创建第一个ck库表(一)_clickhouse可视化工具_这是王姑娘的微博的博客-CSDN博客OLAP是什么,以及快速安装ClickHouse(容器化部署),CK可视化工具的下载链接使用以及创建第一个CK数据库和表,然后新增数据,浏览3分钟即可快速掌握这些知识https://blog.csdn.net/wnn654321/article/details/125837194ClickHouse常见SQL语法和常见合并数引擎Demo(二)_这是王姑娘的微博的博客-CSDN博客分区是表的分区,把一张表的数据分成N多个区块,分区后的表还是一张表,数据处理还是由自己来完成PARTITION BY,指的是一个表按照某一列数据(比如日期)进行分区,不同分区的数据会写入不同的文件中建表时加入partition概念,可以按照对应的分区字段,允许查询在指定了分区键的条件下,尽可能的少读取数据注意:不是所有的表引擎都可以分区,合并树(MergeTree) 系列的表引擎才支持数据分区,Log系列引擎不支持。...https://blog.csdn.net/wnn654321/article/details/125920177

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/356944.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

城市轨道交通供电系统研究(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密…

一文告诉你什么是财务数据治理?

大家好&#xff0c;我是梦想家Alex&#xff0c;今天是周末&#xff0c;就不给大家分享技术文了&#xff5e;应出版社老师推荐&#xff0c;文末给大家送几本DAMA中国主席力荐&#xff0c;20位行业专家历时2年共同打造的《财务数据治理实战》&#xff0c;将数据治理理论应用于财务…

怎样查询PMP成绩?

【如何查询成绩】1、输入网址&#xff08;PMI官网&#xff0c;不知道网址的私戳&#xff09;&#xff0c;点击 Log In如果忘记 PMI 的账号和密码了&#xff0c;怎么办&#xff1f;可以在你报名机构官网的个人中心的学习中心的我的报名处查看 PMI 的注册名和密码2、点击 Exam An…

CMake 入门学习4 软件包管理

CMake 入门学习4 软件包管理一、Linux下的软件包管理1. 检索已安装的软件包2. 让自己编译软件支持pkg-config搜索3. 在CMakeLists查找已安装的软件包二、适合Windows下的包管理工具1. vcpkg2. Conan(1) 安装Conan(2) 配置Conan(3) 创建工程(4) 安装依赖库(5) 使用依赖库三、CMa…

汉字----dgfont

Abstract 字符生成是一个具有挑战性的问题,特别是对于一些由大量字符组成的书写系统,近年来受到了广泛的关注。然而,现有的字体生成方法通常是在监督学习中。它们需要大量的配对数据,这是劳动密集型和昂贵的收集。此外,常见的图像到图像转换模型通常将风格定义为纹理和颜…

golang入门笔记——内存管理

文章目录自动内存管理概念自动内存管理-相关概念&#xff1a;追踪垃圾回收&#xff1a;分代GC&#xff08;Generational GC&#xff09;引用计数内存分配Go内存分配-分块Go内存分配——多级缓存Go内存管理优化Balanced GC自动内存管理 概念 1.动态内存 程序在运行时根据需求…

大数据全系安装

内容版本号CentOS7.6.1810ZooKeeper3.4.6Hadoop2.9.1HBase1.2.0MySQL5.6.51HIVE2.3.7Sqoop1.4.6flume1.9.0kafka2.8.1scala2.12davinci3.0.1spark2.4.8flink1.13.5 1. 下载CentOS 7镜像 CentOS官网 2. 安装CentOS 7系统——采用虚拟机方式 2.1 新建虚拟机 2.2.1 [依次选择]-&…

SPI通讯简介

一、基本概念 SPI是串行外设接口(Serial Peripheral Interface)的缩写&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;主要应用在EEPROM,FLASH,实时时钟&#xff0c;AD转换器&#xff0c;多MCU间通讯等等&#xff0c;SPI端口可以在多主器件…

从业6年,对敏捷和自动化测试的一点心得

不久前&#xff0c;参加Thoughtworks组织的一场自动化测试的分享&#xff0c;同事由于出差国外不能参加&#xff0c;特意嘱托我提问两个问题&#xff1a; 在互联网这个将“敏捷”与“持续集成”进行积极实践的环境里&#xff0c;“敏捷测试”与“自动化测试”成了一个大家经常…

【数据库数据乱码错误】存进去的数据乱码(???)

目录 1.当我新增一条数据的时候&#xff0c;成功后查看数据库中的数据时&#xff0c;竟然变成&#xff1f;&#xff1f;&#xff1f;乱码格式了&#xff1a; 2.那么问题有3处需要注意&#xff1a; 第一&#xff1a;settings配置 第二&#xff1a;POM文件 第三&#xff1a;…

JavaWeb8-线程安全问题

目录 1.概念 1.1.单线程 1.2.多线程 2.导致线程不安全的5个因素 ①抢占式执行&#xff08;首要原因&#xff09; ②多个线程同时修改了同一个变量 ③非原子性操作 ④内存可见性 ⑤指令重排序 线程优点&#xff1a;加速程序性能。线程缺点&#xff1a;存在安全问题。 1…

深入理解设备像素比

文章目录参考描述像素分辨率显示分辨率图像分辨率物理分辨率分辨率单位&#xff08;仅部分&#xff09;DPIPPI设备像素比设备物理像素设备独立像素设备像素比产生放大与缩小尾声参考 项目描述关于物理像素、逻辑像素&#xff08;css像素&#xff09;、分辨率、像素比的超详细讲…

信源分类及数学模型

本专栏包含信息论与编码的核心知识&#xff0c;按知识点组织&#xff0c;可作为教学或学习的参考。markdown版本已归档至【Github仓库&#xff1a;information-theory】&#xff0c;需要的朋友们自取。或者公众号【AIShareLab】回复 信息论 也可获取。 文章目录信源分类按照信源…

Tomcat 线上调优记录

原始Tomcat配置 启动参数Plaintext-Xms256m -Xmx512m -XX:MaxPermSize128m Tomcat 参数配置XML<Executor name"tomcatThreadPool" namePrefix"catalina-exec-" maxThreads"1500" minSpareThreads"50" maxIdleTime"600000&q…

传感器原理及应用期末复习汇总(附某高校期末真题试卷)

文章目录一、选择题二、填空题三、简答题四、计算题一、选择题 1.下列哪一项是金属式应变计的主要缺点&#xff08;A&#xff09; A、非线性明显 B、灵敏度低 C、准确度低 D、响应时间慢 2.属于传感器动态特性指标的是&#xff08;D&#xff09; A、重复性 B、线性度 C、灵敏…

Centos7安装Python3

前言系统版本&#xff1a;Centos7.6python版本&#xff1a; python 3.10.4下载python下载链接&#xff1a;直通车找到对应版本的python安装包,这里以python 3.10.4为例点击3.10.4版本的链接&#xff0c;拉到最下面找到Files中对应的linux安装包鼠标右键复制下载链接登录linux系…

西电_数字信号处理二_学习笔记

文章目录【 第1章 离散随机信号 】【 第2章 维纳滤波 】【 第3章 卡尔曼滤波 】【 第4章 自适应滤波 】【 第5章 功率谱估计 】这是博主2022秋季所学数字信号处理二的思维导图&#xff08;软件是幕布&#xff09;&#xff0c;供大家参考&#xff0c;如内容上有不妥之处&#xf…

面试题记录

Set与Map的区别 map是键值对&#xff0c;set是值的集合。键&#xff0c;值可以是任何类型map可以通过get获取&#xff0c;map不能。都能通过迭代器进行for…of遍历set的值是唯一的&#xff0c;可以做数组去重&#xff0c;map&#xff0c;没有格式限制&#xff0c;可以存储数据…

Lesson4---Python语言基础(2)

4.1 内置数据结构 4.1.1 序列数据结构&#xff08;sequence&#xff09; 成员是有序排列的每个元素的位置称为下标或索引通过索引访问序列中的成员Python中的序列数据类型有字符串、列表、元组 “abc” ≠ “bac” 4.1.1.1 创建列表和元组 Python中的列表和元组&#xff0c…

阶段二10_面向对象高级_分类分包思想和案例环境搭建

一.分类思想 1.分类思想概念&#xff1a; 分工协作&#xff0c;专人干专事 2.信息管理系统分类[案例] Student 类-------------------->标准学生类&#xff0c;封装键盘录入的学生信息&#xff08;id , name , age , birthday&#xff09; StudentDao 类-----------------&…