MessageRowMapper implements ResultSetMapper<M> { @Override public M map(int idx, ResultSet rs, StatementContext ctx) throws SQLException { PMessageBuilder<M,F> builder = descriptor.builder(); for (int i = 1; i <= rs.getMetaData().getColumnCount(); ++i) { if (!tableName.isEmpty() && !tableName.equalsIgnoreCase(rs.getMetaData().getTableName(i))) { continue; } String name = rs.getMetaData().getColumnLabel(i).toUpperCase(Locale.US); F field = fieldNameMapping.get(name); if (field != null) { int columnType = rs.getMetaData().getColumnType(i); switch (field.getType()) { case BOOL: { if (columnType == Types.BOOLEAN || columnType == Types.BIT) { boolean b = rs.getBoolean(i); if (!rs.wasNull()) { builder.set(field, b); } } else { int b = rs.getInt(i); if (!rs.wasNull()) { builder.set(field, b != 0); } } break; } case BYTE: { byte b = rs.getByte(i); if (!rs.wasNull()) { builder.set(field, b); } break; } case I16: { short b = rs.getShort(i); if (!rs.wasNull()) { builder.set(field, b); } break; } case I32: { if (columnType == Types.TIMESTAMP) { Timestamp ts = rs.getTimestamp(i); if (ts != null) { builder.set(field, (int) (ts.getTime() / 1000L)); } } else { int b = rs.getInt(i); if (!rs.wasNull()) { builder.set(field, b); } } break; } case I64: { if (columnType == Types.TIMESTAMP) { Timestamp ts = rs.getTimestamp(i); if (ts != null) { builder.set(field, ts.getTime()); } } else { long b = rs.getLong(i); if (!rs.wasNull()) { builder.set(field, b); } } break; } case DOUBLE: { double b = rs.getDouble(i); if (!rs.wasNull()) { builder.set(field, b); } break; } case STRING: { builder.set(field, rs.getString(i)); break; } case BINARY: { switch (columnType) { case Types.BINARY: case Types.VARBINARY: byte[] ts = rs.getBytes(i); if (ts != null) { builder.set(field, Binary.copy(ts)); } break; case Types.BLOB: Blob blob = rs.getBlob(i); if (blob != null) { try { builder.set(field, Binary.read(blob.getBinaryStream(), (int) blob.length())); } catch (IOException e) { throw new UncheckedIOException(e.getMessage(), e); } } break; case Types.CHAR: case Types.VARCHAR: case Types.NCHAR: case Types.NVARCHAR: { String tmp = rs.getString(i); if (tmp != null) { builder.set(field, Binary.fromBase64(tmp)); } break; } case Types.NULL: break; default: throw new SQLDataException("Unknown column type " + rs.getMetaData().getColumnTypeName(i) + " for " + descriptor.getType().toString() + " field " + name + " in " + descriptor.getQualifiedName()); } break; } case ENUM: { int val = rs.getInt(i); if (!rs.wasNull()) { PEnumDescriptor ed = (PEnumDescriptor) field.getDescriptor(); builder.set(field, ed.findById(val)); } break; } case MESSAGE: { try { PMessageDescriptor<?,?> md = (PMessageDescriptor) field.getDescriptor(); switch (columnType) { case Types.BINARY: case Types.VARBINARY: byte[] data = rs.getBytes(i); if (data != null) { ByteArrayInputStream in = new ByteArrayInputStream(data); builder.set(field, BINARY.deserialize(in, md)); } break; case Types.BLOB: { Blob blob = rs.getBlob(i); if (blob != null) { builder.set(field, BINARY.deserialize(blob.getBinaryStream(), md)); } break; } case Types.CHAR: case Types.VARCHAR: case Types.NCHAR: case Types.NVARCHAR: { String tmp = rs.getString(i); if (tmp != null) { StringReader reader = new StringReader(tmp); builder.set(field, JSON.deserialize(reader, md)); } break; } case Types.CLOB: { Clob clob = rs.getClob(i); if (clob != null) { builder.set(field, JSON.deserialize(clob.getCharacterStream(), md)); } break; } case Types.NULL: break; default: throw new SQLDataException("Unknown column type " + rs.getMetaData().getColumnTypeName(i) + " for " + descriptor.getType().toString() + " field " + name + " in " + descriptor.getQualifiedName()); } } catch (IOException e) { throw new UncheckedIOException(e.getMessage(), e); } break; } case LIST: case SET: case MAP: { } case VOID: default: { throw new SQLDataException("Unhandled column of type " + rs.getMetaData().getColumnTypeName(i) + " for " + descriptor.getType().toString() + " field " + name + " in " + descriptor.getQualifiedName()); } } } } return builder.build(); } MessageRowMapper(@Nonnull PMessageDescriptor<M,F> descriptor);  MessageRowMapper(@Nonnull String tableName, @Nonnull PMessageDescriptor<M,F> descriptor);  MessageRowMapper(@Nonnull PMessageDescriptor<M,F> descriptor,
                            @Nonnull Map<String,F> fieldMapping);  MessageRowMapper(@Nonnull String tableName,
                            @Nonnull PMessageDescriptor<M,F> descriptor,
                            @Nonnull Map<String,F> fieldMapping); @Override M map(int idx, ResultSet rs, StatementContext ctx); static final String ALL_FIELDS; }
