= How-to: Implement core services with JPA

[[getting-started]]
== Getting Started

In this guide, we will demonstrate how to implement the xref:ROOT:core-components.adoc[core services] of xref:ROOT:index.adoc[Spring Authorization Server] with JPA. The purpose of this guide is to provide a starting point for implementing these services yourself, with the intention that you will make modifications as necessary to suit your needs.

[[define-data-model]]
== Define the data model

This guide seeks to provide a starting point for the data model and uses the simplest possible structure and data types. To come up with the initial schema, we begin by reviewing the xref:ROOT:core-components.adoc[domain objects] used by the core services. Please note:

NOTE: Except for token, state, metadata, settings and claims values, we will use the JPA default column length of 255 for all columns. In reality, the length and even type of columns you use may need to be customized. Your mileage may vary and you are encouraged to experiment and test before deploying to production.

The xref:ROOT:core-components.adoc#registered-client-repository[`RegisteredClient`] domain object contains a few multi-valued fields and some settings fields that require storing arbitrary key/value data. The following is an example that we will use to create a JPA entity.

[[client-schema]]
.Client Schema
[source,sql]
----
include::example$src/main/resources/oauth2-registered-client-schema.sql[]
----

The xref:ROOT:core-components.adoc#oauth2-authorization-service[`OAuth2Authorization`] domain object is more complex, and contains several multi-valued fields as well as numerous arbitrarily long token values, metadata, settings and claims values. The built-in JDBC implementation utilizes a flattened structure that prefers performance over normalization, which we adopt here as well.

CAUTION: It has been difficult to find a flattened database schema that works well in all cases and with all database vendors. You may need to normalize or heavily alter the following schema for your needs.

The following is an example that we will use to create a JPA entity.

[[authorization-schema]]
.Authorization Schema
[source,sql]
----
include::example$src/main/resources/oauth2-authorization-schema.sql[]
----

The xref:ROOT:core-components.adoc#oauth2-authorization-consent-service[`OAuth2AuthorizationConsent`] domain object is the simplest to model, and only contains a single multi-valued field in addition to a composite key.  The following is an example that we will use to create a JPA entity.

[[authorization-consent-schema]]
.Authorization Consent Schema
[source,sql]
----
include::example$src/main/resources/oauth2-authorization-consent-schema.sql[]
----

[[create-jpa-entities]]
== Create JPA entities

The preceding schema examples provide a reference for the structure of the entities we need to create.

NOTE: The following entities are minimally annotated and are just examples. They allow the schema to be created dynamically and therefore do not require the above sql scripts to be executed manually.

The following is an example of the `Client` entity which is used to persist information mapped from the xref:ROOT:core-components.adoc#registered-client-repository[`RegisteredClient`] domain object.

[[client-entity]]
.Client Entity
[source,java]
----
include::example$src/main/java/sample/jpa/Client.java[tag=class]
----

The following is an example of the `Authorization` entity which is used to persist information mapped from the xref:ROOT:core-components.adoc#oauth2-authorization-service[`OAuth2Authorization`] domain object.

[[authorization-entity]]
.Authorization Entity
[source,java]
----
include::example$src/main/java/sample/jpa/Authorization.java[tag=class]
----

The following is an example of the `AuthorizationConsent` entity which is used to persist information mapped from the xref:ROOT:core-components.adoc#oauth2-authorization-consent-service[`OAuth2AuthorizationConsent`] domain object.

[[authorization-consent-entity]]
.Authorization Consent Entity
[source,java]
----
include::example$src/main/java/sample/jpa/AuthorizationConsent.java[tag=class]
----

[[create-spring-data-repositories]]
== Create Spring Data repositories

By closely examining the interfaces of each core service and reviewing the `Jdbc` implementations, we can derive a minimal set of queries needed for supporting a JPA version of each interface.

The following is an example of the `ClientRepository` capable of finding a <<client-entity,`Client`>> by the `id` and `clientId` fields.

[[client-repository]]
.Client Repository
[source,java]
----
include::example$src/main/java/sample/jpa/ClientRepository.java[tag=class]
----

The following is an example of the `AuthorizationRepository` capable of finding an <<authorization-entity,`Authorization`>> by the `id` field as well as the `state`, `authorizationCodeValue`, `accessTokenValue` and `refreshTokenValue` token fields. It also allows querying a combination of token fields.

[[authorization-repository]]
.Authorization Repository
[source,java]
----
include::example$src/main/java/sample/jpa/AuthorizationRepository.java[tag=class]
----

The following is an example of the `AuthorizationConsentRepository` capable of finding and deleting an <<authorization-consent-entity,`AuthorizationConsent`>> by the `registeredClientId` and `principalName` fields, which form a composite primary key.

[[authorization-consent-repository]]
.Authorization Consent Repository
[source,java]
----
include::example$src/main/java/sample/jpa/AuthorizationConsentRepository.java[tag=class]
----

[[implement-core-services]]
== Implement core services

With the above <<create-jpa-entities,entities>> and <<create-spring-data-repositories,repositories>>, we can begin implementing the core services. By reviewing the `Jdbc` implementations, we can derive a minimal set of internal utilities for converting to/from string values for enumerations and reading/writing JSON data for attributes, settings, metadata and claims fields.

CAUTION: Keep in mind that writing JSON data to text columns with a fixed length has proven problematic with the `Jdbc` implementations. While these examples continue to do so, you may need to split these fields out into a separate table or data store that supports arbitrarily long data values.

The following is an example of the `JpaRegisteredClientRepository` which uses a <<client-repository,`ClientRepository`>> for persisting a <<client-entity,`Client`>>, and maps to/from the xref:ROOT:core-components.adoc#registered-client-repository[`RegisteredClient`] domain object.

[[jpa-registered-client-repository]]
.`RegisteredClientRepository` Implementation
[source,java]
----
include::example$src/main/java/sample/jpa/JpaRegisteredClientRepository.java[tag=class]
----

The following is an example of the `JpaOAuth2AuthorizationService` which uses an <<authorization-repository,`AuthorizationRepository`>> for persisting an <<authorization-entity,`Authorization`>>, and maps to/from the xref:ROOT:core-components.adoc#oauth2-authorization-service[`OAuth2Authorization`] domain object.

[[jpa-oauth2-authorization-service]]
.`OAuth2AuthorizationService` Implementation
[source,java]
----
include::example$src/main/java/sample/jpa/JpaOAuth2AuthorizationService.java[tag=class]
----

The following is an example of the `JpaOAuth2AuthorizationConsentService` which uses an <<authorization-consent-repository,`AuthorizationConsentRepository`>> for persisting an <<authorization-consent-entity,`AuthorizationConsent`>>, and maps to/from the xref:ROOT:core-components.adoc#oauth2-authorization-consent-service[`OAuth2AuthorizationConsent`] domain object.

[[jpa-oauth2-authorization-consent-service]]
.`OAuth2AuthorizationConsentService` Implementation
[source,java]
----
include::example$src/main/java/sample/jpa/JpaOAuth2AuthorizationConsentService.java[tag=class]
----