当前位置: 首页 > news >正文

代码生成器实现

代码生成器实现

实现封装元数据的工具类实现代码生成器的代码编写掌握模板创建的

构造数据模型

需求分析

借助Freemarker机制可以方便的根据模板生成文件,同时也是组成代码生成器的核心部分。对于Freemarker而

言,其强调 数据模型 + 模板 = 文件 的思想,所以代码生成器最重要的一个部分之一就是数据模型。在这里数据

模型共有两种形式组成:

  • 数据库中表、字段等信息

针对这部分内容,可以使用元数据读取并封装到java实体类中

  • 用户自定义的数据

为了代码生成器匹配多样的使用环境,可以让用户自定义的数据,并且以key-value的形式配置到properties文件中

接下来针对这两方面的数据进行处理

PropertiesUtils工具类自定义数据

通过PropertiesUtils工具类,统一对properties文件夹下的所有.properties文件进行加载,并存入内存中

package cn.itcast.generate.utils;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;/*** 需要将自定义的配置信息写入到properties文件中*      配置到相对于工程的properties文件夹下*/
public class PropertiesUtils {public static Map<String,String> customMap = new HashMap<>();static {File dir = new File("properties");try {List<File> files = FileUtils.searchAllFile(new File(dir.getAbsolutePath()));for (File file : files) {if(file.getName().endsWith(".properties")) {Properties prop = new Properties();prop.load(new FileInputStream(file));customMap.putAll((Map) prop);}}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) {PropertiesUtils.customMap.forEach((k, v)->{System.out.println(k+"--"+v);});}
}

骚戴理解:这里的PropertiesUtils工具类的作用就是把properties文件夹下的所有.properties文件读取处理,主要就是用于用户自定义的数据,可能有的数据不是从数据库里面读取的,所以就可以定义在properties配置文件里,然后读取配置文件获取数据,再加载到模板里面

导入代码生成器依赖的配置文件

#SQL类型和java类型替换规则
VARCHAR=String
BIGINT=Long
INT=Integer
DATE=java.util.Date
DATETIME=java.util.Date
DOUBLE=Double
TEXT=String
VARCHAR2=String
NVARCHAR2=String
NUMBER=Long
CHAR=String
MEDIUMTEXT=String
TINYINT=Integer
LONGTEXT=String#Table的前缀或者后缀
tableRemovePrefixes=tb_,co_,pe_,bs_

元数据处理

加载指定数据库表,将表信息转化为实体类对象(Table)

package cn.itcast.generate.utils;import cn.itcast.generate.entity.Column;
import cn.itcast.generate.entity.DataBase;
import cn.itcast.generate.entity.Table;import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;public class DataBaseUtils {//获取到mysql中所有的数据库名称//获取数据库连接public static Connection getConnection(DataBase db) throws Exception {Properties props = new Properties();props.put("remarksReporting","true");//获取数据库的备注信息props.put("user",db.getUserName());props.put("password",db.getPassWord());Class.forName(db.getDriver());//注册驱动return DriverManager.getConnection(db.getUrl(),props);}//获取数据库列表public static List<String> getSchemas(DataBase db) throws Exception {//1.获取元数据Connection connection = getConnection(db);DatabaseMetaData metaData = connection.getMetaData();//2.获取所有数据库列表ResultSet rs = metaData.getCatalogs();List<String> list = new ArrayList<>();while (rs.next()) {list.add(rs.getString(1));}rs.close();connection.close();return list;}/*** 获取数据库中的表和字段构造实体类*      Table对象**  1.参数*      DataBase 数据库对象*  2.操作步骤*      1.获取连接*      2.获取databasemetaData*      3.获取当前数据库中的所有表*      4.获取每个表中的所有字段*      5.封装到java对象中即可*/public static List<Table> getDbInfo(DataBase db) throws  Exception {//1.获取连接Connection connection = getConnection(db);//2.获取元数据DatabaseMetaData metaData = connection.getMetaData();//3.获取当前数据库中的所有表ResultSet tables = metaData.getTables(null, null, "pe_permission", new String[]{"TABLE"});List<Table> list = new ArrayList<>();while (tables.next()) {Table tab = new Table();//i.表名String tableName = tables.getString("TABLE_NAME"); //bs_user  User//ii.类名String className = removePrefix(tableName);//iii.描述String remarks = tables.getString("REMARKS");//iiii.主键(主键可能是组合主键,有多个,所以primaryKeys是多个主键的set集合)ResultSet primaryKeys = metaData.getPrimaryKeys(null, null, tableName);String keys = "";while (primaryKeys.next()) {String keyname = primaryKeys.getString("COLUMN_NAME");keys += keyname+",";}tab.setName(tableName);tab.setName2(className);tab.setComment(remarks);tab.setKey(keys);//处理表中的所有字段ResultSet columns = metaData.getColumns(null, null, tableName, null);List <Column> columnList = new ArrayList<>();while (columns.next()) {Column cn = new Column();//构造Column对象//列名称String columnName = columns.getString("COLUMN_NAME"); //user_id  userId , create_time createTimecn.setColumnName(columnName);//属性名String attName = StringUtils.toJavaVariableName(columnName);cn.setColumnName2(attName);//java类型和数据库类型String dbType = columns.getString("TYPE_NAME");//VARCHAR,DATETIMEcn.setColumnDbType(dbType);String javaType = PropertiesUtils.customMap.get(dbType);cn.setColumnType(javaType);//备注String columnRemark = columns.getString("REMARKS");//VARCHAR,DATETIMEcn.setColumnComment(columnRemark);//是否主键String pri = null;if(StringUtils.contains(columnName ,keys.split(","))) {pri = "PRI";}cn.setColumnKey(pri);columnList.add(cn);}columns.close();tab.setColumns(columnList);list.add(tab);}tables.close();connection.close();return list;}public static String removePrefix(String tableName) {String prefix = PropertiesUtils.customMap.get("tableRemovePrefixes");//bs_,     tb_    , co_    ,String temp = tableName;  //bs_userfor(String pf : prefix.split(",")) {temp = StringUtils.removePrefix(temp,pf,true);}//temp = userreturn StringUtils.makeAllWordFirstLetterUpperCase(temp);}public static void main(String[] args) throws Exception {DataBase db = new DataBase("MYSQL","ihrm");db.setUserName("root");db.setPassWord("111111");List<Table> dbInfo = DataBaseUtils.getDbInfo(db);for (Table table : dbInfo) {List<Column> columns = table.getColumns();for (Column column : columns) {System.out.println(column);}}}
}

骚戴理解:removePrefix方法就是把数据库表名的前缀去掉,然后再转成首字母大写的实体类名称,表名前缀都写在了配置文件里面,所以先通过PropertiesUtils获取配置文件的前缀配置信息,如下所示,然后调用StringUtils的removePrefix方法去掉前缀,最后调用StringUtils的makeAllWordFirstLetterUpperCase方法转成首字母大写

#Table的前缀或者后缀
tableRemovePrefixes=tb_,co_,pe_,bs_

实现代码生成

需求分析

为了代码更加直观和易于调用,实现代码生成共有两个类组成:

