java – JDBC-ODBC Bridge查询到Access时,它们具有重音字符

我正在通过JDBC-ODBC Bridge向Java中的Access数据库发送查询,如下所示:

"SELECT * FROM localities WHERE locName='" + cityName + "'"

当cityName是没有重音字符的普通字符串时,结果集是正确的.
但是当cityName恰好是LEÓN,SAHAGÚN之类的东西,其中带有重音字符,那么我就没有结果.在这些情况下,查询似乎失败了.
在MS Access中运行时相同的查询工作正常,我也尝试使用Ms Data Acces SKD并且这些查询完美运行.

它们仅在通过JDBC-ODBC Bridge时失败.
据我所知,Java使用UTF-8作为字符串,Access也是如此.他们都使用Unicode.
有谁知道这个问题的任何解决方案?

解决方法:

听起来你的Java源文件被编码为UTF-8,所以当cityName字符串包含LEÓN时,它被编码为

L  E  Ó     N
-- -- ----- --
4C 45 C3 93 4E

这不是Access存储值的方式. Access将字符存储为Unicode,但不使用UTF-8编码.它使用UTF-16LE编码的变体,其中代码点U 00FF及以下的字符存储在单个字节中,代码点高于U 00FF的字符存储为Null(0x0)值,后跟其UTF-16LE字节对(S).在这种情况下,Ó是U 00D3,低于U 00FF,因此Access将字符串的所有四个字符存储为单个字节:

L  E  Ó  N
-- -- -- --
4C 45 D3 4E

实际效果是Access数据库中字符串的编码与ISO 8859-1字符集的编码相同.

这可以通过以下使用JDBC-ODBC Bridge的Java代码来确认.当Java源文件编码为UTF-8时,它无法找到所需的记录,但是当Java源文件在Eclipse中编码为cp1252时它可以工作:

import java.sql.*;

public class accentTestMain {

    public static void main(String[] args) {
        String connectionString = 
                "jdbc:odbc:Driver={Microsoft Access Driver (*.mdb, *.accdb)};" + 
                "DBQ=C:\\__tmp\\test\\accented.accdb;";
        try {
            Connection con = DriverManager.getConnection(connectionString);
            PreparedStatement stmt = con.prepareStatement("SELECT * FROM localities WHERE locName=?");
            String cityName = "LEÓN";
            stmt.setString(1, cityName);
            stmt.execute();
            ResultSet rs = stmt.getResultSet();
            if (rs.next()) {
                System.out.println(String.format("Record found, ID=%d", rs.getInt("ID")));
            }
            else {
                System.out.print("Record not found.");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

如果你只能支持cp1252字符集中表示的重音字符,那么你应该能够简单地使用cp1252作为Java源文件的编码设置.

另一方面,如果您确实需要Access数据库的完全Unicode字符支持,那么JDBC-ODBC Bridge将无法为您完成工作.这是JDBC-ODBC Bridge和Access ODBC驱动程序之间长期存在的互操作性问题,并且不会修复. (更多细节here.)

在这种情况下,您可能需要考虑使用UCanAccess,它是Access的纯Java JDBC驱动程序.使用带有UTF-8编码源文件的UCanAccess的相应代码将是

// assumes...
//     import java.sql.*;
Connection conn=DriverManager.getConnection(
        "jdbc:ucanaccess://C:/__tmp/test/accented.accdb");
PreparedStatement ps = conn.prepareStatement(
        "SELECT ID FROM localities WHERE locName=?");
ps.setString(1, "LEÓN");
ResultSet rs = ps.executeQuery();
if (rs.next()) {
    System.out.println(String.format(
            "Record found, ID=%d", 
            rs.getInt("ID")));
}
else {
    System.out.println("Record not found.");
}

有关使用UCanAccess的更多信息,请参阅相关问题here.

另一种解决方案是使用Jackcess来操作Access数据库(同样,Java源文件编码为UTF-8):

import java.io.File;
import java.io.IOException;
import com.healthmarketscience.jackcess.*;

public class accentTestMain {

    public static void main(String[] args) {
        Database db;
        try {
            db = DatabaseBuilder.open(new File("C:\\__tmp\\test\\accented.accdb"));
            try {
                Table tbl = db.getTable("localities");
                Cursor crsr = CursorBuilder.createCursor(tbl.getIndex("locName"));
                if (crsr.findFirstRow(tbl.getColumn("locName"), "LEÓN")) {
                    System.out.println(String.format("Record found, ID=%d", crsr.getCurrentRowValue(tbl.getColumn("ID"))));
                }
                else {
                    System.out.println("Record not found.");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                db.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
上一篇:利用word宏功能一键导出数据库表结构


下一篇:python – 安装odbc驱动程序到azure应用程序服务