[[mapping]]
= POJO Mapping

This section covers:

* <<mapping.entities>>
* <<mapping.repositories>>
* The <<Mapping PDX Serializer>>

[[mapping.entities]]
== Entity Mapping

{sdg-name} provides support to map entities that are stored in a Region.  The mapping metadata is defined by
using annotations on application domain classes, as the following example shows:

.Mapping a domain class to a {data-store-name} Region
====
[source,java]
----
@Region("People")
public class Person {

  @Id Long id;

  String firstname;
  String lastname;

  @PersistenceConstructor
  public Person(String firstname, String lastname) {
    // …
  }

  …
}
----
====

The `@Region` annotation can be used to customize the Region in which an instance of the `Person` class is stored.
The `@Id` annotation can be used to annotate the property that should be used as the cache Region key, identifying
the Region entry. The `@PersistenceConstructor` annotation helps to disambiguate multiple potentially available
constructors, taking parameters and explicitly marking the constructor annotated as the constructor to be used to
construct entities. In an application domain class with no or only a single constructor, you can omit the annotation.

In addition to storing entities in top-level Regions, entities can be stored in Sub-Regions as well,
as the following example shows:

[source,java]
----
@Region("/Users/Admin")
public class Admin extends User {
  …
}

@Region("/Users/Guest")
public class Guest extends User {
  …
}
----

Be sure to use the full path of the {data-store-name} Region, as defined with the {sdg-name} XML namespace
by using the `id` or `name` attributes of the `<*-region>` element.

[[mapping.entities.region]]
=== Entity Mapping by Region Type

In addition to the `@Region` annotation, {sdg-name} also recognizes type-specific Region mapping annotations:
`@ClientRegion`, `@LocalRegion`, `@PartitionRegion`, and `@ReplicateRegion`.

Functionally, these annotations are treated exactly the same as the generic `@Region` annotation in the {sdg-acronym}
mapping infrastructure. However, these additional mapping annotations are useful in {sdg-name}'s
annotation configuration model. When combined with the `@EnableEntityDefinedRegions` configuration annotation
on a Spring `@Configuration` annotated class, it is possible to generate Regions in the local cache, whether
the application is a client or peer.

These annotations let you be more specific about what type of Region your application entity class should be mapped to
and also has an impact on the data management policies of the Region (for example, partition -- also known as sharding
-- versus replicating data).

Using these type-specific Region mapping annotations with the {sdg-acronym} annotation configuration model saves you
from having to explicitly define these Regions in configuration.

[[mapping.repositories]]
== Repository Mapping

As an alternative to specifying the Region in which the entity is stored by using the `@Region` annotation
on the entity class, you can also specify the `@Region` annotation on the entity's `Repository` interface.
See <<gemfire-repositories>> for more details.

However, suppose you want to store a `Person` record in multiple {data-store-name} Regions (for example, `People`
and `Customers`). Then you can define your corresponding `Repository` interface extensions as follows:

[source,java]
----
@Region("People")
public interface PersonRepository extends GemfireRepository<Person, String> {
…
}

@Region("Customers")
public interface CustomerRepository extends GemfireRepository<Person, String> {
...
}
----

Then, using each Repository individually, you can store the entity in multiple {data-store-name} Regions,
as the following example shows:

[source,java]
----
@Service
class CustomerService {

  CustomerRepository customerRepo;

  PersonRepository personRepo;

  Customer update(Customer customer) {
    customerRepo.save(customer);
    personRepo.save(customer);
    return customer;
  }
----

You can even wrap the `update` service method in a Spring managed transaction, either as a local cache transaction
or a global transaction.

[[mapping.pdx-serializer]]
== Mapping PDX Serializer

{sdg-name} provides a custom {x-data-store-javadoc}/org/apache/geode/pdx/PdxSerializer.html[`PdxSerializer`]
implementation that uses the mapping information to customize entity serialization.

It also lets you customize entity instantiation by using the Spring Data `EntityInstantiator` abstraction.
By default, the serializer uses a `ReflectionEntityInstantiator` that uses the persistence constructor of
the mapped entity (the default constructor, a singly declared constructor, or a constructor explicitly
annotated with `@PersistenceConstructor`).

To provide arguments for constructor parameters, the serializer reads fields with the named constructor parameter,
explicitly specified using Spring's `@Value` annotation, from the supplied
{x-data-store-javadoc}/org/apache/geode/pdx/PdxReader.html[`PdxReader`],
as shown in the following example:

.Using `@Value` on entity constructor parameters
====
[source,java]
----
public class Person {