  • UI界面统一调用的入口类:GeneratorFacade

方便多种界面调用,主要完成数据模型获取,调用核心代码处理类完成代码生成

  • 代码生成核心处理类:Generator

根据数据模型和模板文件路径,统一生成文件到指定的输出路径

模板生成

  • 配置统一调用入口类GeneratorFacade
package cn.itcast.generate.core;import cn.itcast.generate.entity.DataBase;
import cn.itcast.generate.entity.Settings;
import cn.itcast.generate.entity.Table;
import cn.itcast.generate.utils.DataBaseUtils;
import cn.itcast.generate.utils.PropertiesUtils;import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 1.采集用户UI界面输入的数据*      模板位置*      代码生成路径*      工程配置对象 setting*      数据库对象   DataBase* 2.准备数据模型*      1.自定义配置*      2.元数据*      3.setting* 3.调用核心处理类完成代码生成工作*      方法:Generator*/
public class GeneratorFacade {private String templatePath;private String outPath;private Settings settings;private DataBase db;private Generator generator;public GeneratorFacade(String templatePath, String outPath, Settings settings, DataBase db) throws Exception {this.templatePath = templatePath;this.outPath = outPath;this.settings = settings;this.db = db;this.generator = new Generator(templatePath,outPath);}/*** 1.准备数据模型* 2.调用核心处理类完成代码生成工作*/public void generatorByDataBase() throws  Exception {List<Table> tables = DataBaseUtils.getDbInfo(db);for (Table table : tables) {//对每一个Table对象进行代码生成/*** 数据模型* 调用Generator核心处理类*/Map<String,Object> dataModel = getDataModel(table);
//
//            for(Map.Entry<String,Object> entry:dataModel.entrySet()) {
//                System.out.println(entry.getKey() + "--" + entry.getValue());
//            }
//            System.out.println("------------------------");generator.scanAndGenerator(dataModel);}}/*** 根据table对象获取数据模型*/private  Map<String,Object> getDataModel(Table table) {Map<String,Object> dataModel = new HashMap<>();//1.自定义配置dataModel.putAll(PropertiesUtils.customMap);//2.元数据dataModel.put("table",table);  //table.name2//3.settingdataModel.putAll(this.settings.getSettingMap());//4.类型dataModel.put("ClassName",table.getName2());return dataModel;}
}

骚戴理解:通过DataBaseUtils.getDbInfo(db);获取到的是数据库表数据的元数据信息,getDataModel方法的作用是为了封装数据模型,数据模型结合模板生成代码,数据模型包括自定义配置、元数据、setting里面的数据,而ClassName只是为了方便后面拿这个类名而已,所以也封装在其中

