目录

0x00 适用场景

0x01 问题描述

0x02 字节数组

0x03 Base64编码

0x04 其实没那么麻烦

0x05 回顾

0x00 适用场景

1. 前端: JavaScript
2. 后端: ASP.NET Core C#
3. 数据库: Microsoft SQL Server
4. SQL Server中的timestamp数据类型可以理解为时间戳, 但是这个意思会引起很多误解, 这个误解跟中文翻译无关. Stack Overflow上也有很多人对timestamp这个单词产生误解, 认为可以从这个类型的数据中得到日期时间的信息. 最初, 为了解决数据库记录在update时因并发而产生的问题, 微软在数据库中添加了时间戳这个类型. 但是其实这只是一些字节数组, 并且记录添加或修改的先后会影响这个数组中的数值大小. 其实更为准确地理解这个类型, 应该把它看成是行版本号, 因此后续微软又引入了它的同义词rowversion.

0x01 问题描述

公司的每张表都有RowTS这个字段, 并且数据类型为timestamp. 在服务器端, 使用Dapper来做数据访问. 当insert一条记录时, 由数据库自动生成RowTS值, 当select这条记录时, 在SSMS查询结果中显示的是这种值: 0x00000000001CB574, 于是同事在C#的Model中将该字段类型设置为string. string类型在C#和JavaScript之间通过json字符串传递是没有任何问题的, 但是在update这条记录时, 要把这个值作为where条件时就不行了, 数据库会提示如下错误信息: "Implicit conversion from data type varchar to timestamp is not allowed. Use the CONVERT function to run this query.".
经过同事在SSMS中尝试, 发现不把这个值作为字符串(用单引号括起来), 而是直接作为字面量, 就不会报错. 那么在C#中就是直接拼SQL字符串, 而不是用参数即可. 其实在select查询时, 还需要对值进行转换(转换表达式为CONVERT(NVARCHAR, CAST(Table.RowTS AS BINARY(8)), 1) ), 否则传递到C#中时, Dapper会对它进行类型的自动转换, 转换后的值并不是SSMS中显示的那个样子.
那么, 这个timestamp数据类型到底对应C#的哪个类型? Newtonsoft的JSON序列化器把值转成的字符串为什么跟SSMS中显示的不一样? SSMS显示的那个值是一个什么表现形式? timestamp这种数据类型就真的不能在C#端做参数化查询吗?

0x02 字节数组

让我们顺着线索一点一点来找, 首先要弄明白前面提到的第一个问题 ---- T-SQL的timestamp对应C#的哪个类型? 经过验证, 它不是字符串类型, 当把属性的类型设置为字符串时, 调试时会抛异常. Dapper很明确地指出是RowTS列错误的转换, 并且标识出转换前的类型System.Byte[] - Object. 那么这可以说明timestamp就是字节数组类型, 而在SSMS查询结果中看到的则是某种数据类型的表示形式. 其实用C / C++写底层的同学会对这种类型及表示形式比较熟悉些, 这是字节数组的十六进制表示形式. 首先, 0x起头代表这是十六进制表示法, 同时0x跟数据没关系, 0x后面的才是数据; 其次一个字节需要用2位的十六进制数来表示, 数数后面一共几个2位, 很明显是8个, 那么也就是说这个字节数组的长度为8, 大家也可以看到上面那个T-SQL的转换表达式中BINARY(8)中的8就是这么来的; 然后0x这种十六进制表示法在C#中可以直接作为字面量, 就跟写二进制数21623是一样的, 而且如果想把一个整数显示成十六进制表示, 在ToString方法中加入一个格式参数X2即可, 比如刚才说的21623, 用十六进制表示就是0x5477(很遗憾没见到字母); 最后前面提的那些问题, 我们现在搞清楚了一半. T-SQL的timestamp对应C#的字节数组类型, C#使用Dapper时实体对应的属性类型必须是字节数组, 而不能是字符串, 在SSMS查询结果中看到的是, 字节数组的每个元素转换为十六进制表示法的字符串, 并拼接而成, 我同事用的转换表达式就是完成SSMS把timestamp数据转换为nvarchar数据的功能, 而nvarchar要用C#的字符串来接收, 这样的话, 参数化查询就不成问题了, 对于timestamp这种字段, 只需要为它的参数传入一个字节数组(注意这个字节数组如果不是从数据库中查出来的, 还要保证是8的长度)即可. 剩下的另一半问题就是为什么Newtonsoft的JSON序列化器转换后的字符串, 也就是我们在前端接收到的值跟十六进制表示法的字符串不一样呢? 接着看下一节.

