https://vladmihalcea.com/sql-server-jdbc-sendstringparametersasunicode/
https://learn.microsoft.com/en-us/sql/connect/jdbc/setting-the-connection-properties?view=sql-server-ver16 If the sendStringParametersAsUnicode property is set to "true", String parameters are sent to the server in Unicode format. If the sendStringParametersAsUnicode property is set to "false", String parameters are sent to the server in non-Unicode format such as ASCII/MBCS instead of Unicode. The default value for the sendStringParametersAsUnicode property is "true". Note: The sendStringParametersAsUnicode property is only checked to send a parameter value with CHAR, VARCHAR, or LONGVARCHAR JDBC types. The new JDBC 4.0 national character methods, such as the setNString, setNCharacterStream, and setNClob methods of SQLServerPreparedStatement and SQLServerCallableStatement classes, always send their parameter values to the server in Unicode whatever the setting of this property. For optimal performance with the CHAR, VARCHAR, and LONGVARCHAR JDBC data types, an application should set the sendStringParametersAsUnicode property to "false" and use the setString, setCharacterStream, and setClob non-national character methods of the SQLServerPreparedStatement and SQLServerCallableStatement classes. When the application sets the sendStringParametersAsUnicode property to "false" and uses a non-national character method to access Unicode data types on the server side (such as nchar, nvarchar and ntext), some data might be lost if the database collation doesn't support the characters in the String parameters passed by the non-national character method. An application should use the setNString, setNCharacterStream, and setNClob national character methods of the SQLServerPreparedStatement and SQLServerCallableStatement classes for the NCHAR, NVARCHAR, and LONGNVARCHAR JDBC data types.

  

Last modified: Apr 17, 2021

Imagine having a tool that can automatically detect JPA and Hibernate performance issues. Wouldn’t that be just awesome?

Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, or Play Framework.

So, enjoy spending your time on the things you love rather than fixing performance issues in your production system on a Saturday night!

Introduction

In this article, I’m going to explain why you should always disable the sendStringParametersAsUnicode default JDBC Driver setting when using SQL Server.

Database table

Let’s assume we have the following database table:

The PostID column is the Primary Key, and the Title column is of the VARCHAR type and has a secondary index as well:

1
CREATE INDEX IDX_Post_Title ON Post (Title)

The Post table contains the following records:

| PostID | Title                                       |
|--------|---------------------------------------------|
| 1      | High-Performance Java Persistence, part 1   |
| 2      | High-Performance Java Persistence, part 2   |
| 3      | High-Performance Java Persistence, part 3   |
| 4      | High-Performance Java Persistence, part 4   |
| ..     | ..                                          |
| 249    | High-Performance Java Persistence, part 249 |
| 250    | High-Performance Java Persistence, part 250 |

As you can see, the Title column is highly selective since every record has a different title value.

Unexpected CONVERT_IMPLICIT and Clustered Index Scan

When finding a Post row by its associated Title column value, we expect an Index Seek operation against the IDX_Post_Title index, but this is not what we get when using the default SQL Server JDBC settings.

For instance, if we enable the runtime query statistics to retrieve the associated execution plan of the SQL query that filters by the Title column:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
executeStatement(entityManager, "SET STATISTICS IO, TIME, PROFILE ON");
 
try (PreparedStatement statement = connection.prepareStatement("""
    SELECT PostId, Title
    FROM Post
    WHERE Title = ?
    """
)) {
 
    statement.setString(1, title);
 
    if (statement.execute() && statement.getMoreResults()) {
        LOGGER.info("Execution plan: {}{}",
            System.lineSeparator(),
            resultSetToString(statement.getResultSet())
        );
    }
}

We get the following SQL execution plan:

1
2
3
4
5
|StmtText                                                                                            |
|----------------------------------------------------------------------------------------------------|
|SELECT PostId, Title FROM Post WHERE Title = @P0                                                    |
|  |--Clustered Index Scan(OBJECT:([high_performance_sql].[dbo].[Post].[PK__Post__AA12603828AEBF55]),|
|     WHERE:(CONVERT_IMPLICIT(nvarchar(255),[high_performance_sql].[dbo].[Post].[Title],0)=[@P0]))   |

The Clustered Index Scan operation tells us that SQL Server has used the PostId Clustered Index to scan the leaf pages in search of the Title value we provided.

The reason why the IDX_Post_Title index was not used is because of the implicit conversion that was done between the provided NVARCHAR value and the VARCHAR value of the Title column.