  • 处理模板代码生成的核心类Generator
package cn.itcast.generate.core;import cn.itcast.generate.utils.FileUtils;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 代码生成器的核心处理类*      使用Freemarker完成文件生成*             数据模型 + 模板*  数据:*      数据模型*      模板的位置*      生成文件的路径**/
public class Generator {private String templatePath;//模板路径private String outPath;//代码生成路径private Configuration cfg;public Generator(String templatePath, String outPath) throws Exception {this.templatePath = templatePath;this.outPath = outPath;//实例化Configuration对象cfg = new Configuration();//指定模板加载器FileTemplateLoader ftl = new FileTemplateLoader(new File(templatePath));cfg.setTemplateLoader(ftl);}/*** 代码生成*      1.扫描模板路径下的所有模板*      2.对每个模板进行文件生成(数据模型)*/public void scanAndGenerator(Map<String,Object> dataModel) throws Exception {//1.根据模板路径找到此路径下的所有模板文件List<File> fileList = FileUtils.searchAllFile(new File(templatePath));//2.对每个模板进行文件生成for (File file : fileList) {executeGenertor(dataModel,file);}}/*** 对模板进行文件生成* @param dataModel : 数据模型* @param file      : 模板文件*            模板文件:c:com.ihrm.system.abc.java*/private void executeGenertor(Map<String,Object> dataModel,File file) throws Exception {//1.文件路径处理   (E:\模板\${path1}\${path2}\${path3}\${ClassName}.java)//templatePath : E:\模板\String templateFileName = file.getAbsolutePath().replace(this.templatePath,"");String outFileName = processTemplateString(templateFileName,dataModel);//2.读取文件模板Template template = cfg.getTemplate(templateFileName);template.setOutputEncoding("utf-8");//指定生成文件的字符集编码//3.创建文件File file1 = FileUtils.mkdir(outPath, outFileName);//4.模板处理(文件生成)FileWriter fw = new FileWriter(file1);template.process(dataModel,fw);fw.close();}public String processTemplateString(String templateString,Map dataModel) throws Exception {StringWriter out = new StringWriter();Template template = new Template("ts",new StringReader(templateString),cfg);template.process(dataModel,out);return out.toString();}public static void main(String[] args) throws Exception {String templatePath = "C:\\Users\\ThinkPad\\Desktop\\ihrm\\day13\\资源\\测试\\模板";String outPath = "C:\\Users\\ThinkPad\\Desktop\\ihrm\\day13\\资源\\测试\\生成代码路径";Generator generator = new Generator(templatePath, outPath);Map <String,Object> dataModel = new HashMap<>();dataModel.put("username","张三");generator.scanAndGenerator(dataModel);}}

骚戴理解:executeGenertor方法这段代码的作用是根据模板文件和数据模型生成代码文件。

1. String templateFileName = file.getAbsolutePath().replace(this.templatePath,"");

这行代码的作用是将模板文件的绝对路径中的模板根路径(即templatePath)替换为空字符串,得到模板文件名(不包含根路径)。

2. String outFileName = processTemplateString(templateFileName,dataModel);

这行代码的作用是将模板文件名中的变量替换为具体的值,得到输出文件的文件名。

3. Template template = cfg.getTemplate(templateFileName);

这行代码的作用是根据模板文件名,从Configuration对象中获取对应的Template对象。

4. template.setOutputEncoding("utf-8");

这行代码的作用是指定生成文件的字符集编码为UTF-8。

5. File file1 = FileUtils.mkdir(outPath, outFileName);

这行代码的作用是根据输出路径和输出文件名,创建输出文件。

6. FileWriter fw = new FileWriter(file1);

这行代码的作用是创建一个FileWriter对象,用于将模板处理后的结果输出到指定的文件中。

7. template.process(dataModel,fw);

这行代码的作用是将数据模型和FileWriter对象作为参数传入Template的process方法中,根据数据模型中的值,将模板文件中的变量替换为具体的值,并将输出结果写入FileWriter对象中。

8. fw.close();

这行代码的作用是关闭FileWriter对象,释放资源。

路径处理

使用字符串模板对文件生成路径进行统一处理

