0

I am trying to read data from an Access database using the Java library Jackcess. The database has several tables and queries, some of which are linked tables pointing to Excel sheets on the file-system.

I saw that I can use a LinkResolver to intercept the resolving of the linked data, but it expects a full-blown Database, not just data for one single table.

I can easily use Apache POI to open the Excel file and extract the necessary data, but I don't know how I can pass the data in the LinkResolver.

What is the simplest way to provide the location of the Excel file or read the data from the Excel file and pass it back to Jackcess so it can load the linked data successfully?

centic
  • 15,565
  • 9
  • 68
  • 125
  • Is there a reason you need the data to be specifically loaded via the Jackcess API (as opposed to just accessing the relevant data via POI)? – jtahlborn Apr 30 '17 at 23:06
  • I want to actually read the data in the Access Database, the Excel file is just necessary for some queries and Jackcess fails without the link being resolved correctly. – centic May 01 '17 at 05:05
  • i don't think i'm understanding. can't you just avoid reading the "remote" tables from within Jackcess? or are you using ucanaccess to run actual sql queries with multiple tables? – jtahlborn May 01 '17 at 13:57
  • I am trying to automatically verify things in the database, but when I read the sql of queries that include this table it fails because it tries to resolve the link then. – centic May 02 '17 at 08:18
  • ah, got it. makes sense. – jtahlborn May 02 '17 at 14:10

2 Answers2

2

At this point in time, the LinkResolver API is only built for loading "remote" Table instances from other databases. It was not built to be a general purpose API for any type of external file. You could certainly file a feature request with the Jackcess project.

UPDATE:

As of the 2.1.7 release, jackcess provides the CustomLinkResolver utility to facilitate loading linked tables from files which are not access databases (using a temporary db).

jtahlborn
  • 52,909
  • 5
  • 76
  • 118
1

I came up with the following initial implementation of a LinkResolver which builds a temporary database with the content from the Excel file. It still lacks some things like Close-handling and temp-file-removal of the temporary database, but it seems to work for basic purposes.

/**
 * 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);
    }
}
centic
  • 15,565
  • 9
  • 68
  • 125
  • heh, i actually came back to this question to post a similar suggestion. i was thinking that jackcess could have a base class for a LinkResolver that gave you the hooks to load the data from the other file and pull it into the temporary database. – jtahlborn May 07 '17 at 03:08
  • Yes, would be nice to have some more support here. Also I seem to have walked into some additional roadblock in the meantime here as jackcess seems to look for the table in the main db, not the linked one, sometimes. – centic May 08 '17 at 06:07
  • yeah, that wouldn't surprise me. the linked stuff hasn't been used heavily (that i'm aware of), and passing the Table instance from one db to another is kind of sketchy. if you find bugs, please [report them](https://sourceforge.net/p/jackcess/bugs/) at the main project site. – jtahlborn May 09 '17 at 01:14
  • i created a [feature reques](https://sourceforge.net/p/jackcess/feature-requests/36/) to track this idea. i have most of the code worked out, just need to polish the edges. – jtahlborn May 11 '17 at 01:48
  • the aforementioned feature request will be in the 2.1.7 release (see CustomLinkResolver). – jtahlborn May 11 '17 at 03:40
  • 1
    Thanks a lot! I'll try that as soon as it is available. In the meantime I managed to link the Excel file into a table in a temporary DB successfully, looks like I am good for some verification/validation code now :) – centic May 11 '17 at 06:05
  • 2.1.7 release is now available, [CustomLinkResolver](http://jackcess.sourceforge.net/xref/com/healthmarketscience/jackcess/util/CustomLinkResolver.html). – jtahlborn May 18 '17 at 03:14