@Test public void testDefaultMapping() { generator.setFillRate(1.0) .setMaxCollectionItems(16) .withGenerator(CompactFields.kDescriptor, g -> { g.setValueGenerator(CompactFields._Field.NAME, ctx -> ctx.getFairy().textProducer().latinWord()); g.setValueGenerator(CompactFields._Field.LABEL, ctx -> ctx.getFairy().textProducer().word()); }).withGenerator(NormalFields.kDescriptor, g -> { g.setValueGenerator(NormalFields._Field.NAME, ctx -> ctx.getFairy().textProducer().latinWord()); g.setValueGenerator(NormalFields._Field.LABEL, ctx -> ctx.getFairy().textProducer().word()); }); OptionalFields expected = generator.generate(OptionalFields.kDescriptor) .mutate() .setId(1234) .setTimestampS((int) clock.instant().getEpochSecond()) .setTimestampMs(clock.instant().getEpochSecond() * 1000) .build(); OptionalFields empty = OptionalFields.builder() .setId(2345) .build(); try (Handle handle = db.getDBI().open()) { handle.createStatement("INSERT INTO mappings.default_mappings (" + " id, present, tiny, small, medium, large, real," + " fib, name, data, compact," + " timestamp_s, timestamp_ms," + " binary_message, blob_message, other_message," + " blob_data, base64_data, int_bool" + ") VALUES (" + " :e.id," + " :e.present," + " :e.tiny," + " :e.small," + " :e.medium," + " :e.large," + " :e.real," + " :e.fib," + " :e.name," + " :e.data," + " :e.message," + " :timestamp_s," + " :e.timestamp_ms," + " :e.binary_message," + " :e.blob_message," + " :e.clob_message," + " :e.blob_data," + " :e.base64_data," + " :e.int_bool" + ")") .bind("timestamp_s", ProvidenceJdbi.toField(expected, TIMESTAMP_S, Types.TIMESTAMP)) .bindNamedArgumentFinder(ProvidenceJdbi.forMessage("e", expected, ProvidenceJdbi.withType(TIMESTAMP_MS, Types.TIMESTAMP), ProvidenceJdbi.withType(BINARY_MESSAGE, Types.BINARY), ProvidenceJdbi.withType(BLOB_MESSAGE, Types.BLOB), ProvidenceJdbi.withType(CLOB_MESSAGE, Types.CLOB), ProvidenceJdbi.withType(BLOB_DATA, Types.BLOB), ProvidenceJdbi.withType(BASE64_DATA, Types.VARCHAR))) .execute(); handle.createStatement("INSERT INTO mappings.default_mappings (" + " id, present, tiny, small, medium, large, real," + " fib, name, data, compact," + " timestamp_s, timestamp_ms," + " binary_message, blob_message, other_message," + " blob_data, base64_data, int_bool" + ") VALUES (" + " :id," + " :present," + " :tiny," + " :small," + " :medium," + " :large," + " :real," + " :fib," + " :name," + " :data," + " :message," + " :timestamp_s," + " :timestamp_ms," + " :binary_message," + " :blob_message," + " :clob_message," + " :blob_data," + " :base64_data," + " :int_bool" + ")") .bind("timestamp_s", ProvidenceJdbi.toField(empty, TIMESTAMP_S, Types.TIMESTAMP)) .bindNamedArgumentFinder(ProvidenceJdbi.forMessage(empty, ProvidenceJdbi.withType(TIMESTAMP_MS, Types.TIMESTAMP), ProvidenceJdbi.withType(BINARY_MESSAGE, Types.BINARY), ProvidenceJdbi.withType(BLOB_MESSAGE, Types.BLOB), ProvidenceJdbi.withType(CLOB_MESSAGE, Types.CLOB), ProvidenceJdbi.withType(BLOB_DATA, Types.BLOB), ProvidenceJdbi.withType(BASE64_DATA, Types.VARCHAR), ProvidenceJdbi.withType(INT_BOOL, Types.INTEGER))) .execute(); OptionalFields val = handle.createQuery("SELECT m.* FROM mappings.default_mappings m WHERE id = :id") .bind("id", ProvidenceJdbi.toField(expected, ID)) .map(ProvidenceJdbi.toMessage("default_mappings", OptionalFields.kDescriptor, ProvidenceJdbi.columnsFromAllFields(), ProvidenceJdbi.withColumn("compact", MESSAGE), ProvidenceJdbi.withColumn("other_message", CLOB_MESSAGE))) .first(); OptionalFields val2 = handle.createQuery("SELECT * FROM mappings.default_mappings WHERE id = :id") .bind("id", ProvidenceJdbi.toField(empty, ID)) .map(ProvidenceJdbi.toMessage(OptionalFields.kDescriptor, ProvidenceJdbi.columnsFromAllFields(), ProvidenceJdbi.withColumn("compact", MESSAGE), ProvidenceJdbi.withColumn("other_message", CLOB_MESSAGE))) .first(); assertThat(val, is(equalToMessage(expected))); assertThat(val2, is(equalToMessage(empty))); } }