架构设计

AI辅助编程

索引

step1 先决条件,科学上网,插件选择

经过试用,阿里的通义灵码github copilot 是当前效果比较好的两个插件。阿里的通义灵码不需要科学上网,copilot需要科学上网。

科学上网请参考,https://iovhm.com/book/books/cee63/page/9872e

step2 安装插件

idea(java方向)

截止至2024年2月28日,github Copilot 暂时不支持idea 内联聊天,但是可以支持代码生成。需要idea2023版本以上。

要获得github Copilot的体验权限,请向直属项目经理申请

不建议同时安装通义灵码和Copilot,可能产生冲突

依次点击 file -> settings - > plugins 搜索 tongyi 和 github copilot 进行安装

需要用到您的个人github账号,请自行至github注册,请使用企业邮箱注册,并将用户名设置为姓名拼音,如果用户名被占用,可以加上后缀vp ,例如 youname-vp,要开通github copilot使用权限,请向直属项目经理申请

需要用到您的个人阿里云账号,请自行至阿里云注册

github 可能时不时抽风,需要对IDEA进行设置。依次点击 file > settings > Appearance & Behavior > System settings > HTTP Proxy ,填入你的科学上网设置

对插件进行设置和开启/关闭一些功能

vscode(前端方向)

点击左侧扩展图标,搜索tongyigithub copilot

插件安装完成后,会在左侧多出对应的图标,点击相关图标既可使用对应的功能。

插件安装完成后,会在左侧多出对应的图标,点击相关图标既可使用对应的功能。

直接使用网页版chatgpt

网址:https://chat.openai.com/,需要魔法科学上网,openai需要登录,请自行注册。请使用gmail,hotmail邮箱注册,尽量不要使用国内的邮箱,以免被封

建设数据中台、业务中台、物联中台,三中台的必要性

在过去的时间里,我们建设了大量的软件信息化系统、硬件基础设施。在设计和建设时,信息化系统按使用部门的要求建设,重点解决使用部门的痛点,软、硬件协作、独立完成一项工作,软硬件系统作为一个重要工具存在。

随着时间的推移,信息化系统由不同的厂家、在不通的时间、采用不同技术构建,系统和系统之间存在数据不通、技术障碍等因素不能协作;功能存在重复,相同的数据在各信息化系统存在不一致的版本。

信息化系统犹如一个个高大的烟囱,可以独立处理业务功能,却无法组合和发挥更大价值,大量数据闲置,未形成数据资产。信息化停留在工具层面,未能对顶层决策带来有效的支撑,信息化系统呈孤岛态势。

在此背景下,在新的信息化建设规划中,我们提出,在有条件的情况下,优先建设数据中台、业务中台、物联中台,夯实基础,避免过去遇到的问题。 数据中台:通过多种技术手段,直接读取数据库、API采集、导入等多种方式,将同构、异构的数据孤岛,重新汇聚入湖(数据湖);通过对数据的比对、清洗、转换、标准化提炼,形成新的有价值数据;使用数据目录共享和对外发布数据,让其他系统可复用;通过对数据标准和源头的确定,使各系统的数据同源(口径一致)。通过对数据标准的制定,让数据交换共享更便利、不受时间、技术的限制。

因为有了全貌、全量的数据,使信息化系统从工具层面,升华到可决策、可视化、可经营、可管理层面。让信息化系统发挥更大的价值。

业务中台:通过对业务系统的共性功能提取,形成可复用的能力;对共性业务能力集中管理;对共性业务能力目录盘点,识别出既有能力,缺失能力,对信息能力查漏补缺;检索共性业务目录,在新的信息化系统规划阶段,既能有效控制建设范围,避免重新建设,降低新建信息化系统的范围,提高新建信息化的速度,最终达到降本增效的目的。

物联中台:通过对多种设备的抽象、模板、接入、管理,将物联设备产生的数据,抽象到数据中台,将物联能力抽象到业务中台。物联设备不再是独立的存在,将设备数字化后使其拥有更多的能力;根据多种策略,进行联动、告警、控制,实时反映设备状况,使设备也能成为生产经营决策的重要组成部分。

关于xx实验室信息化系统建设的必要性

