[转帖]SQL Server JDBC – Set sendStringParametersAsUnicode to false
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.
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 NHAR
, NVARCHAR
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”
-
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:
12345678910111213Test 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的更多相关文章
- SQL Server JDBC驱动中sqljdbc和sqljdbc4区别
为了支持向后兼容以及可能的升级方案,JDBC Driver 2.0 在每个安装包中都包括 2 个 JAR 类库:sqljdbc.jar 和 sqljdbc4.jar. qljdbc.jar 类库提供对 ...
- 下载 Microsoft SQL Server JDBC 驱动程序
JDBC 驱动程序中使用 Maven 中心 JDBC 驱动程序可以通过将其添加为依赖项在 POM.xml 文件中使用以下代码添加到 Maven 项目: XML复制 <dependency> ...
- Microsoft SQL Server JDBC 驱动程序支持矩阵
本页包含 Microsoft SQL Server JDBC 驱动程序的支持矩阵和支持生命周期策略. Microsoft JDBC 驱动程序支持生命周期矩阵和策略 Microsoft 支持生命周期 ( ...
- [转帖]SQL Server 索引中include的魅力(具有包含性列的索引)
SQL Server 索引中include的魅力(具有包含性列的索引) http://www.cnblogs.com/gaizai/archive/2010/01/11/1644358.html 上个 ...
- [转帖]SQL Server 10分钟理解游标
SQL Server 10分钟理解游标 https://www.cnblogs.com/VicLiu/p/11671776.html 概述 游标是邪恶的! 在关系数据库中,我们对于查询的思考是面向集合 ...
- [转帖]SQL Server DBCC命令大全
SQL Server DBCC命令大全 原文出处:https://www.cnblogs.com/lyhabc/archive/2013/01/19/2867174.html DBCC DROPC ...
- [转帖]SQL Server 2000~2017补丁包
SQL Server 2000~2017补丁包 https://www.cnblogs.com/VicLiu/p/11510510.html 最新更新 Product Version Latest S ...
- [转帖]sql server版本特性简介、版本介绍简介
sql server版本特性简介.版本介绍简介 https://www.cnblogs.com/gered/p/10986240.html 目录 1.1.sql server的版本信息 1.2.版本重 ...
- JDBC连接SQL Server
下载jdbc驱动包 下载地址,我下载的是exe版本的,其实是格自解压包.下载完毕之后,双击运行,会解压在当前目录下. Microsoft SQL Server JDBC Driver 3.0\sqlj ...
- Java使用JDBC连接SQL Server数据库
Java使用JDBC连接SQL Server数据库 1.下载驱动 1.下载Microsoft SQL Server JDBC 驱动程序 https://docs.microsoft.com/zh-cn ...
随机推荐
- CUDA C编程权威指南:1-基于CUDA的异构并行计算
什么是CUDA?CUDA(Compute Unified Device Architecture,统一计算设备架构)是NVIDIA(英伟达)提出的并行计算架构,结合了CPU和GPU的优点,主要用来 ...
- 30秒,2种方法解决SQL Server的内存管理问题
今天和大家聊一聊SQL server的内存管理,说之前我们需要先提出一个问题,SQL Server到底是如何使用内存的?弄清楚如何使用之后,才能谈如何管理. 简单说,SQL Server 数据库的内存 ...
- 视频编码耗时长、编码帧发送失败…DVPP视频编码问题典型案例分析
摘要:本期就分享几个关于DVPP视频编码问题的典型案例,并给出原因分析及解决方法 本文分享自华为云社区<DVPP媒体数据处理视频编码问题案例>,作者:昇腾CANN. DVPP(Digita ...
- 遇到联邦计算数据碰撞难题怎么办?不妨试一试PSI
摘要:随着MPC.隐私计算等概念的流行,诸多政府机构.金融企业开始考虑参与到多方计算的场景中,扩展数据的应用价值. 本文分享自华为云社区<使用PSI解决联邦计算的数据碰撞问题>,作者:br ...
- Go语言逆向技术:恢复函数名称算法
摘要:在对程序做安全审计.漏洞检测时,通常都需要对程序做逆向分析,本文在没有符号表的情况下,提出了一种恢复函数名称的算法,方便对go语言二进制文件进行逆向分析,提升分析效率. 本文分享自华为云社区&l ...
- 一文带你梳理Clang编译步骤及命令
摘要: 本文简单介绍了Clang编译过程中涉及到的步骤和每个步骤的产物,并简单分析了部分影响预处理和编译成功的部分因素. 本文分享自华为云社区<Clang编译步骤及命令梳理>,作者:mai ...
- iOS上架报错:无法添加以供审核
无法提交以供审核 要开始审核流程 必须提供以下项目 您必须为要添加的 app 提供版权信息. 您在提交 app 审核时遇到的问题是因为需要提供版权信息,而您的 app 缺少相关的版权信息.以下是解 ...
- 开心档之MySQL 创建数据表
MySQL 创建数据表 创建MySQL数据表需要以下信息: 表名 表字段名 定义每个表字段 语法 以下为创建MySQL数据表的SQL通用语法: CREATE TABLE table_name (col ...
- .Net Core NLog 配置
using NLog; private static Logger logger = LogManager.GetCurrentClassLogger(); //初始化日志类 NLog.config ...
- Pycharm 2023 年最新激活码、破解教程,亲测有用,永久有效
申明:本教程 Pycharm 破解补丁.激活码均收集于网络,请勿商用,仅供个人学习使用,如有侵权,请联系作者删除.若条件允许,希望大家购买正版 ! PS: 本教程最新更新时间: 2023年2月2日~ ...