Even if we provided the Title bind parameter value as a VARCHAR using the setString method:

1
statement.setString(1, title);

The SQL Server JDBC Driver behaved as if we used setNString method instead.

SQL Server JDBC sendStringParametersAsUnicode configuration

By default, SQL Server sends all String parameter values as NVARCHAR since the sendStringParametersAsUnicode configuration is set to true.

So, if we set the sendStringParametersAsUnicode configuration value to false

1
jdbc:sqlserver://localhost;instance=SQLEXPRESS;databaseName=high_performance_sql;sendStringParametersAsUnicode=false;

And, rerun the previous SQL query, we will get the following execution plan:

1
2
3
4
5
|StmtText                                                                        |
|--------------------------------------------------------------------------------|
|SELECT PostId, Title FROM Post WHERE Title = @P0                                |
|  |--Index Seek(OBJECT:([high_performance_sql].[dbo].[Post].[IDX_Post_Title]),  |
|       SEEK:([high_performance_sql].[dbo].[Post].[Title]=[@P0]) ORDERED FORWARD)|

That’s exactly what we were expecting from the start. There’s an Index Seek on the IDX_Post_Title index, and there’s no implicit conversion happening anymore.

Handing Unicode characters

Now, even if you disable the sendStringParametersAsUnicode setting, you can still persist Unicode data in NHARNVARCHAR or NLONGVARCHAR column.

So, if the Title column is of the NVARCHAR type:

1
2
3
4
5
CREATE TABLE Post (
    PostID BIGINT NOT NULL,
    Title NVARCHAR(255),
    PRIMARY KEY (PostID)
)

We can set the Title column using the setNString PreparedStatement method:

1
2
3
4
5
6
7
8
9
10
11
try (PreparedStatement statement = connection.prepareStatement("""
    INSERT INTO Post (Title, PostID)
    VALUES (?, ?)
    """
)) {
 
    statement.setNString(1"România");
    statement.setLong(2, 1L);
 
    assertEquals(1, statement.executeUpdate());
}

And, we can read the Title column using the getNString ResultSet method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try (PreparedStatement statement = connection.prepareStatement("""
    SELECT Title, PostId
    FROM Post
    WHERE Title = ?
    """
)) {
 
    statement.setNString(1"România");
 
    try(ResultSet resultSet = statement.executeQuery()) {
        if (resultSet.next()) {
            assertEquals("România", resultSet.getNString(1));
            assertEquals(1L, resultSet.getLong(2));
        }
    }
}

If you’re using JPA and Hibernate, the NVARCHAR column needs to be annotated with the @Nationalized Hibernate annotation to instruct Hibernate that the underlying String attribute needs to be handled by the StringNVarcharType, as opposed to the default StringType:

1
2
3
4
5
6
7
8
9
10
11
12
@Entity(name = "Post")
public class Post {
 
    @Id
    @Column(name = "PostID")
    private Long id;
 
    @Column(name = "Title")
    @Nationalized
    private String title;
     
}

Awesome, right?

If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.

