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

津做网站嘉兴建设企业网站

津做网站,嘉兴建设企业网站,沈阳网站排名优化,免费做网站公司哪家好mysql8.0.17以下驱动导致blob映射String乱码问题分析与解决 一、问题复现二、问题深究三、解决方法方法1方法2 一、问题复现 1、docker安装mysql8.0,并创建测试数据库及测试数据表 CREATE DATABASE test DEFAULT CHARACTER SET utf8mb4; use test; CREATE TABLE t…

mysql8.0.17以下驱动导致blob映射String乱码问题分析与解决

  • 一、问题复现
  • 二、问题深究
  • 三、解决方法
    • 方法1
    • 方法2

一、问题复现

1、docker安装mysql8.0,并创建测试数据库及测试数据表

CREATE DATABASE `test` DEFAULT CHARACTER SET utf8mb4;
use test;
CREATE TABLE `test_content` (`id` int NOT NULL,`content` varchar(255) DEFAULT NULL,`b_content` blob,`lb_content` longblob,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2、使用mysql8.0.16驱动、mybatis、spring-boot搭建测试项目

2.1 实体类

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class TestContent {private int id;private String content;private String bContent;private String lbContent;
}

2.2 mapper

@Mapper
public interface TestContentMapper {@Select("select * from test_content where id = #{id}")TestContent selectOne(@Param("id") int id);@Select("select * from test_content where id = #{id}")Map<String, Object> selectOneMap(@Param("id") int id);@Update("update test_content set content=#{p.content}, b_content=#{p.bContent}, lb_content=#{p.lbContent} where id=#{p.id}")int update(@Param("p") TestContent p);@Insert("insert into test_content(id, content, b_content, lb_content) values (#{p.id}, #{p.content}, #{p.bContent}, #{p.lbContent})")int insert(@Param("p") TestContent p);}

2.3 配置文件

spring:datasource:type: com.zaxxer.hikari.HikariDataSourceurl: jdbc:mysql:///test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTCusername: rootpassword: YOUR_PASSWORDdriver-class-name: com.mysql.cj.jdbc.Driver
mybatis:configuration:map-underscore-to-camel-case: true

2.4 单元测试类

@SpringBootTest(classes = Application.class)
public class TestBlobConvert {@Resourceprivate TestContentMapper testContentMapper;@Testpublic void testBlobConvert() {int insert = testContentMapper.insert(TestContent.builder().id(1).content("红红火火恍恍惚惚aaa").bContent("红红火火恍恍惚惚aaa").lbContent("红红火火恍恍惚惚aaa").build());System.out.println("insert = " + insert);Map<String, Object> map = testContentMapper.selectOneMap(1);map.forEach((key, value) -> System.out.println("key = " + key+ ", value = " + value));TestContent testContent = testContentMapper.selectOne(1);System.out.println("testContent = " + testContent);testContent.setContent(testContent.getContent() + "q");int update = testContentMapper.update(testContent);System.out.println("update = " + update);TestContent testContent1 = testContentMapper.selectOne(1);System.out.println("testContent1 = " + testContent1);}
}

执行单元测试发现复现成功;控制台打印结果为:

insert = 1
key = b_content, value = [B@b5c6a30
key = lb_content, value = [B@3bfae028
key = id, value = 1
key = content, value = 红红火火恍恍惚惚aaa
testContent = TestContent(id=1, content=红红火火恍恍惚惚aaa, bContent=红红火火恍恍惚惚aaa, lbContent=红红火火恍恍惚惚aaa)
update = 1
testContent1 = TestContent(id=1, content=红红火火恍恍惚惚aaaq, bContent=红红火火恍恍惚惚aaa, lbContent=红红火火恍恍惚惚aaa)

二、问题深究

起初想法是直接自定义typeHandler一把梭,后来决定debug看一看发现,在驱动com.mysql.cj.jdbc.result.ResultSetImpl中public String getString(int columnIndex)时乱码就已经产生;
继续往下com.mysql.cj.protocol.a.result.TextBufferRow#getValue
然后是com.mysql.cj.protocol.result.AbstractResultsetRow#decodeAndCreateReturnValue
再然后com.mysql.cj.protocol.a.MysqlTextValueDecoder#decodeByteArray
最后com.mysql.cj.result.StringValueFactory#createFromBytes

public String createFromBytes(byte[] bytes, int offset, int length, Field f) {return StringUtils.toString(bytes, offset, length, f.getEncoding());
}

一看这个f.getEncoding()是ISO-8859-1
随后看了下8.0.17版本这个位置的代码,做了处理,使用url上的characterEncoding编码进行解码

public String createFromBytes(byte[] bytes, int offset, int length, Field f) {return StringUtils.toString(bytes, offset, length,f.getCollationIndex() == CharsetMapping.MYSQL_COLLATION_INDEX_binary ? this.pset.getStringProperty(PropertyKey.characterEncoding).getValue(): f.getEncoding());}

驱动改成8.0.17后清理数据执行一下看看

insert = 1
key = b_content, value = [B@5042e3d0
key = lb_content, value = [B@1c34365c
key = id, value = 1
key = content, value = 红红火火恍恍惚惚aaa
testContent = TestContent(id=1, content=红红火火恍恍惚惚aaa, bContent=红红火火恍恍惚惚aaa, lbContent=红红火火恍恍惚惚aaa)
update = 1
testContent1 = TestContent(id=1, content=红红火火恍恍惚惚aaaq, bContent=红红火火恍恍惚惚aaa, lbContent=红红火火恍恍惚惚aaa)

好的,这样我们就有了一种不用动代码的解决方法

三、解决方法

方法1

升级mysql驱动至8.0.17或以上版本

方法2

驱动不想或不能升级,那自定义typehandler是一个好的解决方法,在mybatis中debug发现,数据字段为blob、longblob、tinyblob时,JDBCType为JdbcType.LONGVARBINARY
以下是自定义TypeHandler

@MappedJdbcTypes(JdbcType.LONGVARBINARY)
@MappedTypes(String.class)
public class BlobToStringTypeHandler extends BaseTypeHandler<String> {@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {ps.setBytes(i, parameter.getBytes(StandardCharsets.UTF_8));}@Overridepublic String getNullableResult(ResultSet rs, String columnName) throws SQLException {return getStringFromBlob(rs.getBlob(columnName));}@Overridepublic String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return getStringFromBlob(rs.getBlob(columnIndex));}@Overridepublic String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return getStringFromBlob(cs.getBlob(columnIndex));}private String getStringFromBlob(Blob blob) throws SQLException {if (blob == null) return "";byte[] bytes = blob.getBytes(1, (int) blob.length());return new String(bytes, StandardCharsets.UTF_8);}
}

自定义后需要增加配置使其全局生效

mybatis:type-handlers-package: typeHandler所在包名

如果不想改配置文件,想通过代码的方式全局生效可以改一下sqlSessionFactory

@Configuration
public class MybatisConfig implements InitializingBean {@Autowiredprivate SqlSessionFactory sqlSessionFactory;@Overridepublic void afterPropertiesSet() {sqlSessionFactory.getConfiguration().getTypeHandlerRegistry().register("typeHandler所在包名");}
}

当然也可以在某个字段上单独生效,在查询的方法上增加

@Results(@Result(column = "b_content", property = "bContent",jdbcType = JdbcType.LONGVARBINARY, javaType = String.class,typeHandler = BlobToStringTypeHandler.class)
)







来点涩话
花点时间找找是否有更好的方法,结果会让你惊喜

http://www.dtcms.com/a/407298.html

相关文章:

  • 广州做购物网站平面设计培训班要学多久
  • 【复习】计网每日一题--ALOHA
  • 状态机模式:用Python Enum和字典.get()构建健壮的状态管理系统
  • 悬线法,dp 求解 P4147 玉蟾宫
  • 网站建设 北京wordpress关闭站点
  • 云南建投第十建设有限公司网站商城网站开发哪家好
  • 移动固态硬盘插入电脑后提示“需要格式化”或“文件系统损坏”如何修复?
  • ErrorProne 详解
  • 理解 Elasticsearch 中的分块策略
  • 政务服务网站建设整改报告想建个购物网站
  • 网站建设中如何设置外链接网站开发在哪里接活
  • SpringCloud与微服务
  • 织梦网站统计代码app编写软件
  • Python 生成器generator的使用
  • STM32软件I2C读写AT24C64
  • valgrind与coredump调试
  • 建设网站 创建数据库如何评判一个网站建设的怎么样
  • 【ROS2】Intermediate - 单个进程中组合多个节点
  • Common Go Mistakes
  • 网站良精企业网站系统超级商城系统
  • C++ 数组、字符串详解与 C 的差异
  • 目前顶尖AI所用算法,包含的数学内容,详细列举
  • 北京网站制作公司有哪些做网站需要多少钱平邑
  • 网站开发环境vs2015是什么郑州博大泌尿外科医院
  • Android 网络 - NetworkCapabilities(NetworkCapabilities 概述、传输类型、网络能力、实例实操)
  • iOS 26 软件性能测试全流程,启动渲染资源压力对比与优化策略
  • 05-vue3+ts中axios的封装
  • 升阳广州做网站公司手游传奇发布网站999
  • 企业备案网站名称怎么填做网站策划需要什么技能
  • 智能体(Agent)的记忆架构:深入解析短期记忆与长期记忆