  public Person(@Value("#root.thing") String firstName, @Value("bean") String lastName) {
    // …
  }
}
----
====

An entity class annotated in this way has the `thing` field read from the `PdxReader` and passed as the value
for the constructor parameter, `firstname`. The value for `lastName` is a Spring bean with the name "`bean`".

In addition to the custom instantiation logic and strategy provided by `EntityInstantiators`,
the `MappingPdxSerializer` also provides capabilities well beyond {data-store-name}'s own
{x-data-store-javadoc}/org/apache/geode/pdx/ReflectionBasedAutoSerializer.html[`ReflectionBasedAutoSerializer`].

While {data-store-name}'s `ReflectionBasedAutoSerializer` conveniently uses Java reflection to populate entities
and uses regular expressions to identify types that should be handled (serialized and deserialized) by
the `ReflectionBasedAutoSerializer`, it cannot, unlike `MappingPdxSerializer`, perform the following:

* Register custom `PdxSerializer` objects per entity field and property names and types.
* Conveniently identifies ID properties.
* Automatically handles read-only properties.
* Automatically handles transient properties.
* Allows more robust type filtering in a `null`-safe manner (for example, not limited to
only expressing types using regex).

We now explore each feature of the `MappingPdxSerializer` in a bit more detail.

[[mapping.pdx-serializer.custom-serialization]]
=== Custom PdxSerializer Registration

The `MappingPdxSerializer` gives you the ability to register custom `PdxSerializers` based on an entity's
field and property names and types.

For instance, suppose you have defined an entity type modeling a `User` as follows:

[source,java]
----
package example.app.auth.model;

public class User {

  private String name;

  private Password password;

  ...
}
----

While the user's name probably does not require any special logic to serialize the value, serializing the password
might require additional logic to handle the sensitive nature of the field or property.

Perhaps you want to protect the password when sending the value over the network, between a client and a server,
and you only want to store the salted hash. When using the `MappingPdxSerializer`, you can register
a custom `PdxSerializer` to handle the user's password, as follows:

.Registering custom `PdxSerializers` by POJO field/property type
====
[source,java]
----
Map<?, PdxSerializer> customPdxSerializers = new HashMap<>();

customPdxSerializers.put(Password.class, new SaltedHashPasswordPdxSerializer());

mappingPdxSerializer.setCustomPdxSerializers(customPdxSerializers);
----

After registering the application-defined `SaltedHashPasswordPdxSerializer` instance with the `Password`
application domain model type, the `MappingPdxSerializer` consults the custom `PdxSerializer` to serialize
and deserialize all `Password` objects regardless of the containing object (for example, `User`).

However, suppose you want to customize the serialization of only `Passwords` on `User` objects.
To do so, you can register the custom `PdxSerializer` for the `User` type by specifying the fully qualified name
of the `Class's` field or property, as the following example shows:

.Registering custom `PdxSerializers` by POJO field/property name
====
[source,java]
----
Map<?, PdxSerializer> customPdxSerializers = new HashMap<>();

customPdxSerializers.put("example.app.auth.model.User.password", new SaltedHashPasswordPdxSerializer());

mappingPdxSerializer.setCustomPdxSerializers(customPdxSerializers);
----

Notice the use of the fully-qualified field or property name (that is `example.app.auth.model.User.password`)
as the custom `PdxSerializer` registration key.

NOTE: You could construct the registration key by using a more logical code snippet, such as the following:
`User.class.getName().concat(".password");`.  We recommended this over the example shown earlier.
The preceding example tried to be as explicit as possible about the semantics of registration.

[[mapping.pdx-serializer.id-properties]]
=== Mapping ID Properties

Like {data-store-name}'s `ReflectionBasedAutoSerializer`, {sdg-acronym}'s `MappingPdxSerializer` is also able to
determine the identifier of the entity. However, `MappingPdxSerializer` does so by using Spring Data's mapping metadata,
specifically by finding the entity property designated as the identifier using Spring Data's
{spring-data-commons-javadoc}/org/springframework/data/annotation/Id.html[`@Id`] annotation.

For example:

[source,java]
----
class Customer {

  @Id
  Long id;

