• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

Spring Boot 高级篇–DB:SpringBoot+Mysql 无法保存emoj表情

MySQL winrains 来源:一灰灰Blog 5个月前 (04-13) 69次浏览

尤记得很久以前,想存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-jdbcmysql-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. 项目

作者:一灰灰Blog

来源:http://spring.hhui.top/spring-blog/2019/12/10/191210-SpringBoot-Mysql-%E6%97%A0%E6%B3%95%E4%BF%9D%E5%AD%98emoj%E8%A1%A8%E6%83%85%EF%BC%9F/


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (2)