Easy database queries for Java

Using Jinq for JPA with Spring

Use Case
Jinq with Spring JPA
   2.1  Add Jinq to Your Build
   2.2  Quick Hack
   2.3  Initializing Jinq as a Singleton Bean
   2.4  Using the Jinq Singleton Component with Spring JPA
Jinq with Spring Data JPA
   3.1  Custom Queries
   3.2  Using Jinq for Custom Queries

Note: The example code used on this page use the older JEE javax.persistence namespace and Jinq 1.8.x. Projects that use the newer Jakarta EE jakarta.persistence namespace and Jinq 2.0.x will need to modify the example code accordingly.

1  Use Case

Jinq provides query functionality that sits on top of JPA ORMs. Spring provides some frameworks that can be used for abstracting away details of how to access data in databases. Spring is commonly deployed on top of JPA ORMs. Jinq can be used in combination with Spring to make it easier to build JPA queries. Jinq can be particularly useful when used for dynamic queries in Spring. Dynamic queries cannot be auto-generated or error-checked by Spring. Jinq allows you to easily write dynamic queries in your Spring applications.

2  Jinq with Spring JPA

A common way for Spring is used with JPA is to have all access to database entities go through Direct Access Objects (DAO). In Spring, the DAO pattern is often referred to as Repository objects. Since these Repository objects contain normal JPA queries, it is fairly easy to convert the code to use Jinq.

For example, suppose you have defined a JPA entity for City objects in a database. You would then create a CityRepository DAO interface for finding City objects in the database. The CityRepositoryImpl would be an implementation for that interface that uses JPA to actually issue queries to the database and return City objects to the program. The JPA EntityManager is automatically injected into the Repository bean, and your program can use that to issue queries.

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;

@Repository
public class CityRepositoryImpl implements CityRepository {

  @PersistenceContext
  private EntityManager em;
  
  public Collection<City> findByName(String name) {
    Query q = em.createQuery(
      "SELECT C FROM City C WHERE C.name = :name");
    q.setParameter("name", name);
    return (Collection<City>)q.getResultList();
  }
}

2.1  Add Jinq to Your Build

The first step to using Jinq is to add the Jinq libraries to your build. If you are using Maven, you can simply add the jinq-jpa Maven dependency to your build files.

2.2  Quick Hack

If you have a JPA EntityManager in your code, it is fairly easy to use that to initialize Jinq. From there you can easily start issuing queries.

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;

@Repository
public class CityRepositoryImpl implements CityRepository {

  @PersistenceContext
  private EntityManager em;
  
  public Collection<City> findByName(String name) {
    // Initialize Jinq
    JinqJPAStreamProvider streams =
      new JinqJPAStreamProvider(em.getMetamodel());
  
    // Issue a query
    return streams.streamAll(em, City.class)
      .where(c -> c.getName().equals(name))
      .toList();
  }
}

Unfortunately, this causes Jinq to be reinitialized every time a query is executed and is inefficient. It is better to initialize Jinq only once as a singleton object that can be shared by any code issuing queries.

2.3  Initializing Jinq as a Singleton Bean

Jinq only needs to be initialized once and then shared among all the Repository objects issuing queries to the database. In Spring, the way to do this is to create a singleton component bean that Spring will initialize and inject into your Repository code, where it can be used for issuing queries.

First, you should create a component for Jinq. This component will be automatically created by Spring. It needs Spring to inject an EntityManagerFactory which describes the JPA classes used by the application. The component will then use the EntityManagerFactory to initialize Jinq.

package com.example;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;

import org.jinq.jpa.JinqJPAStreamProvider;
import org.jinq.jpa.JPAJinqStream;
import org.springframework.stereotype.Component;

@Component
public class JinqSource {
  private JinqJPAStreamProvider streams;
  
  @PersistenceUnit
  public void setEntityManagerFactory(
      EntityManagerFactory emf) throws Exception {
    streams = new JinqJPAStreamProvider(emf);
    // Do any additional Jinq initialization needed here.
  }

  // Wrapper that passes through Jinq requests to Jinq
  public <U> JPAJinqStream<U> streamAll(
      EntityManager em, Class<U>entity) {
    return streams.streamAll(em, entity);
  }
  
  // You can include helper methods here too
  public JPAJinqStream<City> cities(EntityManager em) {
    return streams.streamAll(em, City.class);
  }
}

Then, in your Spring configuration, you have to tell Spring about this new component so that it knows how to initialize it. By default, Spring components are singletons.

<bean id="jinq" class="com.example.JinqSource"/>

2.4  Using the Jinq Singleton Component with Spring JPA

