Jinq supports for JPA-compliant object-relational mappers such as EclipseLink and Hibernate. If you have an existing project using JPA, you can easily add Jinq to your project and use Jinq for some of your queries. Jinq will issue queries using JPA's JPQL, so you can mix JPA queries and Jinq queries without any issues.
To use Jinq with your JPA code, you should either
Jinq queries are written by taking streams of database data and filtering and transforming that data. To use Jinq when using JPA for database access, you need a way to get a Jinq stream of JPA data. You can do this with the org.jinq.jpa.JinqJPAStreamProvider class.
The JinqJPAStreamProvider's constructor normally takes a JPA EntityManagerFactory as a parameter. The JinqJPAStreamProvider is intended to be a singleton in your application that only needs to be created once.
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("JPA"); JinqJPAStreamProvider streams = new JinqJPAStreamProvider(entityManagerFactory);
In some cases, it is easier to get a reference to an EntityManager instead of an EntityManagerFactory. You can also create JinqJPAStreamProvider objects from an EntityManager whenever you want to perform a Jinq query although this is less efficient.
JinqJPAStreamProvider streams = new JinqJPAStreamProvider(entityManager.getMetamodel());
Once you've created a JinqJPAStreamProvider, you can then use the streamAll() method to get a Jinq stream of JPA entities from a database. For example, if you have a Customer JPA entity and a JPA EntityManager called em, then you can get a stream of Customer objects from your database using the code below:
JinqStream<Customer> customers = streams.streamAll(em, Customer.class);
Once you have this stream of entities, you can filter and transform it using the Jinq approach:
List<Customer> customers = streams .streamAll(em, Customer.class) .where( c -> c.getName().equals("Bob") ) .toList();
More information about Jinq queries are available in the query guide.
Jinq currently has basic support for the basic JPA data types. The table below shows the main operations supported for each data type.
Jinq generally does not generally support casts between different data types. This is due to a limitation of JPQL, which does not support casting data between types in queries. As such, Jinq cannot generate JPQL queries with behavior equivalent to Java code that contains casts.
Hibernate 5.2 contains preliminary support for Java 8's java.time.* APIs. When Jinq is used with Hibernate 5.2, it supports the operations listed below using these data types. When Jinq is used with its default configuration in which "isAllEqualsSafe" is enabled, then Jinq also supports the equals() method on these data types as well.
Java type | Supported operations |
---|---|
Instant | isBefore(), isAfter() |
LocalDate | isBefore(), isAfter(), isEqual() |
LocalTime | isBefore(), isAfter() |
LocalDateTime | isBefore(), isAfter(), isEqual() |
OffsetTime | isBefore(), isAfter(), isEqual() |
OffsetDateTime | isBefore(), isAfter(), isEqual() |
ZonedDateTime | isBefore(), isAfter(), isEqual() |
The JPA version of Jinq can be configured by passing configuration information in the form of "hints" to the Jinq query system. These hints can be applied to all Jinq queries or to single queries only.
To apply a hint to all queries, use the setHint() method on the JinqJPAStreamProvider which you use to create streams of entities.
streams.setHint("exceptionOnTranslationFail", true);
To apply a hint to only a single query, use the setHint() method on an individual JinqStream streams.
streams.streamAll(em, Customer.class) .setHint("automaticPageSize", 10000) .where( c -> c.getName().equals("Bob") )
The setHint() method takes two parameters. The first parameter is a string with the name of the hint to set. The second parameter is the value for the hint.
The exceptionOnTranslationFail hint is used to tell Jinq what to do when it encounters Java code that it cannot translate into a database query. Normally, if Jinq cannot translate some code into a database query that can run on the database, Jinq will emulate the effect of running a query by directly run the Java code instead. The database data will be transferred to the computer and the Java code will be applied directly to the data. Jinq will will produce the correct result, but a large amount of data may need to be transferred from the database. If you would prefer it if Jinq told you when a query cannot be generated from your code, you can set this hint to true. Jinq will then throw an IllegalArgumentException when it cannot create a query from your code.
The automaticPageSize hint is used to change how Jinq streams its results. In order to support large datasets that do not fit entirely into memory, Jinq will automatically try to stream result sets from the database instead of holding the entire result set in memory. JPA does not support result streaming, so for large result sets, Jinq will issue multiple smaller JPA queries for different subsets of the result set. By default, Jinq will break query results into chunks or pages of 10000 results. When Jinq issues a query, it will ask for the first 10000 results. If it finds more than 10000 results, then once the first 10000 results have been streamed, it will then ask for the next 10000, and so on until the full result set has been transferred. Although this prevents your Java code from hanging due to running out of memory, some databases may return inconsistent results when a single large query is broken into smaller queries in this way. Also, if your results are already quite large because they contain BLOBs or other large data, 10000 results might already exceed the memory you have available. The automaticPageSize hint allows you to set the number of results that Jinq should should break result sets into.
The queryLogger hint lets you install a callback in Jinq for inspecting the queries that Jinq generates. The callback should be a JPAQueryLogger object. Every time Jinq is about to run a JPQL query, it will pass the query string to the callback.
The lambdaClassLoader hint allows you to tell Jinq to use a certain ClassLoader instance to find the bytecode for the Java lambda functions that it is translating into a database query. Jinq first looks up the bytecode for lambda functions by using the system class loader. If it cannot find the lambda function there, it will use the class loader used to load Jinq. If it still cannot find the lambda function there, it will look up the lambda code using the lambdaClassLoader specified in the hint. Some systems use different classloaders for loading different parts of their codebases to help with security and modularity. For those systems, it is necessary to use this hint to tell Jinq which classloader to use to find the code used by your Jinq queries so that Jinq can translate the code into database queries.
The useCaching hint tells Jinq to cache its analysis of lambdas. This makes Jinq faster because if it encounters same the same query code repeatedly, it doesn't have to repeatedly analyze the same code over and over again. By default, caching is on.
The javax.persistence.fetchgraph hint allows you to pass JPA entity graphs to the underlying JPA query layer in order to optimize your entity fetching. In Jinq 2.0.x, this hint is an alias for the jakarta.persistence.fetchgraph hint.
The jakarta.persistence.fetchgraph hint is only available in Jinq 2.0.x. It allows you to pass JPA entity graphs to the underlying JPA query layer in order to optimize your entity fetching.
There are some methods that Jinq doesn't definitively know whether they can be safely converted into queries or not. Usually, these methods are safe, but Jinq can't be sure. By default, Jinq assumes that these methods are safe, but these assumptions can be disabled. The isObjectEqualsSafe hint tells Jinq to assume that Object.equals(), Objects.equals(), and Guava's Objects.equal() are safe. The isAllEqualsSafe hint tells Jinq to assume that all calls to equals() are safe. The isCollectionContainsSafe hint tells Jinq to assume that all calls to contains() on subclasses of Collection are safe.
Features marked with the Java-only symbol are only supported by the Java version of Jinq for JPA. If you require support for these features in the Scala version of Jinq, please post to the forum to let us know.