0x03 Base64编码

前面曾经提到一个十六进制表示法的值0x00000000001CB574, 我们就拿这个值接着举例. 如果用byte数组接收这个值, 然后再进行json序列化, 传递到前端浏览器. 我们会发现浏览器收到的数据是个字符串, 并不是数组(尽管js有数组这个类型), 这个字符串是这样的: AAAAAAActXQ=. 这个例子比较极端, 生成的Base64编码没有数字, 其实是可以有数字和字母的, 然后=是用来补足位的. 我粗略看了一下Base64编码(因为平常工作中并不需要我去实现编码代码, 而是直接调用各种语言提供的库函数即可), 它其实是把计算机底层存储的数据(只有0和1)以位(bit)为基础, 几个几个地编成一组, 进行多次转换后生成的字符串. 由于有时最后一组的数量不够, 不够的就用二进制的0来占位, 最终用=来标识这些补位的0. 它的第一个用途就是简单"加密", 人的肉眼不可能很直接明了地看出Base64编码与编码前的数据的对应关系(但是用程序还是能解码的); 第二个就是缩小二进制数据转为字符串所占用的长度, 二进制的一个字符只能表示2个数, 如果数据很大, 字符串的长度会很长, 但是进行Base64编码后, 得到的字符串就回很短. 正是由于这个特点, Base64编码被用来作为网络传输的编码格式, 最常用的是图片数据. 而这里8个长度的字节数组也被序列化成Base64编码的字符串, 然后传递json字符串给浏览器. 最后经过验证, C#把字节数组的属性, 序列化成Base编码的json字符串, 传递给JavaScript; 而当JavaScript把这个Base64编码字符串原封不动的传回给C#, C#的json字符串饭序列化会自动把它还原为字节数组, 因此C#这边依然可以用那个字节数组类型的属性来接收. 现在C#到前端js的处理也搞清楚了. 下面把两部分结合到一起, 做个总结.

0x04 其实没那么麻烦

我的同事对于timestamp类型的数据处理有些麻烦了. 其实T-SQL那边不用Convert转换表达式, 直接查询字段即可. 而到了C#里, 只需要把对应的属性的数据类型设置为byte[], 即能正确接收, 然后在C#传递json字符串时, 也不用特殊处理, 直接把byte[]交给json序列化器. 到了前端js中, 由于RowTS是作为标识数据的版本, 因此这个数据值一定不会被更改, 也不允许更改. 只需要在向服务器发送请求时, 仍然原样带着这个数据即可, C#那边仍然接收为字节数组类型(json反序列化器把Base64编码的字符串给转换为了字节数组). 最后在执行update语句时, 也不要做任何转换或处理, SQL语句中直接用参数, 在Dapper中直接用字节数组给这个参数赋值, 就圆满完成了.

0x05 回顾

一开始提出的问题: 数据表中有timestamp类型的字段, 在C#端和js端如何处理?
这个问题我就是这么解决的, SQL语句查询时直接查该字段, C#用byte[]来接收(这里用Dapper), 然后C#再把byte[]传给js(这里用Newtonsoft), js端对收到的值不要做任何改动, 再原样传回, C#端直接用byte[]来接收, 最后SQL语句更新记录时, where条件中的参数用byte[]来赋值, 这样就可以了.
另外, 这里涉及几个概念, 我没有展开讲解, 其实我也没了解十分透彻, 因为不大用得到, 而且即使用到, 也可以在网上查资料. 大家有兴趣的可以回去自己研究, 如下:
1. T-SQL的timestamp类型
2. 字节数组的十六进制表示法
3. Base64编码

如果本文中有哪里写的不对, 请您斧正!

