Thursday, October 1, 2020

How to get Type-Safe and Intuitive Hibernate/JPA Queries by Leveraging Java Streams

A large proportion of Java database applications are using Hibernate/JPA to bridge the gap between Java and SQL. Until recently, we were forced to mix Java and JPQL or to use complex imperative criteria builders to create database queries. Both of these methods are inherently neither type-safe nor very intuitive.

The newly launched open-source library JPAstreamer addresses these issues by allowing you to express Hibernate/JPA queries using Java Streams. This means we can avoid any impedance mismatches between JPQL/HQL and Java and get full type-safety. In this article, I will show you how to put Java Stream queries to work in your application using JPAstreamer.

JPAstreamer in a nutshell

As mentioned, JPAstreamer allows JPA queries to be expressed as standard Java Streams using short and concise, type-safe declarative constructs. This makes our code shorter, less complex, and easier to read and maintain. Best of all, we can stick to using only Java code without needing to mix it with SQL/JPQL or other language constructs/DSL. 

In short, we can query a database like this:


This prints the title of the 15 longest films in the database. 

OSS License 

JPAstreamer is using the same license as Hibernate (LGPL). This makes it easy to use in existing Hibernate projects. JPAstreamer also works with other JPA providers such as EclipseLink, OpenJPA, TopLink etc.


Installing JPAstreamer entails just adding a single dependency in your Maven/Gradle configuration file as described here. For example, Maven users add the following dependency:


Let’s have a look at how JPAstreamer fits in an existing application.

Example Database and JPA Entities

In the examples below, we are using JPAstreamer to query the “Sakila” example database that is available for download directly from Oracle or as a Docker instance.

This is how you install and run the example database using Docker:

$ docker pull restsql/mysql-sakila
$ docker run -d --publish 3306:3306 --name mysqld restsql/mysql-sakila

We will also be relying on JPA entities like the Film-class partly shown here:

@Table(name = "film", schema = "sakila")
public class Film implements Serializable {


@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "film_id", nullable = false, updatable = false, columnDefinition = "smallint(5)")
private Integer filmId;

@Column(name = "title", nullable = false, columnDefinition = "varchar(255)")
private String title;

@Column(name = "description", nullable = false, columnDefinition = "text")
private String description;

@ManyToMany(cascade = CascadeType.ALL)
name = "film_actor",
joinColumns = @JoinColumn(name = "film_id") ,
inverseJoinColumns = @JoinColumn(name = "actor_id")
private List<Artist> actors;

The complete code in this article is open-sourced and available here

JPAstreamer - Printing the Longest Films

Here is a complete example of how we can use JPAstreamer to create a query that prints out the length and title of the 15 longest films in the database:

