DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Low-Code Development: Leverage low and no code to streamline your workflow so that you can focus on higher priorities.

DZone Security Research: Tell us your top security strategies in 2024, influence our research, and enter for a chance to win $!

Launch your software development career: Dive head first into the SDLC and learn how to build high-quality software and teams.

Open Source Migration Practices and Patterns: Explore key traits of migrating open-source software and its impact on software development.

Related

  • Keep Calm and Column Wise
  • Mixing SQL and NoSQL With MariaDB and MongoDB
  • Introduction to Couchbase for Oracle Developers and Experts: Part 2 - Database Objects
  • Using JSON in MariaDB

Trending

  • How To Plan a (Successful) MuleSoft VPN Migration (Part II)
  • Benchmarking Java Streams
  • GBase 8a Implementation Guide: Performance Optimization
  • 7 Linux Commands and Tips to Improve Productivity
  1. DZone
  2. Coding
  3. Frameworks
  4. Efficiently Transforming JDBC Query Results to JSON

Efficiently Transforming JDBC Query Results to JSON

Buffering query results introduces overhead and delay. Use the Pipe class provided by the Kilo framework to stream them instead!

By 
Greg Brown user avatar
Greg Brown
·
Jun. 04, 24 · Tutorial
Like (1)
Save
Tweet
Share
1.8K Views

Join the DZone community and get the full member experience.

Join For Free

A lot of enterprise data is stored in relational databases and accessed via SQL queries. Many web services are little more than HTTP-based wrappers around such queries.

For example, the following service method (built using the open-source Kilo framework) retrieves all rows from an employees table and returns the results to the caller as JSON:

Java
 
@RequestMethod("GET")
public List getEmployees() throws SQLException {
    var queryBuilder = QueryBuilder.select(Employee.class);

    try (var connection = getConnection();
        var statement = queryBuilder.prepare(connection);
        var results = queryBuilder.executeQuery(statement)) {
        return results.stream().map(result -> BeanAdapter.coerce(result, Employee.class)).toList();
    }
}


Unfortunately, this solution, while straightforward, is not terribly efficient. The entire data set must be traversed twice – once to read it from the database, and again to write it to the output stream. Additionally, the caller will not begin receiving a response until all of the rows have been read, and none of the elements will become eligible for garbage collection until all of the results have been written.

For small result sets, the latency and memory implications associated with this approach might be acceptable. However, for larger data volumes the following alternative may be preferable. The query is executed on a background thread, and the transformed results are streamed back to the caller via a pipe:

Java
 
@RequestMethod("GET")
@ResourcePath("stream")
public Iterable getEmployeesStream() {
    var pipe = new Pipe(4096, 15000);

    executorService.submit(() -> {
        var queryBuilder = QueryBuilder.select(Employee.class);

        try (var connection = getConnection();
            var statement = queryBuilder.prepare(connection);
            var results = queryBuilder.executeQuery(statement)) {
            pipe.accept(results.stream().map(result -> BeanAdapter.coerce(result, Employee.class)));
        } catch (SQLException exception) {
            throw new RuntimeException(exception);
        }
    });

    return pipe;
}


From the Kilo documentation:

The Pipe class provides a vehicle by which a producer thread can submit a sequence of elements for retrieval by a consumer thread. It implements the Iterable interface and returns values as they become available, blocking if necessary.

The pipe in this example is configured with a capacity of 4K elements and a timeout of 15s. Limiting the capacity ensures that the producer does not do more work than necessary if the consumer fails to retrieve all of the data. Similarly, specifying a timeout ensures that the consumer does not wait indefinitely if the producer stops submitting data.

This implementation of the method is slightly more verbose than the first one. However, because no intermediate buffering is required, results are available to the caller sooner, and CPU and memory load is reduced.

Note that while the examples above use Kilo’s QueryBuilder and ResultSetAdapter classes to process results, the same approach can be used with an ORM framework such as Hibernate:

Java
 
@RequestMethod("GET")
@ResourcePath("hibernate")
public List getEmployeesHibernate() throws SQLException {
    var configuration = new Configuration();

    configuration.addAnnotatedClass(HibernateEmployee.class);

    try (var connection = getConnection();
        var sessionFactory = configuration.configure().buildSessionFactory();
        var session = sessionFactory.withOptions().connection(connection).openSession()) {
        var criteriaQuery = session.getCriteriaBuilder().createQuery(Employee.class);
        var query = session.createQuery(criteriaQuery.select(criteriaQuery.from(HibernateEmployee.class)));

        return query.list();
    }
}
Java
 
@RequestMethod("GET")
@ResourcePath("hibernate-stream")
public Iterable getEmployeesHibernateStream() {
    var pipe = new Pipe(4096, 15000);

    executorService.submit(() -> {
        var configuration = new Configuration();

        configuration.addAnnotatedClass(HibernateEmployee.class);

        try (var connection = getConnection();
            var sessionFactory = configuration.configure().buildSessionFactory();
            var session = sessionFactory.withOptions().connection(connection).openSession()) {
            var criteriaQuery = session.getCriteriaBuilder().createQuery(Employee.class);
            var query = session.createQuery(criteriaQuery.select(criteriaQuery.from(HibernateEmployee.class)));

            try (var stream = query.stream()) {
                pipe.accept(stream);
            }
        } catch (SQLException exception) {
            throw new RuntimeException(exception);
        }
    });

    return pipe;
}


The performance of the Hibernate versions is comparable to the preceding examples, although they do result in slightly higher memory consumption and GC activity. Hibernate also requires approximately 18MB of external dependencies, compared to the 150KB required by Kilo.

The complete source code for this example is available here. An earlier version of this article can be found here.

JSON Relational database sql Framework

Published at DZone with permission of Greg Brown, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Keep Calm and Column Wise
  • Mixing SQL and NoSQL With MariaDB and MongoDB
  • Introduction to Couchbase for Oracle Developers and Experts: Part 2 - Database Objects
  • Using JSON in MariaDB

Partner Resources


Comments

ABOUT US

  • About DZone
  • Send feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: