自身项目导入luckysheet server

自身项目导入luckysheet server

前言

  • luckysheet serverluckysheet官方的一个后台。用来和luckysheet建立ws连接、保存文档至数据库。
  • 目前最麻烦的是不好导入本身的spring boot项目。直接下载官方代码,也很难直接运行。
  • 如下是我琢磨出来的比较简单的在自身spring boot项目导入luckysheet server的办法。如果有更好的办法,请告知我。。
  • 各位可以试试模块导入方法。。。可能会更加简单。。我原本没想到luckysheet server的文件那么多那么复杂。

1.外部环境配置

  • mysql安装

  • redis安装

  • idea安装以及新建一个spring boot web项目。(如果已经有项目就打开原来的项目)

  • 数据库初始化。注意schema名称要和配置文件的一致。

  • CREATE SCHEMA `collsheet` ;
      
      CREATE TABLE `luckysheet` (
      `id` bigint(30) NOT NULL,
      `block_id` varchar(200) NOT NULL,
      `row_col` varchar(50) DEFAULT NULL,
      `index` varchar(200) NOT NULL,
      `list_id` varchar(200) NOT NULL,
      `status` int(1) DEFAULT NULL,
      `json_data` json DEFAULT NULL,
      `order` int(3) DEFAULT NULL,
      `is_delete` int(1) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `lib` (`list_id`,`index`,`block_id`),
      KEY `order` (`order`),
      KEY `status` (`status`),
      KEY `is_delete` (`is_delete`)
    ) ENGINE=InnoDB DEFAULT CHARSET=armscii8;
    
    INSERT INTO luckysheet VALUES (139400313311449087, 'fblock', '', '1', '1079500#-8803#7c45f52b7d01486d88bc53cb17dcd2c3', 1, '{"row":84,"name":"Sheet1","chart":[],"color":"","index":"1","order":0,"column":60,"config":{},"status":0,"celldata":[],"ch_width":4748,"rowsplit":[],"rh_height":1790,"scrollTop":0,"scrollLeft":0,"visibledatarow":[],"visibledatacolumn":[],"jfgird_select_save":[],"jfgrid_selection_range":{}}', 0, 0);
    INSERT INTO luckysheet VALUES (139400313311449088, 'fblock', '', '2', '1079500#-8803#7c45f52b7d01486d88bc53cb17dcd2c3', 0, '{"row":84,"name":"Sheet2","chart":[],"color":"","index":"2","order":1,"column":60,"config":{},"status":0,"celldata":[],"ch_width":4748,"rowsplit":[],"rh_height":1790,"scrollTop":0,"scrollLeft":0,"visibledatarow":[],"visibledatacolumn":[],"jfgird_select_save":[],"jfgrid_selection_range":{}}', 1, 0);
    INSERT INTO luckysheet VALUES (139400313311449089, 'fblock', '', '3', '1079500#-8803#7c45f52b7d01486d88bc53cb17dcd2c3', 0, '{"row":84,"name":"Sheet3","chart":[],"color":"","index":"3","order":2,"column":60,"config":{},"status":0,"celldata":[],"ch_width":4748,"rowsplit":[],"rh_height":1790,"scrollTop":0,"scrollLeft":0,"visibledatarow":[],"visibledatacolumn":[],"jfgird_select_save":[],"jfgrid_selection_range":{}}', 2, 0);
    
    