当前xxx实验室弱电与智能设备已经初步安装完成。实验室做为一个特殊的区域,在有效持续运营方面还存在信息化管理手段缺失。



人人框架微服务版开发环境和安装部署

renren微服务框架需要jdk 17 ,nodejs 18+ 请注意版本选择

安装mysql数据库

先安mysql装数据库,并创建两个database,一个用于nacos,一个用于项目,初始化nacos数据结构和数据。

nacos-server-2.2.3 数据库初始化脚本:nacos-2.2.3-mysql-schema.sql

安装部署nacos

官方网址:https://nacos.io/zh-cn/docs/what-is-nacos.html
github地址:https://github.com/alibaba/nacos
github数据库初始化脚本:https://github.com/alibaba/nacos/blob/master/distribution/conf/mysql-schema.sql

# 镜像
# 根据需要,开放8848和9848端口

nacos/nacos-server:v2.2.3


# 设置环境变量

# 系统(集群)启动方式 ,cluster:集群,standalone:单机
MODE: standalone

# 数据库名称                       
MYSQL_SERVICE_DB_NAME = renren_cloud_nacos

# 数据地址
MYSQL_SERVICE_HOST = 192.168.0.10

# 数据库密码
MYSQL_SERVICE_PASSWORD = <your password>

# 数据库端口
MYSQL_SERVICE_PORT: 33306

# 数据用用户名
MYSQL_SERVICE_USER: root

# 主机模式,ip:ip地址,host:主机名
PREFER_HOST_MODE: ip

# 数据库类型
SPRING_DATASOURCE_PLATFORM: mysql

安装部署redis

version: "3"
services:
  redis:
    image: redis:6.2.6
    restart: always # 自动重启
    ports:
      - 56301:6379
    command: redis-server --appendonly yes --requirepass <your password>

编写Dockerfile

FROM openjdk:17
EXPOSE 8080

# VOLUME /tmp
ADD target/renren-admin-server.jar /app.jar
CMD ["java","-jar","/app.jar"]

server {
    listen       80;
    #listen 443 ssl;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/log/host.access.log  main;

    #ssl_certificate     /home/ssl/server.crt;
    #ssl_certificate_key /home/ssl/server.key;

    root /usr/share/nginx/html;
    index index.html;

    location / {
        # 不缓存首页,解决VUE单页面发版后不生效
        add_header Cache-Control "no-cache no-store must-revalidate proxy-revalidate,max-age=0";
        add_header Last-Modified $date_gmt;
        # 这个有顺序,需要加在后面
        etag off;
    }
}
FROM nginx:latest
EXPOSE 80

COPY ./dist /usr/share/nginx/html
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf

#!/bin/bash

# 登录
docker login -u <your name> -p <your password> swr.cn-south-1.myhuaweicloud.com

# 打包
docker build -t swr.cn-south-1.myhuaweicloud.com/vp-park/park-baseline/<your image name>:v1.0 ./

# 推送
docker push swr.cn-south-1.myhuaweicloud.com/vp-park/park-baseline//<your image name>:v1.0

编写统一配置文件

在k8s新建ConfigMap,然后再java服务中引用配置:增加环境变量,选ConfigMap,选择需要的configMap名称

# 服务地址
nacos_host = nacos-server

# 名字空间
nacos_namespace = public

# 端口
nacos_port = 8848

进一操作请参考rancher使用手册

rancher2.7使用手册

上传nacos配置文件

