paint-brush
Dependencies, Entity Classes, and Configuration for Jinq to Create Database Queries in Javaby@yaf
277 reads

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

by Fedor YaremenkoMarch 22nd, 2021
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

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.

Company Mentioned

Mention Thumbnail
featured image - Dependencies, Entity Classes, and Configuration for Jinq to Create Database Queries in Java
Fedor Yaremenko HackerNoon profile picture

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.