差不多 8 年后,我自己的建议是基于一些艰苦的学习:
完全不使用排序规则(H2 数据库的默认设置)。
理由:使用排序规则会产生一些真正意想不到的结果和错误。
陷阱:独特的约束
到目前为止,我在日常业务中看到的最常见的唯一约束是强制唯一(名字、姓氏)。通常情况下,应忽略大小写(禁止同时使用 'thomas müller' 和 'Thomas Müller'),但不能忽略变音符号(允许同时使用 'Thomas Müller' 和 'Thomas Muller')。
使用排序规则强度SECONDARY 设置来实现此目的可能很诱人(不区分大小写但区分变音符号)。别。 改用VARCHAR_IGNORECASE 列。
{
// NOT recommended: using SECONDARY collation
Statement s = DriverManager.getConnection("jdbc:h2:mem:", "test", "test").createStatement();
s.execute("SET COLLATION ENGLISH STRENGTH SECONDARY");
s.execute("CREATE TABLE test ( name VARCHAR )");
s.execute("ALTER TABLE test ADD CONSTRAINT unique_name UNIQUE(name)");
s.execute("INSERT INTO test (name) VALUES ('Müller')");
s.execute("INSERT INTO test (name) VALUES ('Muller')");
// s.execute("INSERT INTO test (name) VALUES ('muller')" /* will fail */);
}
{
// recommended: no collation, using VARCHAR_IGNORECASE instead of VARCHAR column
Statement s = DriverManager.getConnection("jdbc:h2:mem:", "test", "test").createStatement();
s.execute("CREATE TABLE test ( name VARCHAR_IGNORECASE )");
s.execute("ALTER TABLE test ADD CONSTRAINT unique_name UNIQUE(name)");
s.execute("INSERT INTO test (name) VALUES ('Müller')");
s.execute("INSERT INTO test (name) VALUES ('Muller')");
// s.execute("INSERT INTO test (name) VALUES ('muller')" /* will fail */);
}
陷阱:搜索/WHERE 子句
建议:没有排序规则的默认行为很好,并且行为符合预期。要进行更模糊的搜索,请使用您自己的代码搜索或类似 Lucene 的库。
SECONDARY 排序规则强度将匹配,即使大小写不同。使用 SELECT WHERE name = '...' 时不会出现这种行为,因为您会忘记所有关于排序规则的设置。
{
Statement s = DriverManager.getConnection("jdbc:h2:mem:", "test", "test").createStatement();
s.execute("SET COLLATION ENGLISH STRENGTH SECONDARY");
s.execute("CREATE TABLE test ( name VARCHAR )");
s.execute("INSERT INTO test (name) VALUES ('Thomas Müller')");
ResultSet rs = s.executeQuery("SELECT count(*) FROM test WHERE name = 'Thomas müller'" /* different case */);
rs.next();
/* prints 1 (!) */ System.out.println(rs.getLong(1));
}
PRIMARY 排序规则强度将匹配,即使 SPACES 不同。您会相信英语主要排序规则会忽略空格吗?看看这个金块:https://stackoverflow.com/a/16567963/1124509
{
Statement s = DriverManager.getConnection("jdbc:h2:mem:", "test", "test").createStatement();
s.execute("SET COLLATION ENGLISH STRENGTH PRIMARY");
s.execute("CREATE TABLE test ( name VARCHAR )");
s.execute("INSERT INTO test (name) VALUES ('Thomas Müller')");
ResultSet rs = s.executeQuery("SELECT count(*) FROM test WHERE name = 'ThomasMüller'" /* no space! */);
rs.next();
/* prints 1 (!) */ System.out.println(rs.getLong(1));
}
排序 / ORDER BY 子句
没有排序规则的默认排序在实际场景中并不是很有用,因为它会根据严格的字符串比较进行排序。通过首先从数据库中加载数据,然后使用代码对其进行排序/排序来解决此问题。
就我个人而言,我主要使用固定spaces problem 的英语初级强度整理器。即使是非英文文本列也能正常工作。
但您可能还需要使用自定义比较器来满足更困难的要求,例如自然或直观的排序顺序,例如sort like windows explorer,或semantic versioning。
{
Statement s = DriverManager.getConnection("jdbc:h2:mem:", "test", "test").createStatement();
s.execute("CREATE TABLE test ( name VARCHAR )");
s.execute("INSERT INTO test (name) VALUES ('é6')");
s.execute("INSERT INTO test (name) VALUES ('e5')");
s.execute("INSERT INTO test (name) VALUES ('E4')");
s.execute("INSERT INTO test (name) VALUES ('ä3')");
s.execute("INSERT INTO test (name) VALUES ('a2')");
s.execute("INSERT INTO test (name) VALUES ('A1')");
ResultSet rs = s.executeQuery("SELECT name FROM test ORDER BY name");
List<String> names = new ArrayList<>();
while(rs.next()) {
names.add(rs.getString(1));
}
// not very useful strict String.compareTo() result: [A1, E4, a2, e5, ä3, é6]
System.out.print(names);
String rules = ((RuleBasedCollator) Collator.getInstance(new Locale("en", "US"))).getRules();
Collator collator = new RuleBasedCollator(rules.replaceAll("<'\u005f'", "<' '<'\u005f'"));
collator.setStrength(Collator.PRIMARY);
names.sort((a, b) -> collator.compare(a, b));
// as humans usually expect it in a name list / table: [A1, a2, ä3, E4, e5, é6]
System.out.print(names);
}
如何检查您的 H2 数据库是否使用排序规则?
查看设置表。如果未设置排序规则,则表中将没有条目。