T-SQL的timestamp类型实际应用的更多相关文章

  1. java获取获得Timestamp类型的当前系统时间。以及java.util.date 、java.sql.Date之间的转换

    java获取取得Timestamp类型的当前系统时间java获取取得Timestamp类型的当前系统时间 格式:2010-11-04 16:19:42 方法1: Timestamp d = new T ...

  2. TIMESTAMP类型字段在SQL Server和MySQL中的含义和使用

    公众号上转的满天飞的一篇文章,MySQL优化相关的,无意中瞄到一句“尽量使用TIMESTAMP而非DATETIME”,之前对TIMESTAMP也不太熟悉,很少使用,于是查了一下两者的区别. 其实,不管 ...

  3. java 反射: 当Timestamp类型的属性值为null时,设置默认值

    import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Metho ...

  4. java获取获得Timestamp类型的当前系统时间

    java获取取得Timestamp类型的当前系统时间java获取取得Timestamp类型的当前系统时间 格式:2010-11-04 16:19:42 方法1: Timestamp d = new T ...

  5. java获取获得Timestamp类型的当前系统时间。

    java获取获得Timestamp类型的当前系统时间.   java获取取得Timestamp类型的当前系统时间 格式:2010-11-04 16:19:42 方法1: Timestamp d = n ...

  6. PL/SQL 日期时间类型函数及运算

    内部存储格式: 世纪.年.月.日.小时.分钟.秒 默认格式是:DD-MON-RR. SYSDATE 返回当前的系统时间. SELECT SYSDATE FROM DUAL: 对日期的数学运算 SELE ...

  7. MySQL 日期类型及默认设置 (除timestamp类型外,系统不支持其它时间类型字段设置默认值)

    MySQL 日期类型及默认设置 之前在用 MySQL 新建 table,创建日期类型列时遇到了一些问题,现在整理下来以供参考. MySQL 的日期类型如何设置当前时间为其默认值? 答:请使用 time ...

  8. oracle数据库的date和timestamp类型

    1.date类型存储数据的格式为年月日时分秒,可以精确到秒 timestamp类型存储数据的格式为年月日时分秒,可以精确到纳秒(9位) 2.date类型 Date类型的数据可以显示到年月日,也可以显示 ...

  9. Timestamp类型浅析

    Oracle针对不同的数据需求,提供了多种类.多层次的数据类型体系.我们在实际应用中,最好可以依据业务数据的实际形态和前端应用的语言.框架特性来确定字段类型的选择. Date类型是我们经常使用的时间类 ...

随机推荐

  1. C# 多线程六之Task(任务)三之任务工厂

    1.知识回顾,简要概述 前面两篇关于Task的随笔,C# 多线程五之Task(任务)一 和 C# 多线程六之Task(任务)二,介绍了关于Task的一些基本的用法,以及一些使用的要点,如果都看懂了,本 ...

  2. Nginx 配置 Location 规则优先级问题

    nginx 的 location与配置中 location 顺序没有关系,与 location 表达式的类型有关.相同类型的表达式,字符串长的会优先匹配. 以下是按优先级排列说明: 等号类型(=)的优 ...

  3. -webkit-CSS属性拾遗

    -webkit-input-placeholder 这个伪类用于设置输入框placeholder文字的样式,基本可以设置例子如下: input::-webkit-input-placeholder { ...

  4. 如何让你的网站用上免费的HTTPS

    因为之前网站上被注入了广告,再百般尝试之后最后还是使用了HTTPS解决了. 在实现HTTPS的时候最关键的就是证书. 证书的质量觉得了你被多少浏览器所信任. 证书的价格也就蹭蹭蹭往上涨了. 这里推荐一 ...

  5. tensorflow 根据节点获取节点前的整张图

    1.先获取节点 output_layer = self.model.get_pooled_output() logits = self.tf_instance.matmul(output_layer, ...

  6. windows下通过VNC图形化访问Ubuntu桌面环境

    要在windows下图形化访问Ubuntu或其它Linux系统桌面环境有很多方法,我比较喜欢的是使用VNC服务,需要在Ubuntu下安装vncserver和在windows下安装客户端访问工具. 1. ...

  7. CNN初探

    CNN初探 版权声明:本文为博主原创文章,转载请指明转载地址 http://www.cnblogs.com/fydeblog/p/7450413.html 前言 这篇博客主要讲解卷积神经网络(CNN) ...

  8. Java转义emoji等特殊符号

    写在前面 网上找了很多转emoji等方法,大多有两种方法 更改数据库编码格式为utf8mb4 过滤字符串中的emoji 都不是很优雅 更改数据库编码,势必影响其他数据库 过滤emoj效率比较低 处理E ...

  9. iOS开源项目周报0413

    由OpenDigg 出品的iOS开源项目周报第十六期来啦.我们的iOS开源周报集合了OpenDigg一周来新收录的优质的iOS开源项目,方便iOS开发人员便捷的找到自己需要的项目工具等. glidin ...

  10. Java基础知识你知道多少?

    Java虚拟机基础知识你知道多少? Java并发基础知识你知道多少? Java数据结构基础知识你知道多少? java序列化与反序列化 https://github.com/zhantong/inter ...