2.pom.xml文件修改

  • 目的是引入luckysheet server需要的依赖。

  • 注意版本号。。个别依赖版本不一致会报错。

  • 文件修改号后,需要等待idea下载好所有依赖。

  • <!--导入数据库驱动,不同数据库,驱动器不一样-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    
    <!--引入 mybatis-spring-boot-starter 的依赖-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.0</version>
    </dependency>
    
    <!--引入 hutool-all-->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.7.22</version>
    </dependency>
    
    <!--websokcet的starter-->
        <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    
    <!--JSON解析-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.70</version>
    </dependency>
    
    <!--为类添加getter和setter和构造函数等-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        </dependency>
    <dependency>
    
    <!--google的gson工具-->
    <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.9.0</version>
        <scope>compile</scope>
    </dependency>
    
    <!--阿里的druid数据源-->
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.20</version>
    </dependency>
    
    <!-- redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- 给redis集群用的Lettuce的连接池 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-collections4</artifactId>
        <version>4.3</version>
    </dependency>
    

    3.application.yml文件修改

    • 主要是mysql数据库配置、redis配置、日志配置等

    • 注意数据库配置有重复。。因为官方就是有重复的。。暂时还没有对重复进行去掉。

    • #服务器配置
      server:
        port: 8080
      
      #数据库配置
      spring:
        application:
          name: web
        jackson:
          time-zone: GMT+8
          date-format: yyyy-MM-dd HH:mm:ss
        http:
          encoding:
            charset: UTF-8
            enabled: true
        datasource:
          druid:
            url: jdbc:mysql://127.0.0.1:3306/collsheet
            username: root
            password: 5587699$lgg
            driver-class-name: com.mysql.cj.jdbc.Driver
        redis:
          host: 127.0.0.1
          port: 6379
          password:
          timeout: 10000ms
          lettuce:
            pool:
              max-active: 8
              max-wait: -1ms
              max-idle: 8
              min-idle: 0
          database: 0
      
      
      db:
        mysql:
          druid:
            url: jdbc:mysql://127.0.0.1:3306/collsheet
            username: root
            password: 5587699$lgg
            driver-class-name: com.mysql.cj.jdbc.Driver
        postgre:
          druid:
            url: jdbc:mysql://127.0.0.1:3306/collsheet
            username: root
            password: 5587699$lgg
            driver-class-name: com.mysql.cj.jdbc.Driver
                
      #日记配置
      logging:
        file:
          path: my-log
        pattern:
          console: "%clr(%d{yyyy-MM-dd HH:mm:ss}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(-){faint} %clr([%14.14t]){faint} %clr(%-40.40logger.%13.13M){cyan} %clr(%3.3L) %clr(:){faint} %msg%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"
      
      #redis通道名称
      redis.channel: luckysheet.channel
      #表格中块的大小,新增加时写入每一个sheet的第一个模块中
      row_size: 500
      col_size: 500
      #使用服务器类型
      servertype: tomcat
      
      

      4. 复制class文件

  • 复制如下一大丢文件至你的项目

  • 我全部丢到一个文件夹了。。不太合适。。最好分好类。

  • 复制过来后,idea会有报错。。都是import错误。。。删掉这些报错的import错误。。然后交给idea自动import即可。

  • BaseHandle.java
    BaseModel.java
    ConfigerService.java
    ConfigMergeModel.java
    DataSourceConfig.java
    DisabledTypeEnum.java
    GridFileRedisCacheService.java
    GridRecordDataModel.java
    GzipHandle.java
    IpAndPortUtil.java
    IRecordDataInsertHandle.java
    IRecordDataUpdataHandle.java
    IRecordDelHandle.java
    IRecordSelectHandle.java
    JdbcTempleConfig.java
    JfGridConfigModel.java
    JfGridFileController.java
    JfGridFileGetService.java
    JfGridFileUtil.java
    JfGridUpdateService.java
    JsonUtil.java
    LuckySheetGridModel.java
    MSExcelUtil.java
    MyStringUtil.java
    MyURLUtil.java
    MyWebSocketHandler.java
    MyWebSocketInterceptor.java
    OperationTypeEnum.java
    Pako_GzipUtils.java
    readme.txt
    RecordDataInsertHandle.java
    RecordDataUpdataHandle.java
    RecordDelHandle.java
    RecordSelectHandle.java
    RedisCacheService.java
    RedisConfig.java
    RedisLock.java
    RedisMessageListener.java
    RedisMessageModel.java
    RedisMessagePublish.java
    RedisQueueService.java
    ScheduleService.java
    SheetOperationEnum.java
    SnowFlake.java
    SysConstant.java
    TestUtil.java
    TimeUtil.java
    WebSocketConfig.java
    WSUserModel.java
    

    5.使用

    • 至此,如果原本的项目可以运行,我们导入的luckysheet server项目也可以运行了。什么都不需要做了。
    • spring boot会自动导入各种config类,自动启动ws服务等
    • 但是还需要如下配置才能实现前端数据保存至数据库。

    5.1 使用步骤

    • 后端用uuid工具生成一个uuid。

    • 用该uuid向luckysheet表插入一条空白记录。插入语句参考如下。这个是初始化语句。目的是为了新建一个空白表格。一条记录表示一个空白sheet页面。

    • luckysheet server用分块实现sheet的保存。第一个fblock表示配置块,里面没有数据的。只有颜色等配置。

    • INSERT INTO luckysheet VALUES (139400313311449087, 'fblock', '', '1', '1079500#-8803#7c45f52b7d01486d88bc53cb17dcd2c3', 1, '{"row":84,"name":"Sheet1","chart":[],"color":"","index":"1","order":0,"column":60,"config":{},"status":0,"celldata":[],"ch_width":4748,"rowsplit":[],"rh_height":1790,"scrollTop":0,"scrollLeft":0,"visibledatarow":[],"visibledatacolumn":[],"jfgird_select_save":[],"jfgrid_selection_range":{}}', 0, 0);
      
    • 传递该uuid到前端

    • 前端的luckysheet配置如下

    • socket_url = "ws://" + window.location.host + "/websocket/luckysheet?t=" + user.name;
      luckysheetOptions = {
          container: 'luckysheet', //luckysheet为容器id
          lang: 'zh', // 设定表格语言
          showinfobar: false,
          allowUpdate: true,
          updateUrl: socket_url,
          loadUrl: "/user/load",
          gridKey: uuid,
      }
      
    • 注意loadUrl地址的controller文件。。可以根据需要使用里面的方法。里面有导入全部数据、导入sheet等。我修改为如下:(最末尾那一行)

    •     @PostMapping("/load")
          public String load(HttpServletRequest request, HttpServletResponse response, @RequestParam(defaultValue = "") String gridKey) {
              //告诉浏览器,当前发送的是gzip格式的内容
              response.setHeader("Content-Encoding", "gzip");
              response.setContentType("text/html");
              String resultStr="";
              if(gridKey.trim().length()!=0){
                  try {
                      String _checkStr=check(request,gridKey.toString(),null,OperationTypeEnum.Read);
                      if(_checkStr.length()>0){
                          return null;
                      }
                      List<JSONObject> dbObject=null;
                      //dbObject=jfGridFileGetService.getDefaultByGridKey(gridKey);
                      dbObject=jfGridFileGetService.getAllSheetByGridKey(gridKey);
      
    • 至此一切都可以正常使用了。

    6 bug处理

    • 目前发现这个后台有3个bug。按道理来说不应该。。。也可能是我的导入办法不对。

    6.1 无法删除sheet页

    • 这个是sql语句有错误。定位到RecordDelHandle.java文件

    • 原来的写法

    • /**
          * 删除sheet(非物理删除)
          *
          * @param model
          * @return
      */
          @Override
          public boolean updateDataForReDel(GridRecordDataModel model) {
              try{
                  String sql1="update "+ JfGridConfigModel.TABLENAME+" set is_delete=?  where  list_id=? and index=? ";
                  log.info("updateSql1:"+sql1);
                  luckySheetJdbcTemplate.update(sql1,new Object[]{model.getIs_delete(),model.getList_id(),model.getIndex()});
                  return true;
              }catch (Exception e){
                  log.error(e.getMessage());
                  return false;
              }
          }
      
    • 主要原因是字段名称index在mysql是系统关键词。。直接如此引用必然报错。修改如下(增加表名用t.index代替)

    •     /**
           * 删除sheet(非物理删除)
           *
           * @param model
           * @return
           */
          @Override
          public boolean updateDataForReDel(GridRecordDataModel model) {
              try{
                  String sql1="update "+ JfGridConfigModel.TABLENAME+" t  set is_delete=?  where  list_id=? and t.index=? ";
                  log.info("updateSql1:"+sql1);
                  luckySheetJdbcTemplate.update(sql1,new Object[]{model.getIs_delete(),model.getList_id(),model.getIndex()});
                  return true;
              }catch (Exception e){
                  log.error(e.getMessage());
                  return false;
              }
          }
      

      6.2 无法修改sheet名称

      • 定位RecordDataUpdataHandle.java文件137行

      • if(v instanceof JSON){
        updateSql.append("CAST('"+((JSON) v).toJSONString()+"' as JSON)");
        }else{
            //updateSql.append("\""+v+"\"");
            updateSql.append(v);
        }
        
      • 其中v是前端传递过来的sheet名称。按道理官方这么写没错。。但是不懂为什么v自身带有两个双引号。导致该sql语句是 ""v""。多了2个双引号。。所以数据库提示字符错误。

      6.3无法修改sheet的行或列的宽度和高度。

      • 这个bug事实上有很大的影响。。不仅仅是无法修改行或列。。

      • 这个bug没解决6.4的bug也没办法解决。。事实上影响到了所有表格cg( Operation_cg)操作(操作config)。

      • bug没解决前,提示sql语句存在sync错误。

      • 定位到RecordDataUpdataHandle.java449行。

      • 注释掉的是原本的报错的写法。。更换成如下写法即可。。主要是JSON_CONTAINS_PATH(t.json_data,'one',\"$."+key+"\")=0"和`"update "+JfGridConfigModel.TABLENAME+" t 需要指定一个表的别名以及告诉JSON_CONTAINS_PATH函数一个正确的列。

      • 同理还有这个类的497行。。一样的错误。。

      •             //String createSql="update "+JfGridConfigModel.TABLENAME+" t set t.json_data=json_set(json_data,\"$."+key+"\",CAST('"+newObj.getString(key)+"' AS JSON)) where 1=1 " +condition+
                    //        " and JSON_CONTAINS_PATH(jsontest,'one',\"$."+key+"\")=0";
                      
                    String createSql="update "+JfGridConfigModel.TABLENAME+" t set t.json_data=json_set(json_data,\"$."+key+"\",CAST('"+newObj.getString(key)+"' AS JSON)) where 1=1 " +condition+
                            " and JSON_CONTAINS_PATH(t.json_data,'one',\"$."+key+"\")=0";
        

      6.4 无法添加边框

      • 这个bug事实上影响也很大。。不仅仅是无法修改边框。甚至其他涉及config的都无法保存成功。

      • 解决及发现这个bug花了数天时间。最终才定位问题所在。我感觉好像是我错了。。。但是按照我这个办法修改后,可以添加边框了。

      • 根本原因是因为luckysheet的config类,比如边框、单元格大小等在后台都是用前端发过来的进行替换原本的。比如如下一个正确的config。后面那个json是前端进行更改发送给后端的。

      • 注意看如下的config的属性。有的是对象,有的是数组。。后台用fastJson框架进行处理,需要转换为JSONObject或JSONArray对象。。

      • 但是涉及到config类的保存方法中,把config的属性的类型定死为JSONObject了。。所以就会提示JSONArray无法转换为JSONObject。。。需要进行相应的判断。。

      • 解决办法更加复杂了。。因为涉及到多个方法。。这些方法的参数类型都定死了为JSONObject了。。。。。

      • 所以。。最最根本的原因是前端更改接口了!!!!后端没料到会传入JSONArray。

        "config": {
            "columnlen": {
              "2": 260,
              "3": 260
            },
            "borderInfo": [
              {
                "color": "#000",
                "range": [
                  {
                    "row": [
                      0,
                      9
                    ],
                    "top": 0,
                    "left": 0,
                    "width": 73,
                    "column": [
                      0,
                      4
                    ],
                    "height": 19,
                    "top_move": 0,
                    "left_move": 0,
                    "row_focus": 0,
                    "width_move": 369,
                    "height_move": 199,
                    "column_focus": 0
                  }
                ],
                "style": "1",
                "rangeType": "range",
                "borderType": "border-all"
              }
            ]
          },
        
        
         {"t":"cg","v":{"2":251,"3":203},"i":"1","k":"columnlen"}
        
      • 解决步骤:

      • JfGridUpdateService.java文件中:1168行
        //JSONObject _v = null
        Object _v = null;//需要替换的值
        
      • JfGridUpdateService.java文件中:1220行
        //JSONObject _k = JfGridFileUtil.getObjectByObject(_config, k);
        Object _k = _config.get(k);
        
      • IRecordDataUpdataUhanle.java文件中利用多态增加如下一个接口:
        boolean updateJsonbForSetRootNull(JSONObject query, String word, JSONObject db, Integer position, String words);
        
        boolean updateJsonbForSetRootNull(JSONObject query, String word, Object db, Integer position, String words);
        
      • RecordDataUpdataUhanle.java文件中利用多态实现接口:
        public boolean updateJsonbForSetRootNull(JSONObject query, String word, Object db, Integer position, String words) {
                return updateJsonbForInsertNull(query, word, db, position, words);
        }
        
      • RecordDataUpdataUhanle.java文件中利用多态增加如下方法:
        注意在515行处增加如下判断语句:
        public boolean updateJsonbForInsertNull(JSONObject query, String word, Object db, Integer position, String words) {
            if (db instanceof JSONObject){
                updateSql.append("CAST('"+((JSONObject)db).toString(SerializerFeature.WriteMapNullValue)+"' as JSON)");
            }
        
            if (db instanceof JSONArray){
                updateSql.append("CAST('"+((JSONArray)db).toString(SerializerFeature.WriteMapNullValue)+"' as JSON)");
            }
        
        
            updateSql.append(") where 1=1 "+condition);
        
            log.info("updateSql:{}",updateSql.toString());
            luckySheetJdbcTemplate.update(updateSql.toString(),arr.toArray());
        }
        
### 6.5前端手动输入单一的单元格数值。会出现某个单元格数值不见了。

- 前端手动输入单一的单元格数值。会出现某个单元格数值不见了。这个单元格可能是之前手动输入的某个数值,也可能是之前用拖动生成的数值。

- 解决方法如下:

- ```java
  定位JfGridUpdateService.java 1554行
  //_result = recordDataUpdataHandle.updateJsonbForElementInsert(query, "celldata", _db, 0);
  _result = recordDataUpdataHandle.updateJsonbForElementInsert(query, "celldata", _db, 999999999);
  ```

- 原因:这个方法访问如下方法:

- ```java
  RecordDataUpdataHandle.updateJsonbForElementInsert(JSONObject query, String word, JSONObject db, Integer position)
  ```

- 最终都会执行这个sql语句:

- ```sql
  update luckysheet t set t.json_data=json_set(t.json_data,"$.celldata[position]",CAST('{"r":0,"c":0,"v":{"ct":{"t":"n","fa":"General"},"v":1346,"m":"1346"}}' as JSON)) where 1=1  and t.list_id="78290eb1-c879-4ae3-9eb3-e11dab05da93"  and t.index="1"  and t.block_id="0_0" 
  ```

- 如果传入的position为0。就会把celldata[0]的json给替换掉。如果position为999999999。就会插入新数据。

### 6.6 重复登录bug

- 这个不能说是bug。。只能说是考虑问题。。

- 2022年5月23日。。测试的时候发现。。如果一个用户可以在多个网页打开同一个项目。。经常会把该项目搞崩溃。。导致该项目无法访问。所以目前暂时定死了。。一个用户同时只能登录一个luckysheet。。

- ```java
  MyWebSocketHandler.java 第52行。
  修改该变量为public。
  //private static final Hashtable<String, Hashtable<String, WSUserModel>> USER_SOCKET_SESSION_MAP;
  public static final Hashtable<String, Hashtable<String, WSUserModel>> USER_SOCKET_SESSION_MAP;
  ```

- ```java
  MyWebSocketInterceptor.java 第46行增加如下4行。只要该方法返回false,就不能连接ws。
  如下通过判断是否有token登录。如果有token登录就拒绝登录。
  String str = MyWebSocketHandler.USER_SOCKET_SESSION_MAP.toString();
  if (str.indexOf(token) > -1){
      return false;
  }
  ```


### 6.7 双击表格的某个单元格,进入单元格编辑界面,然后马上退出编辑网页。。然后就不能再次进入这个项目了

- 原因:双击某个单元格的时候,前端向后端发送如下命令

```json
{
  "t": "mv",
  "i": "1",
  "v": {
    "op": "enterEdit",
    "range": [
      {
        "left": 148,
        "width": 73,
        "top": 60,
        "height": 19,
        "left_move": 148,
        "width_move": 73,
        "top_move": 60,
        "height_move": 19,
        "row": [
          3,
          3
        ],
        "column": [
          2,
          2
        ],
        "row_focus": 3,
        "column_focus": 2
      }
    ]
  }
}
```

- 注意,前端发送过来的是一个JSONObject对象。后端接收到这个命令后,什么都不做直接写入数据库

```json
updateSql:update luckysheet t set t.json_data=json_set(t.json_data,"$.jfgird_select_save",CAST('{"op":"enterEdit","range":[{"top_move":0,"top":0,"left":148,"column_focus":2,"width":73,"left_move":148,"column":[2,2],"width_move":73,"row":[0,0],"height_move":19,"row_focus":0,"height":19}]}' as JSON)) where 1=1  and t.list_id='e4777a8b-7d29-43d0-95d6-6b17863e3bdb' and t.index='1' and t.block_id='fblock'
```

- 注意如上sql语句。。把jfgird_select_save这个key对应的值写成了一个对象。。但是前端要求这个是一个数组。。所以前端会报错,,然后就不能进入这个文档了。。

- 简单来说就是某句代码向文档写入了脏数据。。导致前端无法识别这个文档。。然后就无法进入了。

- 解决办法:解决办法就是不写入整个收到的命令。而是提取该命令中的所需要的数据写入数据库。。真正需要的是v.range这个数组。

  ```java
  定位JfGridUpdateService.java第1769行,更换为如下写法:
  //Object db = bson.get("v");
  Object db_wrap = bson.get("v");
  if (!(db_wrap instanceof JSONObject)){
    return;
  }
  
  Object db = ((JSONObject) db_wrap).get("range");
  if (db == null){
    return;
  }
  ```

### 6.8 无法保存单元格回车信息

- 如果单元格的内容有回车(excel经常见)的情况下。会出现更新失败。
- 主要原因还是sql语句的符号错误。
- 错误提示如下:

```java
RecordDataUpdataHandle.DataListValue 188 : StatementCallback; SQL [update luckysheet t set t.json_data=json_set(t.json_data,"$.celldata[0].v",CAST('{"ct":{"s":[{"v":"nihao\r\nnihao"}]
MysqlDataTruncation: Data truncation: Invalid JSON text in argument 1 to function cast_as_json: "Invalid encoding in string." at position 23
MyWebSocketHandler.handleMessage 138 : handleUpdate--error:更新失败
```

- 把完整的sql语句复制到mysql中执行。。报的错误是一样的。由此可见,错误的原因是sql语句符号错误。
- 因为仅仅是回车才出现的错误。所以推测是回车的转义问题。
- 注意到`('{"ct":{"s":[{"v":"nihao\r\nnihao"}]`的`\r\n`仅有一个`\`。试着修改为`\\n\\r`。不在提示这个错误。
- 解决办法如下。解决思路就是用字符串替换`\r\n`为`\\r\\n`。注意顺手把"''"也替换为""。否则当单元格有‘的时候一样会报错。

```java
定位RecordDataUpdataHandle.java文件166行。
增加如下几行
//log.info("updateSql:"+updateSql.toString());
//luckySheetJdbcTemplate.update(updateSql.toString());
String v_str = v.toString()
    .replace("\\r","\\\\r")
    .replace("\\n","\\\\n")
    .replace("'","");
v = JSON.parse(v_str);
```

### 6.9 合并单元格无法保存回车信息。

- 这个bug很少出现。

- 我在导入excel文件的时候才发现有这个bug。具体可以看导入excel文件篇。

- 某个单元格无法写入数据,更新失败。原因如上所述。当这个合并单元格有换行的时候。就会出现这个bug。

- 解决方法如下:注释为原本的写法。稍微改进了下。

  ```java
  定位RecordDataUpdataHandle.java文件224行。
              //updateSql.append("CAST('"+db.toString(SerializerFeature.WriteMapNullValue)+"' as JSON)");
  
  String db_str = db.toString(SerializerFeature.WriteMapNullValue)
      .replace("\\r","\\\\r")
      .replace("\\n","\\\\n")
      .replace("'","");
  updateSql.append("CAST('"+db_str+"' as JSON)");
  ```
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容