我正在使用第三方.NET库(Rhino Security),将其标识符存储为mysql数据库中binary(16)字段中的向导。 一切都可以从该应用程序完美地工作,但是当我尝试通过查询编辑器(对于MySQL为TOAD)手动运行查询时,没有行返回我知道存在的标识符。 例如,如果我运行以下查询,则不会得到任何结果:
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where EntitySecurityKey = '02a36462-49b7-406a-a3b6-d5accd6695e5'
在不使用过滤器的情况下运行同一查询将返回许多结果,其中一个在EntitySecurityKey字段中具有上述GUID。 还有另一种方法可以编写查询以搜索GUID /二进制字段吗?
谢谢!
编辑
我发现TOAD返回一个字符串而不是一个丑陋的Blob很有趣。 使用不同的编辑器返回结果(用于未过滤的查询),我得到了原始二进制数据。 我以为我的查询将使用二进制关键字运行,但以下两项均无效:
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where EntitySecurityKey = BINARY '02a36462-49b7-406a-a3b6-d5accd6695e5'
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where BINARY EntitySecurityKey = '02a36462-49b7-406a-a3b6-d5accd6695e5'
我不熟悉Rhino Security,但据我所知它使用NHibernate来存储到数据库。
.net Guid内部使用1个Int32、2个Int16和8个字节来存储128位Guid值。
当NHibernate(3.2)将guid值存储到BINARY(16)列中或从中提取一个Guid值时,它分别使用.Net ToByteArray()方法和Guid(Byte [] ...)构造函数。这些方法基本上将字节顺序交换为Int32和Int16值。您可以考虑一下方法以查看确切的代码,但这是前4个字节的简单示例:
guidBytes[0] = (byte)this._int32member;
guidBytes[1] = (byte)(this._int32member >> 8),
guidBytes[2] = (byte)(this._int32member >> 16),
guidBytes[3] = (byte)(this._int32member >> 24);
这可能是造成您问题的原因。
例如,您的Guid以不同的方式存储在MySql中。
02a36462-49b7-406a-a3b6-d5accd6695e5-Guid.ToString()的指导
6264a302-b749-6a40-a3b6-d5accd6695e5-您使用MySql hex(Guid)在MySql中进行的引导
注意:hex(x)不添加破折号;为了进行比较,我手动添加了破折号。
请注意,前8个字节的顺序不同。
要测试这是否是您的问题,请运行以下查询:
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where EntitySecurityKey = UNHEX(REPLACE(6264a302-b749-6a40-a3b6-d5accd6695e5,'-',''));
如果是这样,我在自己的旅行中发现了其他一些事项:
Toad for MySql也使这一点变得复杂,因为它会在select语句输出中将Guid显示为.net等效项。如果将Guid包含在select的where子句中(至少根据我的经验),它将不会应用相同的转换。您将需要使用MySql存储的Guid。
您可能需要在执行数据库查询时手动转换Guid,或者可能需要引入自定义NHibernate类型以不同方式转换Guid。
您可以参考:
如何将.NET Guid读取为Java UUID
http://pastebin.com/M07wnK5K(我没有使用此代码,但发现它对参考很有用)
更新:
我只是在上面的pastebin链接上尝试了自定义NH类型。字节顺序不正确,至少对于我的环境而言。我会需要:
private static readonly int[] ByteOrder = new[] { 3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15 };
使.Net输出与从表输出中选择的十六进制(Guid列)匹配。
让我感到非常有趣的是,您将GUID存储在binary(16)字段中,重点放在16上。根据手册,binary字段的长度以字节为单位,并将截断它上面的所有内容(仅在严格模式)。您的GUID是否有可能被截断?使用示例GUID 02a36462-49b7-406a-a3b6-d5accd6695e5,尝试使用前16个字符查询数据库:
WHERE EntitySecurityKey = '02a36462-49b7-40'
根据对此问题的公认答案,字段应为char(16) binary来存储GUID,而不仅仅是binary(16)。但是,我无法在示例表中使用它。
对我有用的是使用char(36)以及binary(36)。尝试将您的字段长度更新为36,而不是16(为了安全起见,我会先在测试表上执行此操作)。
这是我的测试表:
CREATE TABLE test_security_keys (
test_key1 char(16) not null,
test_key2 char(16) binary not null,
test_key3 char(36) not null,
test_key4 binary(16) not null,
test_key5 binary(36) not null
);
然后,我运行了一个脚本来插入许多GUID(一行中的每一列都插入一个GUID)。您可以使用示例GUID对其进行测试:
INSERT INTO test_security_keys
VALUES ('02a36462-49b7-406a-a3b6-d5accd6695e5', '02a36462-49b7-406a-a3b6-d5accd6695e5', '02a36462-49b7-406a-a3b6-d5accd6695e5', '02a36462-49b7-406a-a3b6-d5accd6695e5', '02a36462-49b7-406a-a3b6-d5accd6695e5');
使用简单的SELECT * FROM test_security_keys将显示除大小为36的列以外的所有列。另外,无论是否binary,我都能通过常规的字符串比较成功查询列:
SELECT * FROM test_security_keys WHERE test_key3 = '02a36462-49b7-406a-a3b6-d5accd6695e5';
SELECT * FROM test_security_keys WHERE test_key5 = '02a36462-49b7-406a-a3b6-d5accd6695e5';
如果您已确认当前带有binary(16)的列没有被截断,那么我建议您在WHERE子句中使用CAST()。以下应该适用(与您的示例查询一起使用):
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
WHERE
EntitySecurityKey = CAST('02a36462-49b7-406a-a3b6-d5accd6695e5' AS binary(16));
如果您CAST(.. AS binary(16))并且输入数据长于16个字节,则MySQL应该发出警告(应该是看不见的,不影响任何内容),指出它必须截断数据(如果得到了,请尝试SHOW WARNINGS;)。这是预料之中的,但这也意味着您不能使用binary(16)来存储GUID,而需要使用binary(36)或char(36)。
*我没有使用TOAD尝试任何一种方法,但是我同时使用了命令行MySQL和Navicat,并且两者的结果相同。
这不是问题,GUID只是一个128位的数字,即16个字节,可以存储在一个包含16个字节的二进制字段中-这就是binary(16)。 问题在于,与其他系统相比,.NET guid.ToByteArray()处理字节序的方式有所不同。 请阅读下面的@ user1748279答案以获取适当的信息,或者直接访问stackoverflow.com/questions/5745512/
当我评估其他人如何"搜索"主键为二进制类型(主要是指示GUID的binary(16))的记录并将其与我自己的记录进行比较时,遇到了这个问题。我必须承认我有点退缩,答案迅速脱离了用户希望根据二进制字段进行手动搜索的想法,而是专注于将列类型本身更改为字符,在这种情况下,这是对InnoDB存储的谋杀。
对于缺少结果的尝试解决方案是针对Id列而不是EntitySecurityKey列进行搜索。给定JP的原始查询,修改后的工作版本为:
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where Id = UNHEX(REPLACE('02a36462-49b7-406a-a3b6-d5accd6695e5', '-', ''));
同样,这假定Id字段是binary(16)类型。 (我无法确定哪一列:Id,EntitySecurityKey是二进制文件(16))。如果EntitySecurityKey是binary(16)列,则:
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where EntitySecurityKey = UNHEX(REPLACE('02a36462-49b7-406a-a3b6-d5accd6695e5', '-', ''));
如果未能产生期望的结果,则针对此问题(如何将.NET Guid读取为Java UUID),尝试进行相同的查询但GUID的前三个部分的字节序颠倒了,这将很有意义。
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where Id = UNHEX(REPLACE('6264a302-b749-6a40-a3b6-d5accd6695e5', '-', ''));
我能想到的唯一另一件事是EntitySecurityKey是基于ID的虚拟列,但是我在表设置上没有足够的信息来验证此语句。
干杯。
从您的修改中,我做了一点修改:
请试试
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
WHERE EntitySecurityKey = X'02a3646249b7406aa3b6d5accd6695e5'
由于每个字符串都有16个十六进制字节,因此可以使用...
您是否检查过表格中GUID数据的实际外观?由于它是主键,因此很有可能不会存储为GUID字符串。如果是这种情况,查询字符串值显然将不起作用。
可能是二进制格式?或者可能是一个字符串,但没有连字符?还是它已经以其他方式转换了?我不知道没有看到您的数据。但是无论如何,如果在查询时没有找到您知道的GUID,则无论它采用何种格式,显然都不会以您查询的格式存储它。
我要解决此问题的方法是在您知道存在的记录中搜索不同的值。甚至查询根本没有WHERE子句的前几条记录。
这将向您显示数据的实际外观。运气好的话,这足以让您确定其格式。
如果这样做没有帮助,则可能会从以下问题中得出一个线索:为什么二进制Guid与通常的表示形式有所不同
希望能有所帮助。
不知道如何设置数据库,但是我会尝试这两种变体:
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where EntitySecurityKey = x'02a3646249b7406aa3b6d5accd6695e5'
SELECT Id, EntitySecurityKey, Type
FROM mydb.security_entityreferences
where EntitySecurityKey = x'6264a302b7496a40b6a3d5accd6695e5'