public class LongestFilms  {
public static void main(String[] args) {
final JPAStreamer jpaStreamer = JPAStreamer.of("sakila");
.map(f -> String.format("%3d %s", f.getLength(), f.getTitle()))


This will print:


As can be seen, queries are simple, concise, completely type-safe and follow the Java Stream standard API. No need to learn new stuff.

The code above will create the following SQL (shortened for brevity):

film0_.film_id as film_id1_1_,
film0_.length as length4_1_,
film0_.title as title10_1_,
/* more columns */
film film0_
order by
film0_.length desc limit ?

This means that most of the Java stream is actually executed on the database side. It is only the map() and forEach() operations (which cannot easily be translated to SQL) that are executed in the JVM. This is really cool!

Pre-Joining Columns

To avoid the “SELECT N + 1” problem, it is possible to configure streams to join in columns eagerly by providing a configuration object like this:

StreamConfiguration configuration = StreamConfiguration.of(Film.class)

.filter(Film$.rating.in("G", "PG"))

This will create a Hibernate join under the hood and will only render a single SQL query where all the Film fields “List<Artist> artists” and “Language language” will be populated on the fly:

Film as Film
left join
fetch Film.actors as generatedAlias0
left join
fetch Film.language as GeneratedAlias1
Film.rating in (
:param0, :param1


In this article, I have shown how you can avoid impedance mismatches between JPQL/HQL in Hibernate/JPA using the open-source library JPAstreamer. The Stream API allows you to compose type-safe and expressive database queries in standard Java without compromising the application performance. 


The background to JPAStreamer is that we have developed the stream-based ORM-tool Speedment, and we have come across many developers that want to use Java streams but are constrained to use Hibernate in their applications. Therefore, we have now developed JPAstreamer, a JPA/Hibernate extension that handles Java Stream queries without the need to change the existing codebase. 

Take JPAStreamer for a spin and let me know what you like/dislike by dropping a message on Gitter!


Thursday, September 24, 2020

Extend Hibernate to Handle Java Stream Queries

The Java Stream API released in Java 8 has proven itself to be an efficient, terse yet intuitive way of expressing application logic. The newly launched open-source project JPAstreamer allows you to express Hibernate or other JPA database queries using Java Streams. In this article, we will show you how to extend the API of your existing database applications to handle Stream queries in an instant. 

To quickly give you an idea of what JPAstreamer accomplish, we’ll start by showing you an example of a Stream operating on a database table that contains arbitrary users (with attributes including a first and last name): 


This will print ten users with a first name that starts with the letter A sorted in reversed order based on their last names. Omitting the details (that are covered shortly), this demonstrates how the desired result set is easily described as a pipeline of Stream operators. 

On the surface, it may look as if the presented Stream would require every row in the User-table to be materialized in the JVM. Although, the Stream is actually optimized and rendered to JPA queries. Thus, the Stream queries are as performant as alternative approaches i.e. JPQL or Criteria Builder, but with the difference that JPAstreamer constitutes a streamlined and type-safe approach to expressing queries.

How JPAstreamer Works

JPAstreamer plugs into your application with the addition of a single dependency in your Maven/Gradle build. The specific dependency is described here

Like the well-known Java library Lombok, JPAstreamer uses an annotation processor to form a meta-model at compile time. It inspects any classes marked with the standard JPA annotation @Entity and for every entity Foo.class, a corresponding Foo$.class is generated. The generated classes represent entity attributes as Fields that are used to form predicates on the form User$.firstName.startsWith(”A”) that can be interpreted by JPAstreamer’s query optimizer.  

It is important to note that JPAstreamer does not alter or disturb the existing codebase, but simply extends the API to handle Java Stream queries from this point forward. Further, the meta-model is placed in “generated sources” located in the “target” folder and doesn’t need to be checked in with the source code nor tested.

Let’s Get Streaming 

We’ll now walk you through the easy process of setting up JPAstreamer in your database application. To follow along, your application must use Java 8 (or later) and Hibernate or another JPA provider that is responsible for object persistence (if you wish to use the Stream API without JPA, you are better off using the open-source Stream ORM Speedment). 

As mentioned, installation simply entails adding a dependency (described here) to your Maven/Gradle build and rebuilding the application to generate the JPAstreamer meta-model. 

Once you’ve completed the simple set-up, you need to obtain an instance of JPAStreamer like so: 

JPAStreamer jpaStreamer = JPAStreamer.of("db-name"); 

You should replace the String ”db-name” with the name of the persistence unit you wish to query. Look it up in your JPA configuration-file (often named persistence.xml) under the tag <persistence-unit>.

The JPAstreamer instance provides access to the method .stream() that accepts the name of the Entity you wish to Stream. To query the user table, you would simply type: 


This returns a stream of all the user rows of type Stream<User>. With a Stream source at hand, you are free to add any Java Stream operations to form a pipeline through which the data will flow (data flowing is a conceptual image rather than an actual description of how the code executes). For example: 

List<String> users = jpaStreamer.stream(User.class)
.map(u -> u.getFirstName() + ” ” + u.getLastName())

This Stream collects the name of users who reached the age of 20 in a List. User$ refers to the generated entity that is part of JPAstreamer’s meta-model. This entity is used to form predicates and comparators for operations such as .filter() and .sort() which are quickly composed leveraging code completion in modern IDEs. 

Here is another example that counts all the users who are from Germany and are named “Otto” using a combined predicate: 

long count = jpaStreamer.stream(User.class)


In this article, we have shown how you can integrate the open-source library JPAstreamer with Hibernate (or any JPA provider) to compose type-safe and expressive database queries as standard Java Streams. 



Per Minborg
Julia Gustafsson

Friday, April 24, 2020

Java/Cloud: How to Quickly Create a Kubernetes-ready REST Microservice

Java/Cloud: How to Quickly Create a Kubernetes-ready REST Microservice 

It is safe to say that the Microservice + Cloud combination is all the rage these days. Microservices are being developed more than ever, in turn resulting in an increase in the number of application deployments. During the past decade, containerization and orchestration tools such as Docker and Kubernetes were developed, making the microservice pattern really easy to adopt.

This article will teach you how to generate a completely functional microservice with an exposed REST API capable of interacting with a MySQL database and deploy it to your local Kubernetes cluster. The learnings here can be applied to almost any database type like Oracle, SQL Server, DB2, and so on.

If you ever get stuck during the article, feel free to refer to the final version of the source code, which can be found in this GitHub repository.

Speed(ment) is Key 

As developers, one of the things we strive for in our everyday work is shorter development time.

At this point, we can already identify two aspects of our microservice that will make our development time longer than needed:

  1. We need to create a persistence layer 
  2. We need to expose a REST API

What if I were to tell you that a tool exists which can handle these things, without you having to write a single line of code?

Speedment is a Java ORM Toolkit and Runtime designed to allow developers to create super fast applications super fast. Speedment uses the native Java Streams API for database interaction, making it extremely easy to use for newcomers, seasoned veterans and anyone that falls in between. Speedment comes with a graphical tool, giving developers the ability to generate a Java representation of their database within seconds.

Furthermore, Speedment’s bundle system allows developers to easily expand the base functionality of the base Toolkit. One such enhancement is the Spring plugin which enables developers to generate a completely functional CRUD REST API in order to interact with their database.

In the remainder of the article, you will learn how to use the Speedment Tool to generate a working REST microservice and deploy it to a Kubernetes cluster. If you’re interested in Speedment as a whole, detailed documentation with examples can be found in the online manual.

Getting Started

Being distributed via Maven, Speedment is installation free but requires Java 8 or later. To get started, head over to the Speedment Initializer where you’ll be able to download a project template with all of the dependencies needed to create your microservice. These are the settings we will be using in the example:

If your setup is different, e.g. different database, make sure you apply the appropriate changes in the initializer.

Once you’re done configuring the project, click the Download button and unpack the downloaded zip-file. To launch the Speedment Tool, execute the following command from a terminal:

mvn speedment:tool

If this is your first time running the Speedment Tool, you’ll be asked to connect to your database. We’re running the vanilla MySQL Sakila sample database on our local machine, so the auth information would look like this:

Once you’ve filled in the required fields, click the Connect button. If the auth information you’ve provided was correct, you will be presented with the following screen:

Generating the Microservice

When you’ve connected to the database via the Speedment Tool, you can start configuring the various options that are available. There are a lot of options you can play around with, but for the purposes of this article, we will be focusing on the options that are needed to expose a REST API.

To enable REST API generation, click on the Project node in the tree view, and check the Enable REST option:

We’ve also enabled the Generate REST documentation option to automatically generate the OpenAPI documentation for our REST API. This option is not mandatory, but it will allow us to test our API more easily in the end.

The next step is optional, but it will make our REST routes a bit more aesthetically pleasing. Head over to the database schema node in the tree view and set the value of REST Endpoint to a front slash (/). By default, the schema name is included in the generated REST routes and this modification removes it.

Next, we’re going to enable the generation of REST controllers for the following tables:
  • Actor
  • Category
  • Customer
  • Film
  • Staff
  • Store

The steps to enable the controller generation are identical regardless of the table. For that reason we will only demonstrate them on the Actor table.

 Click the Actor table in the tree view and enable the Generate @RestController option. This, in turn, will enable several REST related options for that table. The options we are interested in, which you should enable, are:
  • REST Enable LIST
  • REST Enable GET
  • REST Enable CREATE
  • REST Enable UPDATE
  • REST Enable DELETE

We will also rename the REST Endpoint from /actor to /actors (again only for aesthetic purposes). By default, the REST Endpoint is named the same as the table it is associated with. In our case, the renaming makes sense, because when we visit the /actors endpoint, a list of actors will be retrieved, rather than a single actor.

Go ahead and repeat these steps for the other tables listed above. After you’re done, click the Generate button. This will generate a Java representation of your database along with the necessary REST configurations and controllers.

Running the Microservice

If we were to run our application right now as it is, it will most likely crash. This is because we haven’t specified the password our application should use to connect to the database.

When we generated our application, a bunch of Speedment-specific application properties were exposed. One such property is the spring.speedment.password property, which we can use to set the password Speedment will use to connect to our database.

There are a couple of ways to specify application properties. We’re going to be defining them in the application.properties file, which you should create in your application’s resources folder.

This is what our application.properties file looks like:

 # Application properties file - START

# Application properties file - END

The default password for the Sakila database is sakila, but if your database has a different password, make sure those changes are reflected in the application.properties file.

Once we have everything configured, we can run our application. This is done by executing the following command from the project’s root folder:

mvn spring-boot:run

If you’ve enabled the Generate REST documentation option, you can visit http://localhost:8080/swagger-ui.html to access the REST API documentation:

You can execute your requests manually or directly from Swagger UI. If we were to visit http://localhost:8080/actors in our browser, we should get a JSON response with a list of actors stored in our database:
      "actorId": 1,
      "firstName": "PENELOPE",
      "lastName": "GUINESS"
      "actorId": 2,
      "firstName": "NICK",
      "lastName": "WAHLBERG"
      "actorId": 3,
      "firstName": "ED",
      "lastName": "CHASE"
... TRUNCATED ... 

Before Deployment

Before we start with the deployment process of our microservice, make sure you have the following dependencies installed on your local machine: Docker, kubectl, Minikube and Skaffold.

Dockerizing our Microservice

Before we can actually deploy our microservice to a Kubernetes cluster, we need to convert it into a format that Kubernetes can actually work with. Kubernetes is a container orchestration tool, so this is where Docker comes in to aid us with the container creation process.

In the root of your project, create a Dockerfile with the following contents:

FROM openjdk:11-slim-buster


ARG JAR_NAME=speedment-spring-app


ENTRYPOINT ["java", "-jar", "app.jar", "--spring.speedment.host=sakila"]

The exported arguments (JAR_LOCATION, JAR_NAME, JAR_VERSION) may be different for your project, depending on the information you provided in the pom.xml file. From the root of your project, execute the following command:

mvn install

This will create a target folder with a JAR file containing your microservice. Make sure the name and the version of the file match with the information you put in the Dockerfile.

Creating the Deployment Configurations

We will be deploying two images to our Kubernetes cluster: the Sakila database and our microservice. The Sakila database already has a Docker image publicly available: restsql/mysql-sakila. However, we need to build an image for our microservice. This is where the Dockerfile we created earlier will come in handy. Later on, we will be using a tool called Skaffold to create an image for our microservice and use it in the deployment process.

Start by creating a folder called k8s in the root of your project. This is where you’ll store all of your Kubernetes Deployment and Service configurations. We will keep our microservice and database configurations separate, so create two folders called storage and app in the k8s folder.

We now proceed with the configurations for the Sakila database. In the storage folder, we will create two YAML files - sakila-deployment.yml and sakila-service.yml. The sakila-deployment.yml file will store our deployment configuration for the Sakila database. As this is not a Kubernetes tutorial we will only be providing the final configurations. This is what the sakila-deployment.yml file should look like in the end:

apiVersion: apps/v1
kind: Deployment
  name: sakila
    storage: sakila
  replicas: 1
      storage: sakila
        storage: sakila
      - name: sakila
        image: restsql/mysql-sakila
        - containerPort: 3306

And this is the final sakila-service.yml file:

apiVersion: v1
kind: Service
  name: sakila
    storage: sakila
    storage: sakila
  - name: database
    port: 3306
    targetPort: 3306

The deployment and service configurations for our microservice are nearly identical. In the app folder, create a YAML file called speedment-spring-app-deployment.yml with the following contents:

apiVersion: apps/v1
kind: Deployment
  name: speedment-spring-app
    app: speedment-spring-app
  replicas: 1
      app: speedment-spring-app
        app: speedment-spring-app
      - name: speedment-spring-app
        image: speedment-spring-app-example
        - containerPort: 8080

In the same folder create another YAML file called speedment-spring-app-service.yml with the following contents:

apiVersion: v1
kind: Service

  name: speedment-spring-app
    app: speedment-spring-app
  - name: http
    port: 8080
    targetPort: 8080
  type: NodePort

These are all of the configurations we need in order to deploy our database and application. After adding the deployment configurations, our file structure should look like this:

Starting the Cluster

We are nearly done with the preparations for the deployment. There is one last thing we need to do - start our local Kubernetes cluster. This is done by executing the following command:

minikube start

The Deployment

In order to deploy our database and application we will be using Skaffold. In the root of your project create a file called skaffold.yml with the following contents:

apiVersion: skaffold/v2alpha3
kind: Config
  - image: speedment-spring-app-example
      dockerfile: Dockerfile
    - k8s/app/*
    - k8s/storage/*

With this file completed, we are finally ready for deployment. From the project root, execute the following command:

skaffold dev --port-forward=true

When we execute this command, two things will happen:

  1. A Docker image will be created from the Dockerfile we created earlier
  2. Deployments and services will be created from the configurations we created earlier

Once your microservice starts up, you can use it the same way you used it earlier. The only difference now is that it is running from a Kubernetes cluster.

Note: It takes around 30-60 seconds for the Sakila database to boot up completely. Since our application starts a lot quicker than the Sakila database, it will most likely crash and restart a couple of times before the database is ready.


Creating applications in a time-efficient manner can sometimes be hard. We have explained how to generate a microservice from a database and deploy it to a Kubernetes cluster, so hopefully, you’ve learned something that will reduce your development time.

We hope you’ve enjoyed reading this article as much as we enjoyed writing it. The final version of the source code from this article can be found here.


Per Minborg
Mislav Miličević


The Speedment Initializer capable of generating project templates
Speedment OpenSource on GitHub
Speedment Online Manual
Github Repository with the final version of the source code

Wednesday, March 4, 2020

Java/Spring: How to Generate an Entire Swagger Documented CRUD REST API With Speedment

As developers, one of the most cumbersome tasks we often face in our day-to-day lives is writing good and understandable documentation. It doesn’t matter if our documentation is only a few lines long explaining the core functionality of a feature or if it’s a full-blown essay demonstrating the ins and outs of a system. What matters is that the message we’re trying to convey through our documentation is precise and understandable.

In our previous article, we covered the topic of automatic REST API generation. More precisely, we demonstrated how to generate an entire CRUD REST API for your database using Speedment’s revamped Spring Integration plugin.

Today, we’ll be taking this knowledge a step further and demonstrate how to generate interactive documentation for your REST API in a single click.

If you didn’t get a chance to use the Speedment Spring plugin, we highly suggest you read our previous article as it contains the information necessary to follow this guide.

Do You Like Java Streams?

If the answer to this question is either ‘Yes!’, ‘Absolutely!’ or perhaps ‘Heck yeah!’, then Speedment is the right tool for you. Speedment is a Java ORM toolkit and runtime which uses pure Java Streams as an interface between your application and the database.

Alongside the already familiar Streams API, Speedment provides end-users with a graphical tool in order to generate a Java representation of your database in a matter of seconds, allowing them to completely stay in a Java-only environment.

If you’re interested in learning more about Speedment, head over to the documentation page where you’ll find a bunch of guides and examples. For the remainder of this article, we’ll be focusing on the new update to Speedment’s Spring plugin.

Before we Begin

In order to generate the REST API documentation, Speedment uses a combination of the OpenAPI specification and Swagger UI.

The preparation steps will differ depending on if you’re starting from scratch or not, but the end result will be the same regardless of your starting point.

If you have followed the guide in our previous article, where we explain how to generate a REST API using Speedment, you’ll only need to add a couple of dependencies to your project’s pom.xml file:


On the other hand, if you’re starting from scratch, head over to the Initializer where you’ll be able to generate a Speedment project with Spring support. Once you reach the Initializer, you’ll be presented with plenty of options to configure your project. One configuration option that is particularly important is the Plugins section of the Initializer.

To enable Spring support in your new Speedment project, tick the checkbox next to the "Spring" option. Once you’re happy with your project configuration, go ahead and click the Download button at the bottom of the Initializer.

When you’re ready, you can launch the Speedment Tool by executing the following command from the root folder of your project template:

mvn speedment:tool

If you’ve installed the plugin correctly, you’ll see some Spring Boot specific options which can be used to configure your REST API and documentation.

If this is your first time using Speedment, you may want to familiarize yourself with the workflow by following the “Hello Speedment” quick start guide.

Swagger Automata

For the following example, we’ll be using Sakila, a MySQL Sample Database. You can download it as a standalone instance or as a Docker container.

When you open the Speedment Tool and successfully connect to your database, you will be presented with a user interface containing the metadata information about your database and some options that you can configure:

If you click the “Generate” button found in the top banner, a Java representation of your database will get generated. To generate the documentation for your REST API, you must enable the “Generate REST documentation” option found in the project view (which is accessed by selecting the top node in the tree).

Once enabled, additional configuration options will become available allowing you to further customize your generated documentation:

The next time you regenerate your Spring project, some OpenAPI specific configurations will get generated. In order to see and use the generated documentation, you’ll need to run your Spring application. To do so, execute the following command:
mvn spring-boot:run

Once your Spring application is up and running, you can find your generated Swagger documentation at the following endpoint - http://localhost:8080/swagger-ui.html

Depending on how you configured your project, you might see different results in the generated documentation. For instance, if you disable REST API generation for a certain table, the next time you regenerate your project, the endpoint for that table will not be available in the documentation.

With the generated Swagger documentation, you’re able to instantly learn what REST endpoints your application has registered, what HTTP methods are available for each endpoint and execute HTTP requests for those endpoints directly from the Swagger UI:

If you’re not sure what is required in the request body, you can find the request body models at the bottom of the documentation, under the “Models” section:

Note: When connecting to the Swagger endpoint, if you get presented with the following prompt, make sure your Spring entry point is in the correct package (must be above or in the same package that the Swagger configuration is located in):

This is usually a sign that your Swagger configuration was not scanned by Spring.


Writing good and understandable documentation can be a long and tedious process. With the new update to Speedment’s Spring Boot plugin, users are able to generate interactive documentation for their REST API in a matter of seconds.


Article "How to Generate an Entire Database CRUD REST API with Speedment"
The Speedment Initializer capable of generating project templates
Speedment on GitHub


Per Minborg
Mislav Miličević

Tuesday, February 11, 2020

Java14: Join Database Tables with Java 14's new Record

Java14 Records in Joins

Did you know that you can join database tables into a Java Stream with Java 14's preview record feature? Read this short article and find out how it is done using the Speedment Stream ORM. We will start with how to set up your project.


Download Java 14. Go to the Speedment Initializer and download your project skelaton (including pom.xml). Modify the following lines in your pom.xml file:



Make sure that you have the latest version of your ide (e.g. IDEA 2010.1) that supports the new Java 14 features.

Speedment Joins

Speedment allows dynamically JOIN:ed database tables to be consumed as standard Java Streams. In this article, we will use the exemplary Sakila database that contains films, actors, languages etc. Download Sakila here or grab a Docker version here

Tables, views and joins can easily be turned into standard Java streams with Speedment. This is how it can look like in Java 14:

    var speedment = new SakilaApplicationBuilder()

    var joinComponent = speedment.getOrThrow(JoinComponent.class);

    var films = speedment.getOrThrow(FilmManager.class);

    // Define a Java 14 "record" that can hold a Film and a Language
    record FilmLanguage(Film film, Language language) {}

    var join = joinComponent.from(films.getTableIdentifier())
            // Provide the constructor of the Java 14 "record"
            // to be used to construct Film/Language composites

            .forEach(filmLanguage -> System.out.format(
                    "%s is in %s%n",

This will produce the following output:

ACE GOLDFINGER is in English

Code Breakdown

The from() method takes the first table we want to use (Film). The innerJoinOn() method takes a specific column of the second table we want to join. Then, the equal() method takes a column from the first table that we want to use as our join condition. So, in this example, we will get matched Film and Language entities where the column Language.LANGUAGE_ID equal Film.LANGUAGE_ID.

Finally, build() will construct our Join object that can, in turn, be used to create Java Streams. The Join object can be re-used over and over again.

Note how the constructor of the record FilmLanguage is provided in the build() method. Note also how a film and language entity can be obtained from the record (e.g. filmLanguage.film()). This is a big improvement over previous Java version where we had to provide rather lengthy custom classes or use tuples with accessor like get0() and get1() rather than the much more descriptive film() and language()

JOIN Types and Conditions

We can use innerJoinOn(), leftJoinOn(), rightJoinOn() and crossJoin() and tables can be joined using the conditions equal(), notEqual(), lessThan(), lessOrEqual(), greaterThan() and lessOrEqual().

What's Next?

Download open-source Java 14 here.
Download Speedment here.
Read all about the JOIN functionality in the Speedment User's Guide.