社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  DATABASE

SpringBoot+Mysql 无法保存emoj表情?

一灰灰 • 6 年前 • 635 次点击  
阅读 243

SpringBoot+Mysql 无法保存emoj表情?

尤记得很久以前,想存 emoj 表情到 mysql 中,需要额外的将 emoj 表情转码之后保存,每次读取时,再解码还原成一下;每次这种 sb 的操作,真心感觉心塞,那么有没有办法直接存呢?

mysql 本身可以通过选择编码集(如 utfbmb4)来支持 emoj 表情,然而今天遇到了一个相当鬼畜的问题,表中可以直接写入 emoj 表情,但是通过 spring boot 代码塞入的 emoj 时,却抛出异常:

Caused by: java.sql.SQLException: Incorrect string value: '\xF0\x9F\x98\x9D\xE6\xB1...' for column 'nick' at row 1
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1084) ~[mysql-connector-java-5.1.30.jar:na]
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4232) ~[mysql-connector-java-5.1.30.jar:na]
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4164) ~[mysql-connector-java-5.1.30.jar:na]
	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2615) ~[mysql-connector-java-5.1.30.jar:na]
	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2776) ~[mysql-connector-java-5.1.30.jar:na]
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2838) ~[mysql-connector-java-5.1.30.jar:na]
	at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2082) ~[mysql-connector-java-5.1.30.jar:na]
复制代码

接下来演示一下正确的使用姿势,以及导致上面问题的错误 case,避免大家重复采坑

I. Emoj 表情支持之旅

接下来我们的目标是可以直接向 mysql 中读取或写入 emoj 表情

1. 表字符集

首先针对 mysql 表,需要指定字符集为utfbmb4

CREATE TABLE `Subscribe` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `email` varchar(140) NOT NULL DEFAULT '',
  `nick` varchar(30) NOT NULL DEFAULT '昵称',
  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0 订阅未激活, 1 订阅已激活 , 2 取消订阅',
  `created` int(13) NOT NULL DEFAULT '0' COMMENT '创建时间',
  `updated` int(13) NOT NULL DEFAULT '0' COMMENT '更新时间'
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
复制代码

上面直接设置表的字符集为utf8mb4,如果某个表已经存在,但是字符集不是 utf8mb4,这种 case 下我们也可以单独的设置某个列的编码如下

ALTER TABLE `Subscribe` CHANGE `nick` `nick` VARCHAR(30)  CHARACTER SET utf8mb4 NOT NULL  DEFAULT '';
复制代码

如上设置之后,我们可以直接在这个表中添加 emoj

2. SpringBoot 支持

接下来进入正题,springboot 项目,如何支持 emoj 的插入;首先看一下项目依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </


    
dependency>
</dependencies>

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
<repositories>
    <repository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>https://repo.spring.io/libs-snapshot-local</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/libs-milestone-local</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>spring-releases</id>
        <name>Spring Releases</name>
        <url>https://repo.spring.io/libs-release-local</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>
复制代码

我们使用的是2.2.1.RELEASE版本,请确保引入了依赖spring-boot-starter-jdbc 与 mysql-connector-java

然后配置 db 相关属性, application.properties

## DataSource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/story?useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=
复制代码

然后就可以愉快的进行测试了

@Slf4j
@SpringBootApplication
public class Application {

    public Application(JdbcTemplate jdbcTemplate) {
        log.warn("application start!!!");

        // 插入emoj 表情
        jdbcTemplate.update("insert into Subscribe (`email`, `nick`) values (?, ?)",
                UUID.randomUUID().toString() + "@t.com", "🐺狼");

        List<Map<String, Object>> r = jdbcTemplate.queryForList("select * from Subscribe order by id desc limit 2");
        log.info("r: {}", r);
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}
复制代码

实测结果如下

这个不已经插入成功了么,那么问题来了,本文开头的那个异常是怎么回事呢

3. 场景复现

出现文章开头的问题,主要是由于mysql-connector-java的版本问题导致的,我们来复现一下,首先将版本指定为5.1.30 (因为我们内部使用的就是这个版本,所以采坑了...)

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.30</version>
</dependency>
复制代码

其次需要在环境配置中,指定一下driver-class-name

spring.datasource.driver-class-name= com.mysql.jdbc.Driver
复制代码

注意

这里需要说明一下,在更高的mysql-connector-java版本中,已经改成com.mysql.cj.jdbc.Driver这个类了;如果依旧配置上面的 Driver,在执行时会有一行提示

Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
复制代码

最后再次执行前面的测试代码,异常就来了

4. 小结

在 mysql 中存入 emoj 表情的场景可以说比较多了,毕竟 21 世纪了,不支持 emoj 的应用是没有前途的;通过前面的 case,即介绍了如何正确的让 springboot 应用支持 emoj 表情,也给出了一个由于版本问题导致的坑

emoj 支持步骤

  • 首先是源头支持,需要修改 mysql 的表字符集;或者修改某些列的字符集,设置为 utf8mb4
  • 注意引入的mysql-connector-java版本,务必选择比较新的版本,
    • 如springboot2.2.1.RELEASE默认提供的版本为8.0.18
    • 而我们演示中的 5.1.30 则不支持 emoj 插入
  • 驱动类,新版中已经使用com.mysql.cj.jdbc.Driver替换之前的com.mysql.jdbc.Driver

II. 其他

0. 项目

1. 一灰灰 Blog

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激

下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

  • 一灰灰 Blog 个人博客 blog.hhui.top
  • 一灰灰 Blog-Spring 专题博客 spring.hhui.top

一灰灰blog

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/51284