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

根据mybatis plus注解动态创建sqlite表和表字段

根据mybatis plus注解动态创建sqlite表和表字段

启动时动态创建sqlite数据库,根据mybatis plus注解动态创建表。如果有新增字段,动态创建字段。


文章目录

  • 根据mybatis plus注解动态创建sqlite表和表字段
  • 一、初始化数据库
    • 1.系统启动时初始化数据库
    • 2.初始化sqlite数据库文件
    • 3.根据mybatis plus注解初始化数据库
    • 4.解析mybatis plus注解提取数据库信息
  • 后记


一、初始化数据库

1.系统启动时初始化数据库

通过@PostConstruct注解在项目启动时调用初始化方法

   @PostConstructpublic void init() throws SQLException, IOException {//初始化数据库createDatabase();//初始化数据库表createTables();}

2.初始化sqlite数据库文件

sqlite放在外部目录下,如果放在resource目录下。打成jar后不好管理和读取。

    @ApiModelProperty("数据源地址")@Value("${spring.datasource.url}")private String sqliteDbPath;/*** 初始化数据库*/private void createDatabase() throws IOException {String sqlite = sqliteDbPath.substring("jdbc:sqlite:".length(), sqliteDbPath.indexOf("?"));File audioStationDbFile = new File(sqlite);log.info("sqlite数据库文件地址:" + audioStationDbFile.getAbsolutePath());if (!audioStationDbFile.getParentFile().exists()) {audioStationDbFile.getParentFile().mkdirs();}if (!audioStationDbFile.exists()) {audioStationDbFile.createNewFile();}}

3.根据mybatis plus注解初始化数据库

    /*** 初始化表*/private void createTables() throws SQLException {List<String> tables = jdbcTemplate.queryForList("SELECT name FROM sqlite_master ", String.class);log.info("已存在的数据库:" + tables);List<String> domainTableNames = new ArrayList<String>();Map<String, Class> classMap = new HashMap<String, Class>();//spring工具类,可以获取指定路径下的全部类ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();try {String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +ClassUtils.convertClassNameToResourcePath(DOMAIN_PACKAGE) + "/*.class";Resource[] resources = resourcePatternResolver.getResources(pattern);//MetadataReader 的工厂类MetadataReaderFactory readerfactory = new CachingMetadataReaderFactory(resourcePatternResolver);for (Resource resource : resources) {//用于读取类信息MetadataReader reader = readerfactory.getMetadataReader(resource);//扫描到的classString classname = reader.getClassMetadata().getClassName();Class<?> clazz = Class.forName(classname);//判断是否有指定主解TableName anno = clazz.getAnnotation(TableName.class);if (anno != null) {//将注解中的类型值作为key,对应的类作为 valuedomainTableNames.add(anno.value());classMap.put(anno.value(), clazz);}}} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}
//        log.info("实体类表清单:" + domainTableNames);for (String tableName : domainTableNames) {if (!tables.contains(tableName)) {log.info("数据库[" + tableName + "]不存在,正在创建");String createTableSql = MyBatisPlusSuppotSqliteInit.getInstance().createTable(classMap.get(tableName));jdbcTemplate.update(createTableSql);} else {//存在表,检查字段是否存在List<Map<String, Object>> sqliteTableMapList = jdbcTemplate.queryForList("PRAGMA  table_info(" + tableName + ")");List<SqliteTableStructureDto> sqliteTableStructureDto = new ArrayList<>();for (Map<String, Object> map : sqliteTableMapList) {SqliteTableStructureDto dto = new SqliteTableStructureDto();BeanUtil.copyProperties(map, dto);dto.setDfltValue(null != map.get("dflt_value") ? String.valueOf(map.get("dflt_value")) : null);dto.setNotNull("1".equals(map.get("notnull")) ? true : false);dto.setPk("1".equals(map.get("pk")) ? true : false);sqliteTableStructureDto.add(dto);}sqliteTableMapList = null;List<String> createFieldSqlList = MyBatisPlusSuppotSqliteInit.getInstance().createField(JsMobileUser.class, sqliteTableStructureDto);String createFieldSql = createFieldSqlList.stream().collect(Collectors.joining(";\n"));jdbcTemplate.update(createFieldSql);}}//初始化参数int userCount = jdbcTemplate.queryForObject("SELECT COUNT(1) FROM js_mobile_user", Integer.class);if (userCount <= 0) {jdbcTemplate.update("INSERT INTO `xxx`(`xxx`) " +" VALUES ('xxx'");}}
@Data
@NoArgsConstructor
@ApiModel("sqlite表结构字段")
public class SqliteTableStructureDto {@ApiModelProperty("主键id")private String cid;@ApiModelProperty("字段名称")private String name;@ApiModelProperty("字段映射")private String type;@ApiModelProperty("是否允许为空")private boolean notNull;@ApiModelProperty("默认值")private String dfltValue;@ApiModelProperty("是否为主键")private boolean pk;}

4.解析mybatis plus注解提取数据库信息

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.faker.audioStation.model.dto.ModelField;
import com.faker.audioStation.model.dto.SqliteTableStructureDto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;/*** <p>MyBatisPlus支持sqlite初始化建表</p>*/
@Slf4j
public class MyBatisPlusSuppotSqliteInit {/*** 单里模式*/private static MyBatisPlusSuppotSqliteInit myBatisPlusSuppotSqliteInit = null;/*** 私有化构造函数,使用getInstance()去获取实例*/private MyBatisPlusSuppotSqliteInit() {}/*** 获取实例** @return*/public static MyBatisPlusSuppotSqliteInit getInstance() {if (null == myBatisPlusSuppotSqliteInit) {myBatisPlusSuppotSqliteInit = new MyBatisPlusSuppotSqliteInit();}return myBatisPlusSuppotSqliteInit;}@Data@ApiModel("mybatisPlus对象")public class MybatisPlusDto {//表名String tableName = null;//主键对应的字段名String tableId = null;//表字段结构List<ModelField> modelFieldList = new ArrayList();//表字段名称列表List<String> columnList = new ArrayList<String>();}/*** 获取实例** @param clazz* @return*/public MybatisPlusDto getMybatisPlusDto(Class clazz) {MybatisPlusDto mybatisPlusDto = new MybatisPlusDto();//检查实体类是否缺少注解boolean isTableName = clazz.isAnnotationPresent(TableName.class);if (isTableName) {TableName tableNameIn = (TableName) clazz.getAnnotation(TableName.class);if (null == tableNameIn.value() || "".equals(tableNameIn.value())) {throw new RuntimeException("实体类无TableName注解!");}mybatisPlusDto.tableName = tableNameIn.value();}//是否包含注解boolean isTableField = false;boolean isTableId = false;Field[] fields = clazz.getDeclaredFields();for (int i = 0; i < fields.length; i++) {if (isTableField == false) {isTableField = fields[i].isAnnotationPresent(TableField.class);}if (isTableId == false) {isTableId = fields[i].isAnnotationPresent(TableId.class);}if (isTableField && isTableId) {//都找到注解了 就终止break;}}if (!isTableField) {throw new RuntimeException("实体类无TableField注解!");}if (!isTableId) {log.warn("实体类无isTableId注解!");}//获取表结构for (int i = 0; i < fields.length; i++) {ModelField modelField = new ModelField();modelField.setModelName(fields[i].getName());modelField.setModelType(fields[i].getType());boolean annotationPresent = fields[i].isAnnotationPresent(TableField.class);if (annotationPresent) {// 获取注解值String tableField = fields[i].getAnnotation(TableField.class).value();if (null != tableField && !"".equals(tableField)) {modelField.setTableField(tableField.toUpperCase());mybatisPlusDto.columnList.add(tableField.toUpperCase());boolean apiMp = fields[i].isAnnotationPresent(ApiModelProperty.class);if (apiMp) {modelField.setApiModelProperty(fields[i].getAnnotation(ApiModelProperty.class).value());} else {log.debug("类" + clazz.getName() + "的字段" + fields[i].getName() + "没有注解ApiModelProperty");}mybatisPlusDto.modelFieldList.add(modelField);} else {log.warn("属性[" + modelField.getModelName() + "]对应表字段为空!");}} else if (fields[i].isAnnotationPresent(TableId.class)) {// 获取注解值mybatisPlusDto.tableId = fields[i].getAnnotation(TableId.class).value().toUpperCase();if (null != mybatisPlusDto.tableId && !"".equals(mybatisPlusDto.tableId)) {modelField.setTableField(mybatisPlusDto.tableId.toUpperCase());mybatisPlusDto.columnList.add(mybatisPlusDto.tableId.toUpperCase());boolean apiMp = fields[i].isAnnotationPresent(ApiModelProperty.class);if (apiMp) {modelField.setApiModelProperty(fields[i].getAnnotation(ApiModelProperty.class).value());} else {log.debug("类" + clazz.getName() + "的字段" + fields[i].getName() + "没有注解ApiModelProperty");}mybatisPlusDto.modelFieldList.add(modelField);} else {log.warn("属性[" + modelField.getModelName() + "]对应表字段为空!");}}}
//        log.debug("类[" + clazz.getName() + "]的表名为[" + mybatisPlusDto.tableName + "];字段信息为:" + mybatisPlusDto.modelFieldList);return mybatisPlusDto;}/*** 建表** @param clazz*/public String createTable(Class clazz) {MybatisPlusDto dto = this.getMybatisPlusDto(clazz);StringBuffer sql = new StringBuffer();sql.append("CREATE TABLE ").append("\"").append(dto.getTableName()).append("\" (\n");//表字段结构List<ModelField> modelFieldList = dto.getModelFieldList();for (ModelField modelField : modelFieldList) {if (modelField.getTableField().equals(dto.getTableId())) {if (modelField.getModelType().equals(Integer.class)|| modelField.getModelType().equals(int.class)|| modelField.getModelType().equals(Long.class)|| modelField.getModelType().equals(long.class)) {sql.append("\"" + modelField.getTableField() + "\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n");} else {sql.append("\"" + modelField.getTableField() + "\" TEXT NOT NULL PRIMARY KEY,\n");}} else {sql.append("\"" + modelField.getTableField() + "\" ").append(this.getSqliteType(modelField.getModelType())).append(",\n");}}sql.setLength(sql.length() - 2);sql.append(");\n");
//        log.info(sql.toString());return sql.toString();}/*** 转换数据类型为sqlite类型** @param aClass* @return*/private String getSqliteType(Class aClass) {if (aClass.equals(Integer.class)|| aClass.equals(int.class)|| aClass.equals(Long.class)|| aClass.equals(long.class)) {return "INTEGER";}if (aClass.equals(Float.class)|| aClass.equals(float.class)|| aClass.equals(Double.class)|| aClass.equals(double.class)) {return "REAL";}if (aClass.equals(Date.class)) {return "NUMERIC";}if (aClass.equals(String.class)) {return "TEXT";}if (aClass.equals(Boolean.class)|| aClass.equals(boolean.class)) {return "INTEGER";}return "NUMERIC";}/*** 创建表字段** @param clazz* @param sqliteList* @return*/public List<String> createField(Class clazz, List<SqliteTableStructureDto> sqliteList) {List<String> sqlList = new ArrayList<String>();List<String> fields = sqliteList.stream().map(item -> item.getName().toUpperCase()).collect(Collectors.toList());MybatisPlusDto dto = this.getMybatisPlusDto(clazz);StringBuffer sql = new StringBuffer();//表字段结构List<ModelField> modelFieldList = dto.getModelFieldList();for (ModelField modelField : modelFieldList) {if (!fields.contains(modelField.getTableField().toUpperCase())) {log.warn("表[" + dto.getTableName() + "]字段[" + modelField.getTableField().toUpperCase() + "]缺失,正在生成重建sql");sqlList.add("alter table \"" + dto.getTableName() + "\" add \"" + modelField.getTableField().toUpperCase() + "\""+ this.getSqliteType(modelField.getModelType()) + "");}}return sqlList;}
}

后记

一个简单的根据注解的转换,也可以替换成mysql或oracle的写法,不过mysql已经有了mybatis-enhance-actable了,就不要重复造轮子了。


相关文章:

根据mybatis plus注解动态创建sqlite表和表字段

根据mybatis plus注解动态创建sqlite表和表字段 启动时动态创建sqlite数据库&#xff0c;根据mybatis plus注解动态创建表。如果有新增字段&#xff0c;动态创建字段。 文章目录根据mybatis plus注解动态创建sqlite表和表字段一、初始化数据库1.系统启动时初始化数据库2.初始化…...

同步、异步ETL架构的比较

背景介绍&#xff1a; 数据的抽取&#xff0c;转换和加载 (ETL, Extract, Transform, Load) 是构建数据仓库过程中最复杂也是至 关重要的一个步骤&#xff0c;我们通常用两种办法来处理 ETL 流程: 一种是异步(Asynchronous) ETL 方式, 也称为文本文件(Flat file)方式。 另外…...

【机会约束、鲁棒优化】具有排放感知型经济调度中机会约束和鲁棒优化研究【IEEE6节点、IEEE118节点算例】(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

用Python帮老叔选出好基金,大赚一笔,老叔专门提着茅台登门道谢

我有个老叔很喜欢买基金&#xff0c;因为不想被割韭菜&#xff0c;所以啥群都没进&#xff0c;全部自己精挑细选。 看着他的一个本子密密麻麻地写了一大堆东西&#xff0c;全是基金的数据分析&#xff0c;一大把年纪了挺不容易的&#xff0c;于是就决定帮他一把。 在跟他详谈…...

ZeroTier实现内网穿透详细教程,无需公网IP,实现异地组网

ZeroTier实现内网穿透详细教程&#xff0c;无需公网IP&#xff0c;实现异地组网ZeroTier1.官网注册账号&#xff0c;创建自己的局域网段2.点击创建好的网络&#xff0c;进入设置界面进行设置3.下载客户端&#xff0c;安装客户端&#xff0c;然后连接到网络中4.加入网络成功后&a…...

电商 SaaS 全渠道实时数据中台最佳实践

摘要&#xff1a;本文整理自聚水潭数据专家张成玉&#xff0c;聚水潭高级数据工程师应圣楚&#xff0c;在 FFA 2022 行业案例专场的分享。本篇内容主要分为四个部分&#xff1a;实时数仓的建设和发展数据中台的产品体系及架构实时计算的实践和优化对实时计算的未来展望Tips&…...

macos ncnn 安装踩坑记录···

安装真麻烦踩了无数坑&#xff0c;官方给的安装教程&#xff1a;macos安装ncnn, 安装过程老是报错&#xff0c;记录一下卡的比较久的&#xff0c;网上也不好找资料的错. 我的电脑&#xff1a; 1. 使用homebrew 的时候失败fatal: not in a git directory Error: Command failed…...

ESP32设备驱动-AM2301(DHT21)温度湿度传感器驱动

AM2301(DHT21)温度湿度传感器驱动 文章目录 AM2301(DHT21)温度湿度传感器驱动1、AM2301(DHT21)介绍2、硬件准备3、软件准备4、驱动实现1、AM2301(DHT21)介绍 AM2301 湿敏电容数字温湿度模块是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温…...

[数据结构]:16-归并排序(顺序表指针实现形式)(C语言实现)

目录 前言 已完成内容 归并排序实现 01-开发环境 02-文件布局 03-代码 01-主函数 02-头文件 03-PSeqListFunction.cpp 04-SortFunction.cpp 结语 前言 此专栏包含408考研数据结构全部内容&#xff0c;除其中使用到C引用外&#xff0c;全为C语言代码。使用C引用主要是…...

React(七):Router基本使用、嵌套路由、编程式导航、路由传参、懒加载

React&#xff08;七&#xff09;一、React-Router的基本使用1.安装和介绍2.路由的配置和跳转3.Navigate的使用4.如果找不到对应的路由路径&#xff1f;二、嵌套路由的用法三、编程式路由导航1.类组件中使用useNavigate2.函数式组件中使用useNavigate四、路由跳转传参1.设置好路…...

Java基础面试题(一)

Java基础面试题 一、面向对象和集合专题 1. 面向对象和面向过程的区别 面向过程&#xff1a;是分析解决问题的步骤&#xff0c;然后用函数把这些步骤一步一步地实现&#xff0c;然后在使用的时候一一调用则可。性能较高&#xff0c;所以单片机、嵌入式开发等一般采用面向过程…...

代码命名规范是一种责任也是一种精神(工匠精神)

代码命名规范之美规范概述命名规范管理类命名BootstrapProcessorManagerHolderFactoryProviderRegistrarEngineServiceTask传播类命名ContextPropagator回调类命名Handler &#xff0c;Callback&#xff0c;Trigger&#xff0c;ListenerAware监控类命名MetricsEstimatorAccumul…...

奇淫技巧:阅读源码时基于一组快捷键,让我们知道身在何方!

一个十分蛋疼的问题 在我们阅读框架底层源码的时候&#xff0c;我们往往会一个方法一个方法的往下翻&#xff0c;翻了很久很快就会有这样的灵魂拷问&#xff1a;我从那个类&#xff08;方法&#xff09;来&#xff0c;我要到哪个&#xff08;类&#xff09;方法中去。这个时候…...

你真的弄懂this指向了吗

前言 在说 this 指向之前&#xff0c;请观察以下代码&#xff0c;并说出它们的输出结果&#xff1a; 第 1 组&#xff1a;标准函数 window.color "red"; let o {color: "blue", }; function sayColor() {console.log(this.color); }sayColor(); // 输…...

阿里云服务器使用教程:使用xshell、xFtp工具连接阿里云服务器(Centos7)并修改Centos7的yum源为阿里镜像源

目录 1、下载并安装xshell、xFtp 2、远程连接阿里云服务器 3、 修改Centos7的yum源为阿里镜像源 1、下载并安装xshell、xFtp XShell可以在Windows界面下来访问远端不同系统下的服务器&#xff0c;从而比较好的达到远程控制终端的目的。它支持 RLOGIN、SFTP、SERIAL、TELNET、…...

一文快速入门 HTML 网页基础

专栏简介: 前端从入门到进阶 题目来源: leetcode,牛客,剑指offer. 创作目标: 记录学习JavaEE学习历程 希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长. 学历代表过去,能力代表现在,学习能力代表未来! 目录 1.HTML 结构 1.1. 认识 HTML 标签 1.2 HTML 文件结构…...

DEJA_VU3D - Cesium功能集 之 100-任意多边形(标绘)

前言 编写这个专栏主要目的是对工作之中基于Cesium实现过的功能进行整合,有自己琢磨实现的,也有参考其他大神后整理实现的,初步算了算现在有差不多实现小140个左右的功能,后续也会不断的追加,所以暂时打算一周2-3更的样子来更新本专栏(每篇博文都会奉上完整demo的源代码,…...

Cadence OrCAD Capture全局修改原理图的非本地库符号的方法图文教程Repalce Catch功能

⏪《上一篇》   🏡《总目录》   ⏩《下一篇》 目录 1,概述2,修改方法2.1,新建本地库2.2,待修改搬入本地库2.3,修改原理图符号2.4,全局更新原理图符号3,总结B站关注“硬小二”浏览更多演示视频 1,概述 在完成原理图设计...

npm包版本号详解

npm包在发布时&#xff0c;需要按照包版本语义化中的约定去更新设置&#xff0c;例如我们常见的1.0.0&#xff0c;1.0.1&#xff0c;0.0.1等这样的版本号&#xff0c;那么这些数字分别代表什么意思呢&#xff1f;下面我们将详细介绍。 npm版本号的组成 一个完整的版本号&…...

ubuntu 系统安装docker——使用docker打包python项目,整个流程介绍

目录 1 安装docker和配置镜像源 2 下载基础镜像 3 通过镜像创建容器 4 制作项目所需的容器 5 容器制作好后打包为镜像 6 镜像备份为.tar文件 7 从其他服务器上恢复镜像 8 docker的其他常用指令 首先科普一下镜像、容器和实例&#xff1b; 镜像&#xff1a;相当于安装包&…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南

1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发&#xff0c;使用DevEco Studio作为开发工具&#xff0c;采用Java语言实现&#xff0c;包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

Fabric V2.5 通用溯源系统——增加图片上传与下载功能

fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...