简洁版本
我目前正在研究 MySQL 排序规则的问题以及它们如何影响一组值(使用 Hibernate 映射,但现在应该不重要)。我想拥有一组使用与 MySQL 使用相同的排序规则的字符串。例如,我希望“foobar”和“fööbar”被认为是相等的,但“foo bar”和“foobar”被认为是不同的。使用默认Collator.getInstance()(具有Collator.PRIMARY强度)不能可靠地工作,因为仍然存在差异(最明显的是空格)。那么如何为每个可能的 String 获得一个与 MySQL 表现相同的 Collator?
长版
我想在存储 Set 值的表上有一个唯一索引,并确保 Set 仅包含数据库中允许的值,反之亦然。
表如下所示:
CREATE TABLE `MY_SET` (
`entity_id` int NOT NULL,
`value` varchar(255) NOT NULL,
UNIQUE `entity-value`(`entity_id`, `value`)
) ENGINE = InnoDB DEFAULT CHARSET=latin1 DEFAULT COLLATION=;
现在,如果我使用普通字符串和 HashSet 来保存我的值,例如
public class MyValues {
private MyEntity _myEntity;
private final HashSet<String> _values = new HashSet<String>();
}
可以将“foobar”和“fööbar”都添加到值集中。现在,如果 Hibernate 将 Set 刷新到 DB,MySQL 将抱怨“foobar”和“fööbar”对定义的“实体值”键重复。因此,我想我包装字符串并使用Collator来检查字符串是否相等:
public class MyValues {
private MyEntity _entity;
private final HashSet<CollatedString> _values = new HashSet<CollatedString>();
}
public static class CollatedString {
private String _string;
private CollationKey _key;
public String getString() {
return _string;
}
public void setString(final String string) {
_string = string;
_key = getCollator().getCollationKey(_string);
}
@Override
public int hashCode() {
return _key.hashCode();
}
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof CollatedString)) {
return false;
}
return _key.equals(((CollatedString) obj)._key);
}
}
这适用于 "foobar" 和 "fööbar":
final MyEntity e = new MyEntity();
final MyValues v = new MyValues();
v.setEntity(e);
v.getValues().add(new CollatedString("foobar"));
v.getValues().add(new CollatedString("fööbar"));
System.out.println("1 == " + v.getValues().size()); // prints 1 == 1
但不适用于 MySQL 认为不同的“foo bar”和“foobar”:
v.getValues().add(new CollatedString("foobar"));
v.getValues().add(new CollatedString("foo bar"));
System.out.println("2 == " + v.getValues().size()); // prints 2 == 1 (which is wrong)
基本上剩下要做的就是实现getCollator()方法:
public static final Collator getCollator() {
// FIXME please help!
}
示例的完整代码可用:下载