2 Comments on “SQL Server JDBC – Set sendStringParametersAsUnicode to false”

  1. Pavel Rund
    March 3, 2023

    Thank you for inspiring article, but are you sure about your results? I tried the scenario you described and SQL server have chosen index seek operation even in case of sendStringParametersAsUnicode=true.
    May be it is dependent of SQL server version. I used MSSQL 2017.

    Regards
    Pavel Rund

    • You’re welcome.

      This test provides the proof.

      Here are the results:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      Test with sendStringParametersAsUnicode=true
       
      | Rows | Executes | StmtText                                                                                                                                                                                                                    | StmtId | NodeId | Parent | PhysicalOp           | LogicalOp            | Argument                                                                                                                                                                                         | DefinedValues                                                                                                       | EstimateRows | EstimateIO   | EstimateCPU | AvgRowSize | TotalSubtreeCost | OutputList                                                                                                          | Warnings | Type     | Parallel | EstimateExecutions |
      | ---- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------ | ------ | -------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- | ------------ | ------------ | ----------- | ---------- | ---------------- | ------------------------------------------------------------------------------------------------------------------- | -------- | -------- | -------- | ------------------ |
      | 1    | 1        | SELECT PostId, Title FROM Post WHERE Title = @P0                                                                                                                                                                            | 1      | 1      | 0      |                      |                      |                                                                                                                                                                                                  |                                                                                                                     | 2.0          |              |             |            | 0.0050384817     |                                                                                                                     |          | SELECT   | 0        |                    |
      | 1    | 1        |   |--Clustered Index Scan(OBJECT:([high_performance_java_persistence].[dbo].[Post].[PK__Post__AA12603836E8D7BA]), WHERE:(CONVERT_IMPLICIT(nvarchar(255),[high_performance_java_persistence].[dbo].[Post].[Title],0)=[@P0])) | 1      | 2      | 1      | Clustered Index Scan | Clustered Index Scan | OBJECT:([high_performance_java_persistence].[dbo].[Post].[PK__Post__AA12603836E8D7BA]), WHERE:(CONVERT_IMPLICIT(nvarchar(255),[high_performance_java_persistence].[dbo].[Post].[Title],0)=[@P0]) | [high_performance_java_persistence].[dbo].[Post].[PostID], [high_performance_java_persistence].[dbo].[Post].[Title] | 2.0          | 0.0046064816 | 4.32E-4     | 61         | 0.0050384817     | [high_performance_java_persistence].[dbo].[Post].[PostID], [high_performance_java_persistence].[dbo].[Post].[Title] |          | PLAN_ROW | 0        | 1.0                |
       
      Test with sendStringParametersAsUnicode=false
       
      | Rows | Executes | StmtText                                                                                                                                                                           | StmtId | NodeId | Parent | PhysicalOp | LogicalOp  | Argument                                                                                                                                                          | DefinedValues                                                                                                       | EstimateRows | EstimateIO | EstimateCPU | AvgRowSize | TotalSubtreeCost | OutputList                                                                                                          | Warnings | Type     | Parallel | EstimateExecutions |
      | ---- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------ | ------ | ---------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------ | ---------- | ----------- | ---------- | ---------------- | ------------------------------------------------------------------------------------------------------------------- | -------- | -------- | -------- | ------------------ |
      | 1    | 1        | SELECT PostId, Title FROM Post WHERE Title = @P0                                                                                                                                   | 1      | 1      | 0      |            |            |                                                                                                                                                                   |                                                                                                                     | 1.0          |            |             |            | 0.0032831        |                                                                                                                     |          | SELECT   | 0        |                    |
      | 1    | 1        |   |--Index Seek(OBJECT:([high_performance_java_persistence].[dbo].[Post].[IDX_Post_Title]), SEEK:([high_performance_java_persistence].[dbo].[Post].[Title]=[@P0]) ORDERED FORWARD) | 1      | 2      | 1      | Index Seek | Index Seek | OBJECT:([high_performance_java_persistence].[dbo].[Post].[IDX_Post_Title]), SEEK:([high_performance_java_persistence].[dbo].[Post].[Title]=[@P0]) ORDERED FORWARD | [high_performance_java_persistence].[dbo].[Post].[PostID], [high_performance_java_persistence].[dbo].[Post].[Title] | 1.0          | 0.003125   | 1.581E-4    | 61         | 0.0032831        | [high_performance_java_persistence].[dbo].[Post].[PostID], [high_performance_java_persistence].[dbo].[Post].[Title] |          | PLAN_ROW | 0        | 1.0                |

      So, you can run the test for yourself and see that it works as explained in the article.