Once you have a singleton JinqSource component, you can alter your repository code to use it. You need to use the @Autowired annotation to tell Spring to automatically inject the singleton JinqSource into your code. Then you can use it for queries.

import com.example.JinqSource;
import org.springframework.beans.factory.annotation.Autowired;

@Repository
public class CityRepositoryImpl implements CityRepository {

  @PersistenceContext
  private EntityManager em;

  @Autowired
  private JinqSource source;
  
  public Collection<City> findByName(String name) {
    return source.cities(em)
      .where(c -> c.getName().equals(name))
      .toList();
  }
}

3  Jinq with Spring Data JPA

Another way that Spring is used with JPA is with the Spring Data JPA framework. The Repository pattern involves writing a lot of boilerplate code for getting an EntityManager and using it to create queries. Spring Data JPA can autogenerate a lot of this boilerplate code for.

In Spring Data JPA projects, many of your queries are automatically generated and error-checked for you. The main benefit of using Jinq is for dynamic queries.

In a standard Spring Data JPA project, you define interfaces for Direct Access Objects (DAOs) or Repository objects. These interfaces are used to abstract away accesses to databases. The interfaces should extend one of the Spring Data repository classes. The Spring framework then automatically generates an implementation for the interfaces. It creates queries based on the names of methods and classes.

For example, suppose you have defined a JPA entity for City objects in a database. You would then create a CityRepository DAO interface for finding City objects in the database. The interface would extend Spring's Repository interface. Below is a diagram of the classes involved in setting up the repository.

The code for the CityRepository interface is shown below.

package com.example.repository;

import org.springframework.data.repository.Repository;

public interface CityRepository 
    extends Repository<City, Integer> {
	
  Collection<City> findAll() throws DataAccessException;
}

3.1  Custom Queries

Spring Data JPA is not able to automatically generate all of your queries. You still need to hand-code more complex queries or dynamic queries. Since Spring Data automatically generates the implementations of your repository objects, you need to jump through some hoops to get Spring Data to integrate your custom query code into your repository objects.

First you need to define an interface that includes the custom queries that you want to add to your repository. This interface can have any name.

public interface AdditionalQueries {
  // Filters cities based on optional population and
  // country restrictions
  Collection<City> filterCities(
    int population, String country);
}

You can then change your repository interface to include this interface of additional queries. This way, your repository objects will support these custom queries.

package com.example.repository;

import org.springframework.data.repository.Repository;

public interface CityRepository 
    extends Repository<City, Integer>,
            AdditionalQueries {
	
  Collection<City> findAll() throws DataAccessException;
}

Then you have to supply an implementation of these custom queries that Spring Data will include in its auto-generated implementations of repositories. For Spring Data to know how to integrate this code into its auto-generated repository implementations, the code must appear in a class following these restrictions:

Below is a class with a stub implementation of the additional custom queries that were added to the CityRepository.

package com.example.repository.jinq;

// Implements custom queries using Jinq
public class CityRepositoryJinqImpl 
    implements AdditionalQueries {

  public Collection<City> filterCities(
      int population, String country) {
    // Empty stub implementation
    return null;
  }
}

Then, you need to tell Spring about which classes to use as implementations for your custom queries. You simply change your Spring configuration for your repositories to specify which post-fix to use when looking for classes that implement custom queries. In the example, the postfix JinqImpl is used because Jinq will be used to implement these custom queries.

<repositories 
    base-package="com.example.repository" 
    repository-impl-postfix="JinqImpl"/>

Below is a diagram showing the different classes involved in adding custom queries to a Spring Data repository:

3.2  Using Jinq for Custom Queries

Once you have added custom queries to your repository objects, you can use Jinq to implement these custom queries. The first step to using Jinq is to add the Jinq libraries to your build. If you are using Maven, you can simply add the jinq-jpa Maven dependency to your build files. Then, you should create a Jinq singleton component that Spring can inject into your code and that you can use for queries.

Using this Jinq component, you can simply modify your custom query implementation in CityRepositoryJinqImpl to use Jinq for its queries. In the example, the query is a dynamic query that creates different database queries depending on the filter parameters.

package com.example.repository.jinq;

// Implements custom queries using Jinq
public class CityRepositoryJinqImpl 
    implements AdditionalQueries {

  @PersistenceContext
  private EntityManager em;
  
  @Autowired
  private JinqSource source;
  
  public Collection<City> filterCities(
      int population, String country) {
    
    JinqStream<City> cities = source.cities(em);
	
    if (population > 0)
      cities = cities.where(c ->
          c.getPopulation() > population);
		  
    if (country != null)
      cities = cities.where(c ->
          c.getCountry().equals(country))
		  
    return cities.toList();
  }
}