  ...
}
----

In this case, the `Customer` `id` field is marked as the identifier field in the PDX type metadata by using
{x-data-store-javadoc}/org/apache/geode/pdx/PdxWriter.html#markIdentityField-java.lang.String-[`PdxWriter.markIdentifierField(:String)`]
when the `PdxSerializer.toData(..)` method is called during serialization.

[[mapping.pdx-serializer.read-only-properties]]
=== Mapping Read-only Properties

What happens when your entity defines a read-only property?

First, it is important to understand what a "`read-only`" property is.  If you define a POJO by following the
http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html[JavaBeans]specification (as Spring does),
you might define a POJO with a read-only property, as follows:

[source,java]
----
package example;

class ApplicationDomainType {

  private AnotherType readOnly;

  public AnotherType getReadOnly() [
    this.readOnly;
  }

  ...
}
----

The `readOnly` property is "`read-only`" because it does not provide a setter method. It has only a getter method.
In this case, the `readOnly` property (not to be confused with the `readOnly` `DomainType` field) is considered
"`read-only`".

As a result, the `MappingPdxSerializer` does not try to write this value back when populating an instance of
`DomainType` in the `PdxSerializer.fromData(:Class<?>, :PdxReader)` method.

This is useful in situations where you might be returning a view or projection of some entity type and you only want
to write state that is writable. Perhaps the view or projection of the entity is based on authorization or some other
criteria. The point is that you can leverage this feature as is appropriate for your application's use cases
and requirements. If you want the field or property to always be written, you can define a setter.

[[mapping.pdx-serializer.transient-properties]]
=== Mapping Transient Properties

Likewise, what happens when your entity defines `transient` properties?

You would expect the `transient` fields or properties of your entity not to be serialized to the stream of PDX bytes
when serializing the entity. That is exactly what happens, unlike {data-store-name}'s own `ReflectionBasedAutoSerializer`,
which serializes everything accessible from the object through Java reflection.

The `MappingPdxSerializer` does not serialize any fields or properties that are qualified as being transient either
by using Java's `transient` keyword (in the case of fields) or by using the
{spring-data-commons-javadoc}/org/springframework/data/annotation/Transient.html[`@Transient`]
Spring Data annotation on either fields or properties.

For example, you might define an entity with transient fields and properties as follows:

[source,java]
----
package example;

class Process {

  private transient int id;

  private File workingDirectory;

  private String name;

  private Type type;

  @Transient
  public String getHostname() {
    ...
  }

  ...
}
----

Neither the `Process` `id` field nor the readable `hostname` property are written to the PDX serialized bytes.

[[mapping.pdx-serializer.type-filtering]]
=== Filtering by Class types

Similar to {data-store-name}'s `ReflectionBasedAutoSerializer`, {sdg-acronym}'s `MappingPdxSerializer` lets you filter
the types of objects that the `MappingPdxSerializer` serializes and deserializes.

However, unlike {data-store-name}'s `ReflectionBasedAutoSerializer`, which uses complex regular expressions to express
which types the serializer handles, {sdg-acronym}'s `MappingPdxSerializer` uses the much more robust
https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html[`java.util.function.Predicate`] interface
and API to express type-matching criteria.

If you like to use regular expressions, you can implement a `Predicate` by using
Java's https://docs.oracle.com/javase/8/docs/api/java/util/regex/package-summary.html[regular expression support].

The nice part about Java's `Predicate` interface is that you can compose `Predicates` by using convenient
and appropriate API methods, including:
https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html#and-java.util.function.Predicate-[`and(:Predicate)`],
https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html#or-java.util.function.Predicate-[`or(:Predicate)`],
and https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html#negate--[`negate()`].

The following example shows the `Predicate` API in use:

[source,java]
----
Predicate<Class<?>> customerTypes =
  type -> Customer.class.getPackage().getName().startsWith(type.getName());

Predicate typeFilters = customerTypes
  .or(type -> User.class.isAssignble(type)) // Include User sub-types (e.g. Admin, Guest, etc)
  .and(type -> !Reference.class.getPackage(type.getPackage()); // Exclude all Reference types

mappingPdxSerializer.setTypeFilters(typeFilters);
----

NOTE: In addition to setting your own type filtering `Predicates`, SDG's `MappingPdxSerializer` now automatically
registers pre-defined `Predicates` that filter types from the `org.apache.geode` package along with `null` objects
when calling `PdxSerializer.toData(:Object, :PdxWriter)` or `null` `Class` types when calling
`PdxSerializer.fromData(:Class<?>, :PdxReader)` methods.