[转帖]SQL Server JDBC – Set sendStringParametersAsUnicode to false的更多相关文章

  1. SQL Server JDBC驱动中sqljdbc和sqljdbc4区别

    为了支持向后兼容以及可能的升级方案,JDBC Driver 2.0 在每个安装包中都包括 2 个 JAR 类库:sqljdbc.jar 和 sqljdbc4.jar. qljdbc.jar 类库提供对 ...

  2. 下载 Microsoft SQL Server JDBC 驱动程序

    JDBC 驱动程序中使用 Maven 中心 JDBC 驱动程序可以通过将其添加为依赖项在 POM.xml 文件中使用以下代码添加到 Maven 项目: XML复制 <dependency> ...

  3. Microsoft SQL Server JDBC 驱动程序支持矩阵

    本页包含 Microsoft SQL Server JDBC 驱动程序的支持矩阵和支持生命周期策略. Microsoft JDBC 驱动程序支持生命周期矩阵和策略 Microsoft 支持生命周期 ( ...

  4. [转帖]SQL Server 索引中include的魅力(具有包含性列的索引)

    SQL Server 索引中include的魅力(具有包含性列的索引) http://www.cnblogs.com/gaizai/archive/2010/01/11/1644358.html 上个 ...

  5. [转帖]SQL Server 10分钟理解游标

    SQL Server 10分钟理解游标 https://www.cnblogs.com/VicLiu/p/11671776.html 概述 游标是邪恶的! 在关系数据库中,我们对于查询的思考是面向集合 ...

  6. [转帖]SQL Server DBCC命令大全

    SQL Server DBCC命令大全   原文出处:https://www.cnblogs.com/lyhabc/archive/2013/01/19/2867174.html DBCC DROPC ...

  7. [转帖]SQL Server 2000~2017补丁包

    SQL Server 2000~2017补丁包 https://www.cnblogs.com/VicLiu/p/11510510.html 最新更新 Product Version Latest S ...

  8. [转帖]sql server版本特性简介、版本介绍简介

    sql server版本特性简介.版本介绍简介 https://www.cnblogs.com/gered/p/10986240.html 目录 1.1.sql server的版本信息 1.2.版本重 ...

  9. JDBC连接SQL Server

    下载jdbc驱动包 下载地址,我下载的是exe版本的,其实是格自解压包.下载完毕之后,双击运行,会解压在当前目录下. Microsoft SQL Server JDBC Driver 3.0\sqlj ...

  10. Java使用JDBC连接SQL Server数据库

    Java使用JDBC连接SQL Server数据库 1.下载驱动 1.下载Microsoft SQL Server JDBC 驱动程序 https://docs.microsoft.com/zh-cn ...

随机推荐

  1. 华为云CCE集群健康中心:一个有专家运维经验的云原生可观测平台

    本文分享自华为云社区<新一代云原生可观测平台之华为云CCE集群健康中心>,作者:云容器大未来. "Kubernetes运维确实复杂,这不仅需要深入理解各种概念.原理和最佳实践,还 ...

  2. 云图说|ModelArts开发环境,让AI开发、探索、教学更简单

    摘要:ModelArts开发环境,以云原生的资源使用和开发工具链的集成,目标为不同类型AI开发.探索.教学用户. 本文分享自华为云社区<[云图说]| 第280期 ModelArts开发环境,让A ...

  3. 基于ModelArts进行流感患者密接排查

    摘要:针对疫情期间存在的排查实时性差.排查效率低.无法追踪密接者等问题,可以使用基于YOLOv4的行人检测.行人距离估计.多目标跟踪的方案进行解决. 本文分享自华为云社区<基于ModelArts ...

  4. Python图像处理丨两种实现图像形态学转化运算

    摘要:本篇文章主要讲解Python调用OpenCV实现图像形态学转化,包括图像顶帽运算和图像黑帽运算. 本文分享自华为云社区<[Python图像处理] 十.形态学之图像顶帽运算和黑帽运算> ...

  5. SQL优化老出错,那是你没弄明白MySQL解释计划

    摘要:数据库的解释计划阐明了sql的执行过程,展示了执行的细节,只要根据数据库告诉我们的问题按图索骥的分析就可以. 本文分享自华为云社区<轻松搞懂mysql的执行计划,再也不怕sql优化了> ...

  6. 数字化转型鸿沟如何消除?ROMA Connect融合集成,联接企业应用现在与未来

    摘要:ROMA Connect平台正在以"联接和融合"的方式,重塑传统企业上云的路径--"条条大路"通向云端. 本文分享自华为云社区<[大厂内参]第13期 ...

  7. PPT 商务PPT 如何展示你的产品

    PPT 商务PPT 如何展示你的产品 如何优雅的展示产品 如何展示互联网产品 直接产品截图,比较生硬,简单粗暴 使用场景+样机 放一个电脑或手机的外壳 如何展示产品 如何展示现实中的产品 多角度剪裁 ...

  8. Java 网络编程 —— 异步通道和异步运算结果

    从 JDK7 开始,引入了表示异步通道的 AsynchronousSockerChannel 类和 AsynchronousServerSocketChannel 类,这两个类的作用与 SocketC ...

  9. selenium 访问无等待

    from selenium.webdriver.common.desired_capabilities import DesiredCapabilities desired_capabilities ...

  10. DNS--主从

    操作系统:centos7.8 DNS-master:192.168.198.128 DNS-slave:192.168.198.129 一 主从同步过程 master修改完成重启后 将传送notify ...