    public String processTemplateString(String templateString,Map dataModel) throws Exception {StringWriter out = new StringWriter();Template template = new Template("ts",new StringReader(templateString),cfg);template.process(dataModel,out);return out.toString();}

骚戴理解:这段代码是使用FreeMarker模板引擎来处理模板字符串,将模板字符串中的变量替换为具体的值,最终返回替换后的字符串。

1.StringWriter out = new StringWriter();

这行代码的作用是创建一个StringWriter对象,用于接收模板处理后的输出结果。

2.Template template = new Template("ts",new StringReader(templateString),cfg);

这行代码的作用是创建一个Template对象,用于解析模板字符串并进行变量替换。其中,第一个参数是模板名称(随意取名),第二个参数是StringReader对象,用于读取模板字符串,第三个参数是Configuration对象,用于配置模板引擎的相关参数。

3.template.process(dataModel,out);

这行代码的作用是将数据模型和输出流作为参数传入Template的process方法中,根据数据模型中的值,将模板字符串中的变量替换为具体的值,并将输出结果写入输出流。

4.return out.toString();

这行代码的作用是将输出流中的内容转换为字符串,并返回该字符串。

制作模板

模板制作的约定

  • 模板位置

模板统一放置到相对于当前路径的模板文件夹下

  • 自定义数据

自定义的数据以 .propeties 文件(key-value)的形式存放入相对于当前路径的 properties 文件夹下

  • 数据格式

名称

说明

author

作者

project

工程名

path1

包名1

path2

包名2

path3

包名3

pPackage

完整包名

projectComment

工程描述

ClassName

类名

table

数据库信息

table中数据内容:

name

表名

comment

表注释

key

表主键

columns

所有列信息

columnName

字段列名

columnName2

属性名

columnType

java类型

columnDbType

数据库类型

columnComment

注释

columnKey

是否主键

需求分析

制作通用的SpringBoot程序的通用模板

  • 实体类

类路径,类名,属性列表(getter,setter方法)

  • 持久化层

类路径,类名,引用实体类

  • 业务逻辑层

类路径,类名,引用实体类,引用持久化层代码

  • 视图层

类路径,类名,引用实体类,引用业务逻辑层代码,请求路径

