读取一个 Access 数据库,该数据库使用带有 Excel 个工作表的链接表

Read an Access database which uses linked tables with Excel sheets

我正在尝试使用 Java 库 Jackcess 从 Access 数据库中读取数据。数据库有几个 table 和查询,其中一些链接 table 指向文件系统上的 Excel 个工作表。

我看到我可以使用 LinkResolver 来拦截链接数据的解析,但它需要一个完整的数据库,而不仅仅是一个 table 的数据。

我可以轻松地使用 Apache POI 打开 Excel 文件并提取必要的数据,但我不知道如何在 LinkResolver 中传递数据。

提供 Excel 文件的位置或从 Excel 文件中读取数据并将其传递回 Jackcess 以便其成功加载链接数据的最简单方法是什么?

目前,LinkResolver API 仅用于从其他数据库加载 "remote" Table 实例。它不是为任何类型的外部文件而构建的通用 API。您当然可以向 Jackcess 项目提交功能请求。

更新:

从 2.1.7 版本开始,jackcess 提供了 CustomLinkResolver 实用程序来帮助从非访问数据库(使用临时数据库)的文件加载链接表。

我想到了 LinkResolver 的以下初始实现,它使用 Excel 文件中的内容构建一个临时数据库。它仍然缺少临时数据库的关闭处理和临时文件删除等功能,但它似乎可以用于基本目的。

/**
 * Sample LinkResolver which reads the data from an Excel file
 * The data is read from the first sheet and needs to contain a
 * header-row with column-names and then data-rows with string/numeric values.
 */
public class ExcelFileLinkResolver implements LinkResolver {
    private final LinkResolver parentResolver;
    private final String fileNameInDB;
    private final String tableName;
    private final File excelFile;

    public ExcelFileLinkResolver(LinkResolver parentResolver, String fileNameInDB, File excelFile, String tableName) {
        this.parentResolver = parentResolver;
        this.fileNameInDB = fileNameInDB;
        this.excelFile = excelFile;
        this.tableName = tableName;
    }

    @Override
    public Database resolveLinkedDatabase(Database linkerDb, String linkeeFileName) throws IOException {
        if(linkeeFileName.equals(fileNameInDB)) {
            // TODO: handle close or create database in-memory if possible
            File tempFile = File.createTempFile("LinkedDB", ".mdb");
            Database linkedDB = DatabaseBuilder.create(Database.FileFormat.V2003, tempFile);

            try (Workbook wb = WorkbookFactory.create(excelFile, null, true)) {
                TableBuilder tableBuilder = new TableBuilder(tableName);

                Table table = null;
                List<Object[]> rows = new ArrayList<>();
                for(org.apache.poi.ss.usermodel.Row row : wb.getSheetAt(0)) {
                    if(table == null) {
                        for(Cell cell : row) {
                            tableBuilder.addColumn(new ColumnBuilder(cell.getStringCellValue()
                                    // column-names cannot contain some characters
                                    .replace(".", ""),
                                    DataType.TEXT));
                        }

                        table = tableBuilder.toTable(linkedDB);
                    } else {
                        List<String> values = new ArrayList<>();
                        for(Cell cell : row) {
                            if(cell.getCellTypeEnum() == CellType.NUMERIC) {
                                values.add(Double.toString(cell.getNumericCellValue()));
                            } else {
                                values.add(cell.getStringCellValue());
                            }
                        }
                        rows.add(values.toArray());
                    }
                }
                Preconditions.checkNotNull(table, "Did not have a row in " + excelFile);
                table.addRows(rows);
            } catch (InvalidFormatException e) {
                throw new IllegalStateException(e);
            }

            return linkedDB;
        }
        return parentResolver.resolveLinkedDatabase(linkerDb, linkeeFileName);
    }
}