Java Utilities
APACHE-2.0 License
Java utilities for Protobuf message partitioning, table pretty printing and SQL execution recording.
Features:
The utilities are packaged in several modules and require at least JDK 8. They are automatically built and tested using OpenJDK 8 and 11.
To use them, simply declare a dependency towards the module you are interested in:
<dependency>
<groupId>com.github.chrisgleissner</groupId>
<artifactId>jutil-protobuf</artifactId>
<version>1.1.11</version>
</dependency>
<dependency>
<groupId>com.github.chrisgleissner</groupId>
<artifactId>jutil-sql-log</artifactId>
<version>1.1.11</version>
</dependency>
<dependency>
<groupId>com.github.chrisgleissner</groupId>
<artifactId>jutil-table</artifactId>
<version>1.1.11</version>
</dependency>
compile 'com.github.chrisgleissner:jutil-protobuf:1.1.11'
compile 'com.github.chrisgleissner:jutil-sql-log:1.1.11'
compile 'com.github.chrisgleissner:jutil-table:1.1.11'
The ProtobufFieldPartitioner is useful for distributing the elements of a repeated field in a Protobuf message over multiple newly created messages.
This allows for sending a Protobuf message where size restrictions exist, for example when using the Azure Message ServiceBus.
Example:
Collection<Message> msgs = ProtobufFieldPartitioner.partition(msg, repeatedFieldToBePartitioned, 100);
The SqlLog records JSON-formatted SQL executions either in memory or to a file. It gets wired by using Spring Boot 2.3.x auto-configuration and relies on net.ttddyy:datasource-proxy for proxying data sources.
To use this feature, declare a dependency on com.github.chrisgleissner:jutil-sql-log
.
To start recording, wire in the SqlLog
bean and call startRecording
which returns a SqlRecording
.
You will now record all SQL messages sent via any DataSource bean as part of your current thread or any thread which it starts.
The recording is kept either on heap (accessible via sqlRecording.getMessages
and various methods in SqlLog
) or written to the specified file.
All SQL is JSON encoded.
Call sqlLog.stopRecording(id)
or close a SqlRecording
instance to stop its recording.
Any SQL message not recorded otherwise is captured by a default recording which you can get via
sqlLog.getDefaultRecording()
.
This default recording can't be stopped,
but you can temporarily stop all recording (including for the default recording) by calling sqlLog.setEnabled(false)
.
As per ExampleTest, after wiring
@Configuration
public class SampleConfig {
SampleConfig(JdbcTemplate jdbcTemplate, SqlLog sqlLog) {
try (SqlRecording rec = sqlLog.startRecording("example", new File("sql.json"), Charset.forName("UTF-8"))) {
jdbcTemplate.execute("create table foo (id int)");
jdbcTemplate.execute("insert into foo (id) values (1)");
}
}
}
you will find that the sql.json
file contains
[{"success":true, "type":"Statement", "batch":false, "querySize":1, "batchSize":0, "query":["create table foo (id int)"], "params":[]},
{"success":true, "type":"Statement", "batch":false, "querySize":1, "batchSize":0, "query":["insert into foo (id) values (1)"], "params":[]}]
The JDBI Column Mapper allows for easy mapping of a ResultSet
into Java objects.
The jdbi
utility package in this repository provides a wrapper for remapping the column names exposed by a JDBC ResultSet
and
can thus decorate any ColumnMapper
implementation.
This is useful if the DB schema uses a different language or terminology than the Java model to which it is mapped and it is an alternative to applying this mapping via SQL query column labels.
Example:
jdbiHandle.registerRowMapper(RenamingRowMapperFactory.mapColNames(
ConstructorMapper.factory(Person.class),
new CsvColumnNameMapping(Path.of("columnNameMappings.csv"))));
List<Person> personList = jdbiHandle.createQuery("select id, nachname, geburtstag from person")
.mapTo(Person.class)
.collect(Collectors.toList());
The TablePrinter serializes a table to a pretty-printed string, either using ASCII or UTF borders.
A table consists of a header and a random number of rows. These can can be specified as an Iterable<String>
header
and Iterable<Iterable<String>>
rows. Adapters to various 3rd party frameworks are available, see below.
Example:
Iterable<String> headers = Arrays.asList("firstName", "lastName");
Iterable<Iterable<String>> rows = Arrays.asList(Arrays.asList("john", "doe"), Arrays.asList("joe", "doe"));
System.out.println(DefaultTablePrinter.print(headers, rows));
results in:
+===========+==========+
| firstName | lastName |
|===========|==========|
| john | doe |
| joe | doe |
+===========+==========+
Alternatively, you can also print to an OutputStream
.
The TablePrinter is fully configurable to customize your output:
Iterable<String> headers = Arrays.asList("firstName", "lastName");
Iterable<Iterable<String>> rows = Arrays.asList(
Arrays.asList("Tom", "Selleck"),
Arrays.asList("John", "Hillerman"),
Arrays.asList("Roger E.", null),
Arrays.asList("Larry", "Manetti"));
System.out.println(TablePrinter.builder()
.horizontalDividers(true)
.nullValue("n/a")
.tableFormat(new Utf8TableFormat())
.rowNumbers(true)
.startRow(1)
.endRow(3)
.maxCellWidth(5)
.wraparound(false)
.build().print(headers, rows));
results in:
╔═══╤═══════╤═══════╗
║ # │ first │ lastN ║
╠═══╪═══════╪═══════╣
║ 1 │ John │ Hille ║
╟───┼───────┼───────╢
║ 2 │ Roger │ n/a ║
╟───┼───────┼───────╢
║ 3 │ Larry │ Manet ║
╚═══╧═══════╧═══════╝
As per the example above, if you have a very large data structure, you may want to use the startRow
and endRow
builder methods
to only print the specified range. You can also set the maximum cell width (defaults to 100) and control the wrap-aroud of long cells (enabled by default).
Newlines are supported and tabs are rendered as 8 spaces (configurable).
Any data structure that implements the TableProvider interface can be printed and various adapters for this interface are available:
DB ResultSet
Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:mem:test", "sa", "");
String s = DefaultTablePrinter.print(
new ResultSetTableProvider(conn.createStatement().executeQuery("select * from foo"))));
Univocity CSV Parser
CsvParserSettings settings = new CsvParserSettings();
settings.setHeaderExtractionEnabled(true);
CsvParser parser = new CsvParser(settings);
String s = DefaultTablePrinter.print(
UnivocityTableProvider.of(parser.iterateRecords(new File("sample.csv"))));
Apache Commons CSV Parser
String s = DefaultTablePrinter.print(
new ApacheCsvTableProvider(CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(
new FileReader(new File("sample.csv")))))
JavaBeans
Iterable<Person> people = Arrays.asList(new Person("john", "doe", 30),
new Person("mary", "poppins", 40));
String s = DefaultTablePrinter.print(new BeanTableProvider(people))