  • 配置文件

pom文件,springboot配置文件

SpringBoot通用模板

实体类

package ${pPackage}.pojo;import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;@Entity
@Table(name = "${table.name}")
public class ${ClassName} implements Serializable {//这里table.columns是因为上面数据模型里面放的是 dataModel.put("table",table);<#list table.columns as column><#if column.columnKey??>@Id</#if>private ${column.columnType} ${column.columnName2};</#list><#list table.columns as column>public void set${column.columnName2?cap_first}(${column.columnType} value) {this.${column.columnName2} = value;}public ${column.columnType} get${column.columnName2?cap_first}() {return this.${column.columnName2};}</#list>
}

骚戴理解:这段代码使用FreeMarker模板语言生成Java类的属性和getter/setter方法。

具体来说,这段代码包含了以下两个部分:

1. 属性生成部分

-<#list table.columns as column>:这行代码使用FreeMarker的list指令,遍历数据模型中的table.columns字段,将每个元素赋值给变量column。

-<#if column.columnKey??>:这行代码使用FreeMarker的if指令,判断当前列是否为主键列。

-@Id:如果当前列是主键列,则生成@Id注解。

-<#if>:if指令的结束标签。

-private ${column.columnType} ${column.columnName2};:生成Java类的私有属性,属性名和属性类型分别对应数据模型中的column.columnName2和column.columnType。

-</#list>:list指令的结束标签。

2. getter/setter方法生成部分

-<#list table.columns as column>:这行代码使用FreeMarker的list指令,遍历数据模型中的table.columns字段,将每个元素赋值给变量column。

-public void set${column.columnName2?cap_first}(${column.columnType} value) {:生成Java类的setter方法,方法名为set+属性名(首字母大写),方法参数类型和属性类型相同,方法体为将参数值赋值给属性,其中?cap_first是使用内置函数,可以把column.columnName2变成首字母大写。

-public ${column.columnType} get${column.columnName2?cap_first}() {:生成Java类的getter方法,方法名为get+属性名(首字母大写),方法返回值类型为属性类型,方法体为返回属性值。

-</#list>:list指令的结束标签。

总之,这段代码的作用是根据数据模型中的表结构信息,生成Java类的属性和getter/setter方法。

Service层

<#assign classNameLower = ClassName ? uncap_first>
package ${pPackage}.service;import com.ihrm.common.utils.IdWorker;
import ${pPackage}.dao.${ClassName}Dao;
import ${pPackage}.pojo.${ClassName};
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;@Service
public class ${ClassName}Service {@Autowiredprivate ${ClassName}Dao ${classNameLower}Dao;@Autowiredprivate IdWorker idWorker;/*** 保存*/public void add(${ClassName} ${classNameLower}) {//基本属性的设置String id = idWorker.nextId()+"";${classNameLower}.setId(id);${classNameLower}Dao.save(${classNameLower});}/*** 更新*/public void update(${ClassName} ${classNameLower}) {${classNameLower}Dao.save(${classNameLower});}/*** 删除*/public void deleteById(String id) {${classNameLower}Dao.deleteById(id);}/*** 根据id查询*/public ${ClassName} findById(String id) {return ${classNameLower}Dao.findById(id).get();}/*** 查询列表*/public List<${ClassName}> findAll() {return ${classNameLower}Dao.findAll();}
}

Controller层

<#assign classNameLower = ClassName ? uncap_first>
package ${pPackage}.controller;import com.ihrm.common.entity.Result;
import com.ihrm.common.entity.ResultCode;
import com.ihrm.common.exception.CommonException;import ${pPackage}.service.${ClassName}Service;
import ${pPackage}.pojo.${ClassName};
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.List;//解决跨域问题
@CrossOrigin
@RestController
@RequestMapping(value="/${classNameLower}")
public class ${ClassName}Controller {@Autowiredprivate ${ClassName}Service ${classNameLower}Service;//保存@RequestMapping(value="",method = RequestMethod.POST)public Result save(@RequestBody ${ClassName} ${classNameLower})  {//业务操作${classNameLower}Service.add(${classNameLower});return new Result(ResultCode.SUCCESS);}//根据id更新@RequestMapping(value = "/{id}",method = RequestMethod.PUT)public Result update(@PathVariable(value="id") String id, @RequestBody ${ClassName} ${classNameLower} ) {//业务操作${classNameLower}.setId(id);${classNameLower}Service.update(${classNameLower});return new Result(ResultCode.SUCCESS);}//根据id删除@RequestMapping(value="/{id}",method = RequestMethod.DELETE)public Result delete(@PathVariable(value="id") String id) {${classNameLower}Service.deleteById(id);return new Result(ResultCode.SUCCESS);}//根据id查询@RequestMapping(value="/{id}",method = RequestMethod.GET)public Result findById(@PathVariable(value="id") String id) throws CommonException {${ClassName} ${classNameLower} = ${classNameLower}Service.findById(id);return new Result(ResultCode.SUCCESS,${classNameLower});}//查询全部@RequestMapping(value="",method = RequestMethod.GET)public Result findAll() {List<${ClassName}> list = ${classNameLower}Service.findAll();Result result = new Result(ResultCode.SUCCESS);result.setData(list);return result;}
}

Dao层

package ${pPackage}.dao;import ${pPackage}.pojo.${ClassName};
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;public interface ${ClassName}Dao extends JpaRepository<${ClassName},String> ,JpaSpecificationExecutor<${ClassName}> {
}

配置文件

  • application.yml
server: port: 9001
spring: application:  name: ${project}-${path3} #指定服务名datasource:  driverClassName: ${driverName}url: ${url}username: ${dbuser}password: ${dbpassword}jpa: database: MySQLshow-sql: true
  • 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"><parent><artifactId>${path2}_parent</artifactId><groupId>${path1}.${path2}</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>${path2}_${project}</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>${path1}.${path2}</groupId><artifactId>${path2}_common</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency></dependencies>
</project>

相关文章:

代码生成器实现

代码生成器实现 实现封装元数据的工具类实现代码生成器的代码编写掌握模板创建的 构造数据模型 需求分析 借助Freemarker机制可以方便的根据模板生成文件&#xff0c;同时也是组成代码生成器的核心部分。对于Freemarker而 言&#xff0c;其强调 数据模型 模板 文件 的思…...

【Python基础】Python函数(基本函数)

文章目录 Python函数1、函数多返回值2、函数多种传参方式(1)位置参数(2)关键字参数(3)缺省参数(4)不定长参数位置传递关键字传递 3、小结 Python函数 1、函数多返回值 Q&#xff1a;如果一个函数要有多个返回值&#xff0c;该如何书写代码&#xff1f; # 使用多个变量&#…...

Vue3 + TS + Vite —— 大屏可视化 项目实战

前期回顾 Vue3 Ts Vite pnpm 项目中集成 —— eslint 、prettier、stylelint、husky、commitizen_彩色之外的博客-CSDN博客搭建VIte Ts Vue3项目并集成eslint 、prettier、stylelint、huskyhttps://blog.csdn.net/m0_57904695/article/details/129950163?spm1001.2014…...

EasyExcel 批量导入并校验数据

文章目录 前言一、pom二、使用步骤1.导入对象2.读入数据并保存 前言 EasyExcel 批量导入并校验数据 一、pom <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.7</version></depend…...

亚马逊、Allegro卖家建立属于自己的测评系统,实现批量优质账号养成

卖家搭建一套完整的测评系统&#xff0c;卖家自己能够养出批量优质账号&#xff0c;并完全掌控真实买家的浏览、加购、下单和评价等风控数据规律。我们的系统能够自主加速推广&#xff0c;防御反击&#xff0c;同时节省运营成本&#xff0c;实现高效的测评运营。 我们的系统支…...

springboot的目录结构作用

springboot单体项目结构大概如下。 代码都在src/main下&#xff0c; java是后端代码 java下最基本的包 dao(mapper) entity(model) service controller 其他的包根据项目需求扩展。 resources下是配置文件。 如果不是前后端分离&#xff0c;resources下放的是静态文件…...

微信小程序基础使用-请求数据并渲染

小程序基本使用-请求数据并渲染 小程序模板语法-数据绑定 在js中定义数据 Page({data: {isOpen: true,message: hello world!} })小程序的data是一个对象&#xff0c;不同于vue的data是一个函数 在模块中获取使用数据 小程序中使用 {{}} 实现数据与模板的绑定 内容绑定&a…...

代码随想录训练营Day55| 392.判断子序列 ;115.不同的子序列

392.判断子序列 class Solution {public boolean isSubsequence(String s, String t) {int m s.length();int n t.length();if(m>n) return false;int[][] dp new int[m1][n1];dp[0][0]0;for(int i1;i<m;i){for(int j1;j<n;j){if(s.charAt(i-1)t.charAt(j-1)){dp[i…...

网络作业9【计算机网络】

网络作业9【计算机网络】 前言推荐网络作业9一. 单选题&#xff08;共12题&#xff0c;36分&#xff09;二. 多选题&#xff08;共1题&#xff0c;3分&#xff09;三. 填空题&#xff08;共2题&#xff0c;10分&#xff09;四. 阅读理解&#xff08;共1题&#xff0c;17分&…...

C++ QT 上传图片至mysql数据库

以下是一个简单的C QT上传图片至MySQL数据库的代码示例&#xff1a; #include <QtSql> #include <QFile> #include <QByteArray> int main() { //连接数据库 QSqlDatabase db QSqlDatabase::addDatabase("QMYSQL"); …...

2023去水印小程序saas系统源码修复独立版v1.0.3+uniapp前端

&#x1f388; 限时活动领体验会员&#xff1a;可下载程序网创项目短视频素材 &#x1f388; &#x1f389; 有需要的朋友记得关赞评&#xff0c;阅读文章底部来交流&#xff01;&#xff01;&#xff01; &#x1f389; ✨ 源码介绍 一个基于uniapp写的小程序&#xff0c;后端…...

【ChatGPT】数据科学 ChatGPT Cheat Sheet 书籍分享(阿里云盘下载)

封皮 以下为书中部分内容的机器翻译 我们的重要提示指南 1. 以 AI 角色的描述开始提示。 例如&#xff0c;“你是{x}”或“我希望你扮演{x}”。如果您不确定&#xff0c;请尝试“你是一个有帮助的助手”。 例如&#xff0c;您是 OpenAI 的数据科学家&#xff0c;您正在研究大型…...

使用 Docker-compose 搭建lnmp

服务编排&#xff1a; 应用编排&#xff1a; 单机环境下&#xff1a;shell/python脚本多机/集群环境下&#xff1a;ansible、saltstack、pubbet docker容器编排&#xff1a; 单机&#xff1a;docker-compose多机/集群&#xff1a;docker swarm&#xff0c;mesos marathon&a…...

chatgpt赋能python:Python中的矩阵合并方法:介绍和使用方法

Python中的矩阵合并方法: 介绍和使用方法 矩阵合并是Python编程中常用的操作之一&#xff0c;特别是针对数据分析、机器学习和深度学习等领域。Python提供了多种方法来合并矩阵&#xff0c;本文将介绍这些方法并分享如何在实际应用中使用它们。 普通矩阵合并 最基础的矩阵合…...

Java动态代理:优化静态代理模式的灵活解决方案

文章目录 代理模式定义具体实现分析优缺点 优化使用动态代理解决优化相关知识动态代理种类场景应用 代理模式 定义 代理模式&#xff0c;为其他对象提供一种代理以控制对这个对象的访问 具体实现 代理模式的具体实现描述可以分为以下几个步骤&#xff1a; 创建抽象对象接…...

四种Bootloader程序安全机制设计

正文 大家周末好&#xff0c;我是bug菌~ 不管是玩单片机还是嵌入式linux&#xff0c;基本上都会接触到bootloader&#xff0c;所以bootloader程序也是一个关键的组件&#xff0c;进行硬件初始化&#xff0c;应用程序的合法性、完成性检测、升级功能等等都与其息息相关。 像一些…...

【DBA 警世录之习惯性命令---读书笔记】

&#x1f448;【上一篇】 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 【下一篇】&#x1f449; &#x1f53b;【&#x1f4a3; 话题引入&#xff1a;既然 DBA 这个职业如此危险&#xff0c;那么哪些习惯是 DBA 必须养成的呢&#x…...

Vue中如何进行状态持久化(LocalStorage、SessionStorage)

Vue中如何进行状态持久化&#xff08;LocalStorage、SessionStorage&#xff09;&#xff1f; 在Vue应用中&#xff0c;通常需要将一些状态进行持久化&#xff0c;以便在用户关闭浏览器或刷新页面后&#xff0c;仍能保留之前的状态。常见的持久化方式包括LocalStorage和Sessio…...

【30天熟悉Go语言】6 Go 复杂数据类型之指针

文章目录 一、前言二、数据类型总览三、指针1、特殊运算符& *2、内存角度来看指针3、使用指针修改数据4、指针使用的注意事项5、对比着看Java的引用类型 三、总结 一、前言 Go系列文章&#xff1a; GO开篇&#xff1a;手握Java走进Golang的世界2 Go开发环境搭建、Hello Wor…...

Linux内核使用红黑树的场景

进程调度队列 (Process Scheduling)&#xff1a;内核需要对进程按照一定的调度策略进行排队&#xff0c;以便更好地利用 CPU 的时间片。因此&#xff0c;内核使用红黑树作为查找和管理进程调度队列的数据结构&#xff0c;以支持快速的查找、插入和删除操作。 文件系统 (File S…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

IP如何挑?2025年海外专线IP如何购买?

你花了时间和预算买了IP&#xff0c;结果IP质量不佳&#xff0c;项目效率低下不说&#xff0c;还可能带来莫名的网络问题&#xff0c;是不是太闹心了&#xff1f;尤其是在面对海外专线IP时&#xff0c;到底怎么才能买到适合自己的呢&#xff1f;所以&#xff0c;挑IP绝对是个技…...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...