How to programmatically create a Java ResultSet from custom data with no database

Viewed 43749

I have some existing code that accepts a java.sql.ResultSet that contains info retrieved from an Oracle database. I would now like to reuse this code, but I'd like to pass it a ResultSet object that I create myself from some in-memory data that is not affiliated with any database. Is there an existing Java framework class that I can use for this? ResultSet has tons of methods, so implementing my own class for this seemed like overkill, even though I could ignore most of the methods for my specific case.

I was thinking of something along the lines of the old Microsoft ADO recordset object, where I could create the fields and then populate the row data for each field. This seemed like an easily googlable question, but I've been unable to find any good pointers.

8 Answers
  • Create your own AbstractResultSet class, one that (like AbstractQueue) implements all methods by throwing UnsupportedOperationException (Eclipse autogenerates these methods in a split second).
  • Now extend AbstractResultSet. The subclass can override only the methods you're interested in implementing.

This is a slightly left-field solution, but you could make use of a mocking framework (e.g. JMock). These frameworks are generally intended for creating mock implementations of interfaces for unit testing, but I see no reason why you could use one to create a "partial implementation" of java.sql.ResultSet.

For example, say you only wanted to implement the getString() method, and nothing else:

Mockery mockery = new Mockery();
final ResultSet resultSet = mockery.mock(ResultSet.class);

mockery.checking(new Expectations() {{
    allowing(resultSet).getString(1); will(returnValue("my first string"));
    allowing(resultSet).getString(2); will(returnValue("my second string"));
}});

// resultSet is now a java.sql.ResultSet object, which you can pass to your legacy code
resultSet.getString(1);

Rather unorthodox, and quite cumbersome, but it should work

My first option would be to refactor the code so that it takes List<Map<String,Object>> or something appropriate (that is, List<Map<String,Object>> if the data being moved around has no real structure or fully-typed objects if it does have structure).

If that's not feasible or time efficient, the "fun" hack is to query an in-memory H2 database to get a ResultSet. If you don't find a reasonable stub for ResultSet you can easily instantiate, it might be quicker than rolling your own (pull the jar in, write 20 lines of code for creating the db and populating a table).

jOOQ has a MockResultSet for that and similar purpose, which can be used out of the box, or copied into your project.

DSLContext ctx = DSL.using(DEFAULT);
Field<Integer> col1 = DSL.field("COL1", SQLDataType.INTEGER);
Field<String> col2 = DSL.field("COL2", SQLDataType.VARCHAR(10));

Result<?> result = ctx.newResult(col1, col2);
result.add(ctx.newRecord(col1, col2).values(1, "a"));
result.add(ctx.newRecord(col1, col2).values(2, "b"));

try (ResultSet rs = new MockResultSet(result)) {
    while (rs.next())
        System.out.println(rs.getInt(1) + ": " + rs.getString(2));
}

Disclaimer: I work for the company behind jOOQ.

java.sql.ResultSet is an interface, so you could create your own class that implements that interface.

I don't normally answer java questions, as I'm not a java developer, but this seems like an architectural flaw if you need to create a sql object from code to pass into a method in order to recycle a method. I would think you would want to make your receiving method accept some other more specific form of input (such as a custom defined object array) to make it reusable, and then parse your ResultData into that format.

If you need the ResultSet for unit test purposes, you could try this:

https://gist.githubusercontent.com/jrichardsz/661913b8d52b6e820a1b37b972ffdb49/raw/4fc10e4624795b9671d43c5ac9169aa2ae532266/MockResultSet.java

Just needs one dependency:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <version>1.10.19</version>
</dependency>

It works like a charm:

ResultSet mockResultSet = MockResultSet.create(
    new String[] {"name", "lastname"},
    new String[][] {{"jane", "doe"}, {"kurt", "weller"}}
);
Related