Dependencies, Entity Classes, and Configuration for Jinq to Create Database Queries in Java

Written by yaf | Published 2021/03/22
Tech Story Tags: java | orm | database | jpa | spring-boot | springboot | db | querying

TLDR Jinq is a library that provides support for database queries in Java. It is inspired by LINQ's LINQ, inspired by.NET’s LINQ. Under the hood, Jinq uses. Lambda serialization and bytecode analysis. Let's start looking at Jinq with a Spring Boot example. Add the. dependencies in the. build.gradle file: JinqJPAStream provider, JPA entity classes,. JPA helper classes, and JPA database query examples.via the TL;DR App

Jinq is a library that provides support for database queries in Java. It is inspired by .NET’s LINQ. A developer can use Java Stream API, standard functions (e.g.
String#contains
,
Math#abs
), and relational, arithmetic and logical operators (e.g. ==, <, +, /, &&, ==) for filtering data.
List<Book> books = jinqDataProvider.streamAll(entityManager, Book.class)
	.where(book -> book.getPages() > 50 && book.isPublished()).toList();
In this example, the
Book
is not a generated metamodel but a JPA entity class. It looks like magic. Under the hood, Jinq uses lambda serialization and bytecode analysis. Let's start looking at Jinq with a Spring Boot example.

Dependencies

Add the following dependencies in the build.gradle file:
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.jinq:jinq-jpa:1.8.32'

Entity classes

Consider the example with two tables in the database:
books
and
authors
. Add the entity classes:
@Entity(name = "books")
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;

    private boolean published;

    private Integer pages;

    @ManyToOne
    @JoinColumn(name = "author_id")
    private Author author;

    // getters and setters
}
@Entity(name = "authors")
public class Author {
  
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
  
    private String firstName;
  
    private String lastName;
  
    @OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true)
    @OrderBy("id")
    private final Set<Book> books = new LinkedHashSet<>();
    
    // getters and setters
}

Configuration and Helper

We need to create a
JinqJPAStreamProvider
bean for using Jinq.
@Configuration
public class JinqProviderConfiguration {

    @Bean
    JinqJPAStreamProvider jinqDataProvider(EntityManagerFactory emf) {
        return new JinqJPAStreamProvider(emf);
    }

}
We need to use the
JinqJPAStreamProvider#streamAll
method for creating streams. Let's add a helper class.
@Component
public class JinqHelper {
  
    @PersistenceContext
    private EntityManager entityManager;
  
    private final JinqJPAStreamProvider jinqDataProvider;
  
    public JinqHelper(JinqJPAStreamProvider jinqDataProvider) {
        this.jinqDataProvider = jinqDataProvider;
    }
  
    public <T> JPAJinqStream<T> stream(Class<T> clazz) {
        return jinqDataProvider.streamAll(entityManager, clazz);
    }
  
}
Now we are ready to query the database.

Query Examples

We can use the
count
method to get the number of all authors.
long authorsCount = jinqHelper.stream(Author.class).count();
Let's retrieve all published books that have more than 50 pages.
List<Book> books = jinqHelper.stream(Book.class).where(book ->
    book.getPages() > 50 && book.isPublished()).toList();
If we want to read-only specific columns, we can use the
select
method and the
Pair
or
Tuple3
, ...,
Tuple8
classes in the
org.jinq.tuples
package.
List<Pair<String, String>> allAuthors = jinqHelper.stream(Author.class)
        .where(author -> author.getFirstName().equals("Simon"))
        .select(author -> new Pair<>(author.getFirstName(), author.getLastName()))
        .toList();

for (Pair<String, String> author : allAuthors) {
    String firstName = author.getOne();
    String lastName = author.getTwo();
    // ...
}
Jinq provides aggregation methods.
Double avgPages = jinqHelper.stream(Book.class).avg(Book::getPages);
We can use the
joinList
method to perform join-queries. We also use the
Pair
class in the following example
List<Pair<Author, Book>> joinedList = jinqHelper.stream(Author.class)
        .where(author -> author.getFirstName().equals("Simon"))
        .joinList(Author::getBooks)
        .where(pair -> pair.getTwo().isPublished())
        .toList();

Advanced Features

Jinq also provides support for grouping, sorting, different types of joins, subqueries, registering your own functions, and other useful features.

Conclusion

Jinq provides a convenient and natural way to build type-safe database queries. It is easier to use than JPA 1 Criteria API and does not require code generation as Query DSL or JPA 2 metamodel.

Written by yaf | 10+ years Java developer
Published by HackerNoon on 2021/03/22