打开nacos管理界⾯(http://localhost:8848/nacos) ,初始⽤户名nacos,密码nacos,登录之后,如下所示:

导⼊nacos配置⽂件,配置⽂件在项⽬⾥,⽂件名为:【~/doc/nacos/nacos_config.zip】,如下所示:

在nacos⾥,还需要修改datasource.yaml,如:redis、MySQL信息,如下所示:

安装部署代码生成器

安装部署代码生成器:人人框架代码生成器安装部署

人人框架代码生成器安装部署

代码生成器能生成基础的单表、实体、列表、CURD,减少毫无意义的工作,还能保证基础代码的一致性,强制需要使用。

代码生成器生成的代码路径存放运本机磁盘,需要在本机同时运行基础项目、代码生成器项目、前端项目;后端开发人员不会运行前端项目,前端开发人员不会运行java项目。会造成一定的麻烦。

比较好的做法是将代码生成器部署到服务器给大家共享,再用web版本的vscode浏览、将代码复制回来粘贴到项目中。

安装部署代码生成器

源代码位置:/renren-module/renren-devtools

# vi Dockerfile
FROM openjdk:17
EXPOSE 8080

# VOLUME /tmp
ADD target/renren-devtools.jar /app.jar
CMD ["java","-jar","/app.jar"]


#!/bin/bash

# 登录
docker login -u <your name> -p <your password> swr.cn-south-1.myhuaweicloud.com

# 打包
docker build -t swr.cn-south-1.myhuaweicloud.com/vp-park/park-weihai/renren-devtools-server:v1.0 ./

# 推送
docker push swr.cn-south-1.myhuaweicloud.com/vp-park/park-weihai/renren-devtools-server:v1.0

源代码位置:/renren-module/renren-devtools/db/mysql.sql

设置代码生成器生

注意代码生成路径 ,如果是容器运行,请将 /data 目录挂载到磁盘或者NFS

模板位置:/renren-module/renren-devtools/db/template 将模板粘贴到对应项

注意代码生成路径 ,如果是容器运行,请将 /data 目录挂载到磁盘或者NFS

运行code-server

code-server具有极高的权限,开发完成后应该删除相关部署、设置复杂密码

version: "2.1"
services:
  code-server:
    image: swr.cn-south-1.myhuaweicloud.com/vp-whdev/all-in-devops/vscode-server:latest
    container_name: code-server
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
      - PASSWORD=password # web gui password
      - DEFAULT_WORKSPACE=/data # 打开 web gui 时默认打开文件夹
    volumes:
      - /path/to/appdata/config:/data    # 将前面设置的代码生成器映射到 code-server
    ports:
      - 8443:8443
    restart: unless-stopped

# 设置兖州后需要登录,登录页位于网站根目录,需要为 code-server 单独设置一个域名

浏览code-server将代码复制到项目

软件增加license管理

需要增加2个文件,建议目录放置在config目录

FilterConfig.java

全局过滤器配置 ,对需要拦截的路径进行配置

package cn.vppark.whdev.license_test.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Filter配置
 *
 * @author Mark sunlightcs@gmail.com
 */
@Configuration
public class FilterConfig {

    @Value("${app.license:}")
    private String license;

    @Bean
    public FilterRegistrationBean licenseFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new LicenseFilter(license));
        registration.addUrlPatterns("/*");
        registration.setName("licenseFilter");
        registration.setOrder(Integer.MAX_VALUE);
        return registration;
    }

}

LicenseFilter.java

package cn.vppark.whdev.license_test.config;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Date;

/**
 * @Description: 证书校验
 * @date: 2024年4月9日 下午12:39:48
 * @Copyright:
 */
public class LicenseFilter implements Filter {

    private String license;

    /**
     * 公钥
     */
    private final String publicKey = "your public key";

    public LicenseFilter(String license) {
        this.license = license;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //设置返回参数
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("text/html; charset=UTF-8");


        try {
            if (this.license == null || license.isEmpty()) {
                // 优先使用配置文件的证书,因为调试运行的时候,会清空target目录,导致证书文件丢失
                this.license = IOUtils.toString(new FileInputStream(getLicensePath()), StandardCharsets.UTF_8);
            }
            //解密证书
            String licenseContent = verifyLicense(this.license);
            JSONObject json = JSONObject.parseObject(licenseContent);
            //获取过期时间
            Date expire = json.getDate("expire");
            httpServletResponse.setHeader("license-expire", expire.toString());
            //判断证书是否过期
            if (expire.getTime() < System.currentTimeMillis()) {
                httpServletResponse.getWriter().write("软件授权过期!");
                return;
            }
            httpServletResponse.setStatus(HttpServletResponse.SC_OK);
        } catch (FileNotFoundException e) {
            httpServletResponse.getWriter().write("证书文件丢失,请联系管理员!");
            e.printStackTrace();
            return;
        } catch (Exception e) {
            httpServletResponse.getWriter().write("证书文件已损坏,请联系管理员!");
            e.printStackTrace();
            return;
        }
        chain.doFilter(request, response);
    }

    /**
     * @throws
     * @Description: 根据公钥解密字符串
     * @param: licenseContent 证书内容
     */
    public String verifyLicense(String licenseContent) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey))));
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(licenseContent));
        String decryptedString = new String(decryptedBytes);
        // 打印解密后的证书内容
        System.out.println("licenseContent: " + decryptedString);
        return decryptedString;
    }

    /**
     * @throws
     * @Description: 获取license完整路径
     */
    private String getLicensePath() throws URISyntaxException {

        URL classUrl = this.getClass().getProtectionDomain().getCodeSource().getLocation();
        URI uri = classUrl.toURI();
        Path path;
        if (uri.getScheme().equals("jar")) {
            // 如果是一个JAR URL,我们需要找到JAR文件本身
            String jarPath = uri.getSchemeSpecificPart();
            jarPath = jarPath.substring(0, jarPath.indexOf('!'));
            path = Paths.get(new URI(jarPath)).getParent();
        } else {
            // 如果不是JAR URL,则可能是直接从文件系统加载的类
            path = Paths.get(uri);
        }
        String licensePath = path.resolve("license/license").toString();
        // 打印证书路径
        System.out.println("licensePath: " + licensePath);
        return licensePath;
    }

}

各应用平台注册地址

信息系统安全定级说明

一、确定安全保护等级

依据《信息安全等级保护管理办法》(公通字 [2007] 43 号)《关于开展全国重要信息系统安全等级保护定级工作的通知》 (公信安[2007]861 号)、《信息安全技术网络安全等级保护定级指南》(GB/T22240-2020) 等文件规定和国家政务外网的统一要求,所建设的需要信息系统的安全保护等级。

二、安全等级保护等级确定流程

确定信息系统安全保护等级的一般流程如下:

信息安全保护等级矩阵表如下所示:

三、定级对象受侵害客体

定级对象受到破坏时所侵害的客体包括国家安全、社会秩序、公众利益以及公民、法人和其他组织的合法权益。

运维支持考试题

运维知识考试题

高并发性能优化设置

tomcat设置



server:
  tomcat:
    max-connections: 10000 # 最大连接数,默认值为8192,一般情况下可以不用修改
    threads:
      max: 1000 # 处理请求最大线程数,默认值是200,既tomcat的默认并发是200
      min-spare: 100 # 处理请求的最小空闲线程,即使没有请求需要处理,也保留,以便于快速响应连接请求
    accept-count: 100 # 额外的阻塞的处理请求数,当所有线程都在使用,还可以额外接受的请求,并放入请求队列,待有可用线程后立刻处理,一般不超过最大线程数的10%

accept-count: 100:当所有可用的处理线程都在使用时,Tomcat能够接受的额外请求的数量,并将这些请求放入队列中等待处理。意味着如果所有的处理线程都在忙,Tomcat还可以接受额外的100个请求,但这些请求需要等待直到有线程变得可用。

连接数与线程数,根据TCP原理,是先建立连接,再处理请求,如果在连接超时后还没有处理请求,则断开连接,例如常见的timeout,socket closed

nginx设置

主要是开启gzip压缩,减少流量

    # 开启gzip压缩
    gzip on;
    # 压缩哪些文件类型
    gzip_types text/plain  text/css application/json text/javascript application/javascript;
    # 最小压缩大小,小于这个大小不压缩,单位是字节
    gzip_min_length 1000;
    # 压缩率,1-9,数字越大压缩的越好,但是也越消耗CPU
    gzip_comp_level 6;
    # 是否在http header中添加Vary: Accept-Encoding,建议开启
    gzip_vary on;
    # 禁止IE6使用gzip,因为这些浏览器不支持gzip压缩
    gzip_disable "MSIE [1-6]\.";
    # 在特定条件下对代理服务器的响应进行压缩
    gzip_proxied expired no-cache no-store private auth;
    # 缓冲区数量和大小,设置了16个8KB的缓冲区
    gzip_buffers 16 8k;
    # 最小http版本,低于这个版本不压缩
    gzip_http_version 1.1;

