SpringBoot+Mysql 沒法保存emoj表情?

尤記得好久之前,想存 emoj 表情到 mysql 中,須要額外的將 emoj 表情轉碼以後保存,每次讀取時,再解碼還原成一下;每次這種 sb 的操做,真心感受心塞,那麼有沒有辦法直接存呢?java

mysql 自己能夠經過選擇編碼集(如 utfbmb4)來支持 emoj 表情,然而今天遇到了一個至關鬼畜的問題,表中能夠直接寫入 emoj 表情,可是經過 spring boot 代碼塞入的 emoj 時,卻拋出異常:mysql

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,避免你們重複採坑git

<!-- more -->github

I. Emoj 表情支持之旅

接下來咱們的目標是能夠直接向 mysql 中讀取或寫入 emoj 表情spring

1. 表字符集

首先針對 mysql 表,須要指定字符集爲utfbmb4sql

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 下咱們也能夠單獨的設置某個列的編碼以下springboot

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

如上設置以後,咱們能夠直接在這個表中添加 emojbash

2. SpringBoot 支持

接下來進入正題,springboot 項目,如何支持 emoj 的插入;首先看一下項目依賴app

<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-javadom

而後配置 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

相關文章
相關標籤/搜索