Fetching Data

To start fetching data with JPAsStreamer you need to initialize an instance of JPAStreamer. This section describes how that is accomplished.

Obtaining a JPAStreamer instance

From persistence unit name

The simplest way to initialize JPAstreamer is by providing the name of the persistence unit like so:

JPAStreamer jpaStreamer = JPAStreamer.of("sakila"); (1)
1 "sakila" is to be replaced with the name of your persistence unit that can be found in a configuration-file

In the example, the String "sakila" should refer to the name of your persistence unit. Assuming you are already using a JPA provider, your project should contain an XML-file like the one below, describing the persistence unit:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
     http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">

    <persistence-unit name="sakila" transaction-type="RESOURCE_LOCAL"> (1)
        <description>MySQL Sakila Example Database</description>
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <!-- Configuring The Database Connection Details -->
            <property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/sakila" />

            <!-- ... -->

        </properties>
    </persistence-unit>
</persistence>
1 The name of the persistence unit, in this case "sakila", is used to initialize JPAStreamer.
This configuration is just an example configuration for the MySQL Sakila example database. You should use the configuration you already have in place.
If you have multiple persistence units, you can initiate several instances of JPAStreamer to establish connections with different sources.

JPAStreamer does not need any additional configuration and depends solely on this file to establish a database connection. If your starting a project from scratch, make sure to set up your JPA project before trying to use JPAStreamer.

Having obtained a JPAStreamer instance, you are ready to go. Here is an example that includes both the instantiation and the querying:

public static void main(String[] args) {

    JPAStreamer jpaStreamer = JPAStreamer.createJPAStreamerBuilder("sakila") (1)
        .build();

    long count = jpaStreamer.stream(Film.class)
        .filter(Film$.title.startsWith("A"))
        .count();

    System.out.format("There are %d films with a title that starts with A", count);
}

From EntityManagagerFactory

When configuring JPAStreamer with the persistence unit name as described above, a new EntityManagerFactory is created and managed by JPAStreamer. In this case, JPAStreamer is responsible for the life cycle of the factory, and calling JPAStreamer::close will close the EntityManagerFactory.

If you rather wish to reuse an existing EntityManagerFactory you can initialize JPAStreamer as follows:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("sakila");
JPAStreamer jpaStreamer = JPAStreamer.of(emf);
In this case, JPAStreamer is not responsible for clearing up the factory resources and calling JPAStreamer::close will not close the EntityManagerFactory. However, EntityManager instances obtained via the factory are still managed by JPAStreamer.

From Supplier<EntityManager>

As a third option, JPAStreamer can be handed a Supplier of Entity Managers. In this case, JPAStreamer is not responsible for the lifecycle of any supplied Entity Managers. For example:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("sakila");
JPAStreamer jpaStreamer = JPAStreamer.of(emf::createEntityManager);

This is especially useful in contexts where JPAStreamer may not be permitted to create and manage its own EntityManagerFactory, and/or no reference to an EntityManagerFactory is present. An example of such an environment is inside a PanacheRepository when running Hibernate and Panache. PanacheRepository inherits getEntityManager() from PanacheEntityBase, which can be used to supply JPAStreamer with Entity Managers as follows:

@ApplicationScoped
public class FilmRepository implements PanacheRepository<Film> {

    private final JPAStreamer jpaStreamer = JPAStreamer.of(this::getEntityManager);

}
When using a Supplier, JPAStreamer is not responsible for the lifecycle of the Entity Managers, thus JPAStreamer::close will not close any supplied Entity Managers.

Resetting the Streamer

Calling jpaStreamer.stream(Class<T> entityClass) creates a dedicated Streamer for the provided Entity class. The Streamer instance is reused for subsequent calls on jpaStreamer.stream() on the same Entity, see example below:

JPAStreamer jpaStreamer = JPAStreamer.createJPAStreamerBuilder("sakila")
        .build();

long count = jpaStreamer.stream(Film.class) (1)
        .filter(Film$.title.startsWith("A"))
        .count();

long count2 = jpaStreamer.stream(Film.class) (2)
        .filter(Film$.title.startsWith("A"))
        .count();
1 The first call to jpaStreamer.stream(Film.class) will create a Streamer of Film entities
2 The second call will reuse the previously configured Streamer

A Streamer instance hold a javax.persistence.EntityManager which has its own first-layer cache. Thus by default, database changes performed by another application, or made directly on the database, will not be detected. In the example above, the addition of the film "Avatar" to the database between the first and second count query therefore goes unnoticed and count will equal count2.

To ensure that database updates performed by another application are detected, you must reset the Streamer between queries. This will effectively remove the existing Streamer for the specified Entity and close its associated EntityManager. The next query will create a new Streamer with a new EntityManager, resetting the first-level cache associated with the Entity.

You can reset the Streamer for one or more Entity classes with the following command:

jpaStreamer.resetStreamer(Class<?>... entityClasses);
JPAStreamer instances configured with a Supplier<EntityManager do not manage the lifecycle of the supplied Entity Managers, and thus cannot close them. Calling resetStreamer on such instance result in an UnsupportedOperationException. See, Initializing JPAStreamer with a Supplier.

We can thus update the prior example to ensure that database changes are detected as follows:

JPAStreamer jpaStreamer = JPAStreamer.createJPAStreamerBuilder("sakila")
        .build();

long count = jpaStreamer.stream(Film.class) (1)
        .filter(Film$.title.startsWith("A"))
        .count();

jpaStreamer.resetStreamer(Film.class); (2)

long count2 = jpaStreamer.stream(Film.class) (3)
        .filter(Film$.title.startsWith("A"))
        .count();
1 Creates a Streamer of Film entities
2 Resets (removes) the Streamer of Film entities. This resets the first-level cache.
3 Creates a new Streamer of Film entities

What’s Next

The next section demonstrates how to use the available Stream operators and how they map to SQL constructs.