Springboot程序内优化

jmeter自身优化

修改HEAP大小,一般为主机内存的一半

if not defined HEAP (
    rem See the unix startup file for the rationale of the following parameters,
    rem including some tuning recommendations
    set HEAP=-Xms1g -Xmx8g -XX:MaxMetaspaceSize=512m
)

从bat启动时为英文修改

create-springboot-project

创建项目目录结构


# 创建项目结构
mkdir -p src/main/java
mkdir -p src/main/resources
mkdir -p src/test/java
mkdir -p src/test/resources

# 创建第一个包
mkdir -p src/main/java/com/iovhm/hello

# 创建4个环境的配置文件
touch src/main/resources/application.yml
touch src/main/resources/application-dev.yml
touch src/main/resources/application-tet.yml
touch src/main/resources/application-prod.yml


pom.xml


touch ./pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!--如果项目没有层级关系,请删除下面的节点-->
    <!--<parent>-->
    <!--<groupId>cn.vpclub</groupId>-->
    <!--<artifactId>spring-boot-starters</artifactId>-->
    <!--<version>1.4.15</version>-->
    <!--</parent>-->
    <!--如果项目没有层级关系,请删除上面的节点-->

    <!---项目组,必须-->
    <groupId>com.iovhm</groupId>
    <!--项目唯一ID,必须-->
    <artifactId>hello-springboot</artifactId>
    <!--项目版本,必须-->
    <version>1.0.0</version>
    <!--生成类型,必须,可选参数pom,jar,一般情况下使用jar-->
    <packaging>jar</packaging>

    <!---属性设置,项目属性不是必须的,但是建议写上便于精确控制-->
    <properties>
        <java.version>17</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.test.skip>true</maven.test.skip>
        <spring-boot.version>3.4.1</spring-boot.version>
    </properties>
    <!-- 统一spring boot 版本 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!--spring boot 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>
        <!--mybatis支持-->
        <!--如果使用 mybatis plus 请将 mybatis 支持 去掉, mybatis plus 会自动处理依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.4</version>
        </dependency>
        <!-- mybatils plus 支持-->
        <!--如果使用 mybatis plus 请将 mybatis 支持 去掉, mybatis plus 会自动处理依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.2</version>
        </dependency>
        <!--lombok支持-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.36</version>
        </dependency>
        <!--redis支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>
        <!-- hu-tool工具 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.24</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

创建Application.java


touch src/main/java/com/iovhm/Application.java


package com.iovhm;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

创建HelloController.java


touch src/main/java/com/iovhm/hello/HelloController.java



package com.iovhm.hello;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

// 全局访问路径
@RequestMapping("/")
// 标记为本类为Controller类
@RestController
public class HomeController {
    // 在全局访问路径的基础上,成员方法的访问路径
    @RequestMapping("/")
    public String hello() {
        return "hello,world";
    }
}


如果没有配置数据库,可能导致程序无法运行,可以先禁用数据库自动装配

# application.yml
spring:
  autoconfigure:
    # 阻止Spring Boot自动配置数据源
    exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

下一章

springboot 集成mybatis-plus https://qq829.cn/book/books/bbcbf/page/springboot-mybatis-plus

springboot 集成 mybatis

创建项目

create-springboot-project: https://iovhm.com/book/books/bbcbf/page/create-springboot-project

更多的时候我们使用mybatis-plus,本章可以直接跳过

springboot 集成mybatis-plus https://iovhm.com/book/books/bbcbf/page/springboot-mybatis-plus

集成数据库

注意:如果你是按create-springboot-project创建的项目,需要删除配置



# application.yml
spring:
  autoconfigure:
    # 阻止Spring Boot自动配置数据源
    exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

约定与名词解释

确定项目结构

好的结构易于项目维护

增加pom依赖



        <!--mysql支持-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        <!--mybatis支持-->
        <!--如果使用 mybatis plus 请将 mybatis 支持 去掉, mybatis plus 会自动处理依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>


增加数据库连接配置


spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:33306/dataway?charset=utf8mb4&serverTimezone=Asia/Shanghai
    username: <root>
    password: <password>
    driver-class-name: com.mysql.cj.jdbc.Driver


创建数据库


创建Entity(实体)与数据库结构保持一致

在实体上增加注解 @Data


@Data
public class UserEntity {
    private Integer id;
    private String name;
    private Integer age;
    private String email;
}

创建DTO

由于DTO主要负责业务数据传输,DTO可以先直接继承于Entity,再根据业务需要增加字段,注意 @Data 注解


@Data
public class UserDTO extends UserEntity {

}

创建Dao或者Mapper

注意 @Mapper 注解,Mapper是用来操作数据库的,使用的数据结构应该是Entity,虽然用DTO也可以使用,但还是应该遵守编程规范



@Mapper
public interface UserMapper {

    @Select("SELECT * FROM user")
    List<UserEntity> selectList();
}

编写Controller



// 全局访问路径
@RequestMapping("/")
// 标记为本类为Controller类
@RestController
@Slf4j
public class HomeController {

    private final UserMapper userMapper;

    public HomeController(UserMapper userMapper) {
        this.userMapper = userMapper;
    }
    
    @RequestMapping("/")
    public Object hello() {
        List list =userMapper.selectList();
        return list;
    }
}

修改Application

注意 @MapperScan("com.iovhm.dataway.**.dao")


@SpringBootApplication
@MapperScan("com.iovhm.dataway.**.dao")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}


springboot 集成mybatis-plus

创建项目

create-springboot-project: https://iovhm.com/book/books/bbcbf/page/create-springboot-project

集成数据库

注意:如果你是按create-springboot-project创建的项目,需要删除配置



# application.yml
spring:
  autoconfigure:
    # 阻止Spring Boot自动配置数据源
    exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

约定与名词解释

确定项目结构

增加pom依赖


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-bom</artifactId>
                <version>3.5.10</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

        <!--mysql支持-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        <!--mybatis支持-->
        <!--如果使用 mybatis plus 请将 mybatis 支持 去掉, mybatis plus 会自动处理依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <version>3.5.10</version>
        </dependency>


增加数据库连接配置


spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:33306/dataway?charset=utf8mb4&serverTimezone=Asia/Shanghai
    username: <root>
    password: <password>
    driver-class-name: com.mysql.cj.jdbc.Driver


创建数据库


创建Entity(实体)与数据库结构保持一致

注意 @Data@TableName("user") 注解


@Data
@TableName("user")
public class UserEntity {
    private Integer id;
    private String name;
    private Integer age;
    private String email;
}


创建DTO

由于DTO主要负责业务数据传输,DTO可以先直接继承于Entity,再根据业务需要增加字段,注意 @Data 注解


@Data
public class UserDTO extends UserEntity {

}

创建DAO

注意继承于Mybatis-plus的BaseMapper,以及Mapper里面的Entity。DAO是用来操作数据库的类,使用的数据结构应该是Entity,虽然继承于EEntity的DTO也可以使用,但还是应该遵守编程规范


@Mapper
public interface UserDao extends BaseMapper<UserEntity> {
}


创建Service


public interface UserService {

    List<UserDTO> selectList();

    List<UserDTO> selectList2();
}


@Service
public class UserServiceImpl implements UserService {

    private final UserMapper userMapper;
    private final UserDao userDao;

    public UserServiceImpl(UserMapper userMapper, UserDao userDao) {
        this.userMapper = userMapper;
        this.userDao = userDao;
    }

    @Override
    public List<UserDTO> selectList() {
        List list = userDao.selectList(null);
        return list;
    }

    @Override
    public List<UserDTO> selectList2() {
        return userMapper.selectList();
    }

}



创建Controller



// 全局访问路径
@RequestMapping("/")
// 标记为本类为Controller类
@RestController
@Slf4j
public class HomeController {

    private UserService userService;

    public HomeController(UserService userService) {
        this.userService = userService;
    }

    @RequestMapping("/")
    public Object hello() {
        List list = userService.selectList();
        List list2 = userService.selectList2();
        return Map.of("list", list, "list2", list2);
    }
}


修改Application

注意 @MapperScan("com.iovhm.dataway.**.dao")


@SpringBootApplication
@MapperScan("com.iovhm.dataway.**.dao")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}


springboot 集成mybatis-plus通用CRUD

CRUD

mybatis-plust提供了通用CRUD类 IService ,可以直接继承改接口并实现ServiceImpl既可以实现通用CURD

实现自定义的CURD基础类

根据研发规范,IService返回的数据结构是Entity类型,与规范要求的业务代码应该返回DTO不符,可以实现自定义基类来解决

创建并实现自定义基类
/*
 * @Description:自定义CRUD基类
 * @Author: donglietao@163.com
 * @ENT: 实体类
 * @DTO: DTO类
 */

public interface CrudService<ENT, DTO> {

    List<DTO> list(@Param("ew") Wrapper<ENT> queryWrapper);
}

主要是用到了BaseMap<ENT>类型


/*
* @description: CrudServiceImpl
* @author: donglietao@163.com
* */
@Service
@Slf4j
public class CrudServiceImpl<ENT, DTO> implements CrudService<ENT, DTO> {


    // 主要是这个地方,可以使用泛型创建mapper
    @Autowired
    protected BaseMapper<ENT> baseMapper;

    private Class<DTO> getDTOClass() {
        return (Class<DTO>) ReflectionKit.getSuperClassGenericType(this.getClass(), CrudService.class, 1);
    }

    public List<DTO> list(@Param("ew") Wrapper<ENT> queryWrapper) {

        List<ENT> list = baseMapper.selectList(queryWrapper);
        if (list == null) {
            return null;
        }
        // 此处可以简化,将方法抽离到工具类
        List<DTO> listDTO = new ArrayList<>(list.size());
        Class<DTO> dtoClass = getDTOClass();
        for (ENT ent : list) {
            try {
                DTO dto = dtoClass.newInstance();
                BeanUtils.copyProperties(ent, dto);
                listDTO.add(dto);
            } catch (Exception ex) {
                log.error("CrudServiceImpl.list() error:", ex);
            }
        }
        return listDTO;
    }
}


在自定义服务类上实现通用CRUD基类

public interface UserService extends CrudService<UserEntity, UserDTO> {
}

@Service
public class UserServiceImpl extends CrudServiceImpl<UserEntity, UserDTO> implements UserService {

}


进一步简化代码

类型转换工具类

将转换类独立为工具类

@Slf4j
public class ConvertUtils {

    public static <T> T sourceToTarget(Object source, Class<T> target) {
        if (source == null) {
            return null;
        }
        T targetObject = null;
        try {
            targetObject = target.newInstance();
            BeanUtils.copyProperties(source, targetObject);
        } catch (Exception e) {
            log.error("convert error ", e);
        }

        return targetObject;
    }

    public static <T> List<T> sourceToTarget(Collection<?> sourceList, Class<T> target) {
        if (sourceList == null) {
            return null;
        }

        List targetList = new ArrayList<>(sourceList.size());
        try {
            for (Object source : sourceList) {
                T targetObject = target.newInstance();
                BeanUtils.copyProperties(source, targetObject);
                targetList.add(targetObject);
            }
        } catch (Exception e) {
            log.error("convert error ", e);
        }

        return targetList;
    }
}
使用类型转换工具简化调用

@Service
@Slf4j
public class CrudServiceImpl<ENT, DTO> implements CrudService<ENT, DTO> {

    @Autowired
    protected BaseMapper<ENT> baseMapper;

    private Class<DTO> getDTOClass() {
        return (Class<DTO>) ReflectionKit.getSuperClassGenericType(this.getClass(), CrudService.class, 1);
    }

    public List<DTO> list(@Param("ew") Wrapper<ENT> queryWrapper) {

        List<ENT> list = baseMapper.selectList(queryWrapper);
        // 使用转换工具简化调用
        List<DTO> listDTO = ConvertUtils.sourceToTarget(list, getDTOClass());
        return listDTO;
    }
}

cline&&AI编程插件

vscode 插件安装

插件面板搜索cline,可以看到有英文版和中文版

https://dashscope.aliyuncs.com/compatible-mode/v1