Module jakarta.data


module jakarta.data

Jakarta Data standardizes a programming model where data is represented by simple Java classes and where operations on data are represented by interface methods.

The application defines simple Java objects called entities to represent data in the database. Fields or accessor methods designate each entity property. For example,

 @Entity
 public class Product {
     @Id
     public long id;
     public String name;
     public float price;
     public int yearProduced;
     ...
 }
 

The application defines interface methods on separate classes called repositories to perform queries and other operations on entities. Repositories are interface classes that are annotated with the Repository annotation. For example,

 @Repository
 public interface Products extends BasicRepository<Product, Long> {

     @Insert
     void create(Product prod);

     @OrderBy("price")
     List<Product> findByNameIgnoreCaseLikeAndPriceLessThan(String namePattern, float max);

     @Query("UPDATE Product o SET o.price = o.price * (1.0 - ?1) WHERE o.yearProduced <= ?2")
     int discountOldInventory(float rateOfDiscount, int maxYear);

     ...
 }
 

Repository interfaces are implemented by the container/runtime and are made available to applications via the jakarta.inject.Inject annotation. For example,

 @Inject
 Products products;

 ...
 products.create(newProduct);

 found = products.findByNameIgnoreCaseLikeAndPriceLessThan("%cell%phone%", 900.0f);

 numDiscounted = products.discountOldInventory(0.15f, Year.now().getValue() - 1);
 

Jakarta Persistence and Jakarta NoSQL define entity models that you may use in Jakarta Data. You may use jakarta.persistence.Entity and the corresponding entity-related annotations of the Jakarta Persistence specification to define entities for relational databases. You may use jakarta.nosql.mapping.Entity and the corresponding entity-related annotations of the Jakarta NoSQL specification to define entities for NoSQL databases. For other types of data stores, you may use other entity models that are determined by the Jakarta Data provider for the respective data store type.

Methods of repository interfaces must be styled according to a defined set of conventions, which instruct the container/runtime about the desired data access operation to perform. These conventions consist of patterns of reserved keywords within the method name, method parameters with special meaning, method return types, and annotations that are placed upon the method and its parameters.

Built-in repository super interfaces, such as DataRepository, are provided as a convenient way to inherit commonly used methods and are parameterized with the entity type and id type. Other built-in repository interfaces, such as BasicRepository, can be used in place of DataRepository and provide a base set of predefined repository methods which serve as an optional starting point. You can extend these built-in interfaces to add your own custom methods. You can also define your own repository interface without inheriting from the built-in super interfaces. You can copy individual method signatures from the built-in repository methods onto your own, which is possible because the built-in repository methods are consistent with the same set of conventions that you use to write custom repository methods.

Entity property names are computed from the fields and accessor methods of the entity class and must be unique ignoring case. For simple entity properties, the field or accessor method name is used as the entity property name. In the case of embedded classes within entities, entity property names are computed by concatenating the field or accessor method names at each level, delimited by _ or undelimited for query by method name (such as findByAddress_ZipCode or findByAddressZipCode) when referred to within repository method names, and delimited by . when used within annotation values, such as for OrderBy.value() and Query.value(),

 @Entity
 public class Order {
     @Id
     public String orderId;
     @Embedded
     public Address address;
     ...
 }

 @Embeddable
 public class Address {
     public int zipCode;
     ...
 }

 @Repository
 public interface Orders {
     @OrderBy("address.zipCode")
     List<Order> findByAddressZipCodeIn(List<Integer> zipCodes);

     @Query("SELECT o FROM Order o WHERE o.address.zipCode=?1")
     List<Order> forZipCode(int zipCode);

     @Save
     Order checkout(Order order);
 }
 

When using the Query by Method Name pattern, Id is an alias for the entity property that is designated as the id. Entity property names that are used in queries by method name must not contain reserved words.

Methods with Entity Parameters

You can annotate a method with Insert, Update, Save, or Delete if the method accepts a single parameter, which must be one of:

  • An entity.
  • An array of entity (variable arguments array is permitted).
  • An Iterable of entity (subclasses such as List are permitted).
Lifecycle Annotations
Annotation Description Example
Delete deletes entities @Delete
public void remove(person);
Insert creates new entities @Insert
public List<Employee> add(List<Employee> newEmployees);
Save update if exists, otherwise insert @Save
Product[] saveAll(Product... products)
Update updates an existing entity @Update
public boolean modify(Product modifiedProduct);

Refer to the JavaDoc of each annotation for more information.

Query by Method Name

Repository methods following the Query by Method Name pattern must include the By keyword in the method name. Query conditions are determined by the method name following the By keyword.

Query By Method Name
Prefix Description Example
countBy counts the number of entities countByAgeGreaterThanEqual(ageLimit)
deleteBy for delete operations deleteByStatus("DISCONTINUED")
existsBy for determining existence existsByYearHiredAndWageLessThan(2022, 60000)
find...By for find operations findByHeightBetween(minHeight, maxHeight)
updateBy for simple update operations updateByIdSetModifiedOnAddPrice(productId, now, 10.0)

When using the Query By Method Name pattern the conditions are defined by the portion of the repository method name (referred to as the Predicate) that follows the By keyword, in the same order specified. Most conditions, such as Like or LessThan, correspond to a single method parameter. The exception to this rule is Between, which corresponds to two method parameters.

Key-value and Wide-Column databases raise UnsupportedOperationException for queries on attributes other than the identifier/key.

Reserved Keywords for Query by Method Name

Reserved for Predicate
Keyword Applies to Description Example Unavailable In
And conditions Requires both conditions to be satisfied in order to match an entity. findByNameLikeAndPriceLessThanEqual(namePattern, maxPrice) Key-value
Wide-Column
Between numeric, strings, time Requires that the entity's attribute value be within the range specified by two parameters. The minimum is listed first, then the maximum. findByAgeBetween(minAge, maxAge) Key-value
Wide-Column
Contains collections, strings For Collection attributes, requires that the entity's attribute value, which is a collection, includes the parameter value. For String attributes, requires that any substring of the entity's attribute value match the entity's attribute value, which can be a pattern with wildcard characters. findByRecipientsContains(email)
findByDescriptionNotContains("refurbished")
Key-value
Wide-Column
Document
Empty collections Requires that the entity's attribute is an empty collection or has a null value. countByPhoneNumbersEmpty()
findByInviteesNotEmpty()
Key-value
Wide-Column
Document
Graph
EndsWith strings Requires that the characters at the end of the entity's attribute value match the parameter value, which can be a pattern. findByNameEndsWith(surname) Key-value
Wide-Column
Document
Graph
False boolean Requires that the entity's attribute value has a boolean value of false. findByCanceledFalse() Key-value
Wide-Column
GreaterThan numeric, strings, time Requires that the entity's attribute value be larger than the parameter value. findByStartTimeGreaterThan(startedAfter) Key-value
Wide-Column
GreaterThanEqual numeric, strings, time Requires that the entity's attribute value be at least as big as the parameter value. findByAgeGreaterThanEqual(minimumAge) Key-value
Wide-Column
IgnoreCase strings Requires case insensitive comparison. For query conditions as well as ordering, the IgnoreCase keyword can be specified immediately following the entity property name. countByStatusIgnoreCaseNotLike("%Delivered%")
findByZipcodeOrderByStreetIgnoreCaseAscHouseNumAsc(55904)
Key-value
Wide-Column
Document
Graph
In all attribute types Requires that the entity's attribute value be within the list that is the parameter value. findByNameIn(names) Key-value
Wide-Column
Document
Graph
LessThan numeric, strings, time Requires that the entity's attribute value be less than the parameter value. findByStartTimeLessThan(startedBefore) Key-value
Wide-Column
LessThanEqual numeric, strings, time Requires that the entity's attribute value be at least as small as the parameter value. findByAgeLessThanEqual(maximumAge) Key-value
Wide-Column
Like strings Requires that the entity's attribute value match the parameter value, which can be a pattern. findByNameLike(namePattern) Key-value
Wide-Column
Document
Graph
Not condition Negates a condition. deleteByNameNotLike(namePattern)
findByStatusNot("RUNNING")
Key-value
Wide-Column
Null nullable types Requires that the entity's attribute has a null value. findByEndTimeNull()
findByAgeNotNull()
Key-value
Wide-Column
Document
Graph
Or conditions Requires at least one of the two conditions to be satisfied in order to match an entity. findByPriceLessThanEqualOrDiscountGreaterThanEqual(maxPrice, minDiscount) Key-value
Wide-Column
StartsWith strings Requires that the characters at the beginning of the entity's attribute value match the parameter value, which can be a pattern. findByNameStartsWith(firstTwoLetters) Key-value
Wide-Column
Document
Graph
True boolean Requires that the entity's attribute value has a boolean value of true. findByAvailableTrue() Key-value
Wide-Column


Reserved for Subject
Keyword Applies to Description Example Unavailable In
First find...By Limits the amount of results that can be returned by the query to the number that is specified after First, or absent that to a single result. findFirst25ByYearHiredOrderBySalaryDesc(int yearHired)
findFirstByYearHiredOrderBySalaryDesc(int yearHired)
Key-value
Wide-Column
Document
Graph


Reserved for Order Clause
Keyword Description Example
Asc Specifies ascending sort order for findBy queries findByAgeOrderByFirstNameAsc(age)
Desc Specifies descending sort order for findBy queries findByAuthorLastNameOrderByYearPublishedDesc(surname)
OrderBy Sorts results of a findBy query according to one or more entity attributes. Multiple attributes are delimited by Asc and Desc, which indicate ascending and descending sort direction. Precedence in sorting is determined by the order in which attributes are listed. findByStatusOrderByYearHiredDescLastNameAsc(empStatus)

Key-value and Wide-Column databases raise UnsupportedOperationException if an order clause is present.

Reserved for Future Use

The specification does not define behavior for the following keywords, but reserves them as keywords that must not be used as entity attribute names when using Query by Method Name. This gives the specification the flexibility to add them in future releases without introducing breaking changes to applications.

Reserved for query conditions: AbsoluteValue, CharCount, ElementCount, Rounded, RoundedDown, RoundedUp, Trimmed, WithDay, WithHour, WithMinute, WithMonth, WithQuarter, WithSecond, WithWeek, WithYear.

Reserved for find...By and count...By: Distinct.

Reserved for updates: Add, Divide, Multiply, Set, Subtract.

Wildcard Characters

Wildcard characters for patterns are determined by the data access provider. For Jakarta Persistence providers, _ matches any one character and % matches 0 or more characters.

Logical Operator Precedence

For relational databases, the logical operator And is evaluated on conditions before Or when both are specified on the same method. Precedence for other database types is limited to the capabilities of the database.

Return Types for Repository Methods
Method Return Types Notes
countBy... long, Long,
int, Integer,
short, Short,
Number
Jakarta Persistence providers limit the maximum to Integer.MAX_VALUE
deleteBy...,
updateBy...
void, Void,
boolean, Boolean,
long, Long,
int, Integer,
short, Short,
Number
Jakarta Persistence providers limit the maximum to Integer.MAX_VALUE
existsBy... boolean, Boolean For determining existence.
find...,
find...By...
E,
Optional<E>
For queries returning a single item (or none)
find...,
find...By...
E[],
Iterable<E>,
Streamable<E>,
Collection<E>
For queries where it is possible to return more than 1 item.
find...,
find...By...
Stream<E> The caller must arrange to close all streams that it obtains from repository methods.
find...,
find...By...
Collection subtypes The subtype must have a public default constructor and support addAll or add
find...(..., Pageable),
find...By...(..., Pageable)
Page<E>, KeysetAwarePage<E>,
Slice<E>, KeysetAwareSlice<E>
For use with pagination
find...,
find...By...
LinkedHashMap<K, E> Ordered map of Id attribute value to entity

Refer to the Insert, Update, Save, and Delete JavaDoc for valid return types when using those annotations. Whenever the return type is an Iterable subtype that is a concrete class, the class must have a public default constructor and support addAll or add.

Parameter-based Conditions

When using the Parameter-based Conditions pattern, the method name begins with find and must not include the By keyword. The query conditions are defined by the method parameters. Method parameter names must match the name of an entity attribute. The _ character can be used in method parameter names to reference embedded attributes. All conditions are considered to be the equality condition. All conditions must match in order to retrieve an entity. The developer must compile with the -parameters compiler option that makes parameter names available at run time.

The following examples illustrate the difference between Query By Method Name and Parameter-based Conditions patterns. Both methods accept the same parameters and have the same behavior.

 // Query by Method Name:
 Vehicle[] findByMakeAndModelAndYear(String makerName, String model, int year, Sort... sorts);

 // Parameter-based Conditions:
 Vehicle[] find(String make, String model, int year, Sort... sorts);
 

Additional Method Parameters

When using @Query or the Query By Method Name pattern or the Parameter-based Find pattern, after conditions are determined from the corresponding parameters, the remaining repository method parameters are used to enable other capabilities such as pagination, limits, and sorting.

Limits

You can cap the number of results that can be returned by a single invocation of a repository find method by adding a Limit parameter. You can also limit the results to a positional range. For example,

 @Query("SELECT o FROM Products o WHERE (o.fullPrice - o.salePrice) / o.fullPrice >= ?1 ORDER BY o.salePrice DESC")
 Product[] highlyDiscounted(float minPercentOff, Limit limit);

 ...
 first50 = products.highlyDiscounted(0.30, Limit.of(50));
 ...
 second50 = products.highlyDiscounted(0.30, Limit.range(51, 100));
 

Pagination

You can request that results be paginated by adding a Pageable parameter to a repository find method. For example,

 Product[] findByNameLikeOrderByAmountSoldDescNameAsc(
           String pattern, Pageable pagination);
 ...
 page1 = products.findByNameLikeOrderByAmountSoldDescNameAsc(
                  "%phone%", Pageable.ofSize(20));
 

Sorting at Runtime

When using pagination, you can dynamically supply sorting criteria via the Pageable.sortBy(Sort...) and Pageable.sortBy(Iterable) methods. For example,

 Product[] findByNameLike(String pattern, Pageable pagination);

 ...
 Pageable pagination = Pageable.ofSize(25).sortBy(
                           Sort.desc("price"),
                           Sort.asc("name"));
 page1 = products.findByNameLikeAndPriceBetween(
                 namePattern, minPrice, maxPrice, pagination);
 

To supply sorting criteria dynamically without using pagination, add one or more Sort parameters (or Sort...) to a repository find method. For example,

 Product[] findByNameLike(String pattern, Limit max, Sort... sortBy);

 ...
 page1 = products.findByNameLike(namePattern, Limit.of(25),
                                 Sort.desc("price"),
                                 Sort.desc("amountSold"),
                                 Sort.asc("name"));
 

Repository Default Methods

You can compose default methods on your repository interface to supply user-defined implementation.

Resource Accessor Methods

For some advanced scenarios, you might need access to an underlying resource from the Jakarta Data provider, such as a jakarta.persistence.EntityManager, javax.sql.DataSource, or java.sql.Connection

To obtain the above, you can define accessor methods on your repository interface, where the method has no parameters and its result value is one of the aforementioned types. When you invoke the method, the Jakarta Data provider supplies an instance of the requested type of resource.

For example,

 @Repository
 public interface Cars extends BasicRepository<Car, Long> {
     ...

     EntityManager getEntityManager();

     default Car[] advancedSearch(SearchOptions filter) {
         EntityManager em = getEntityManager();
         ... use entity manager
         return results;
     }
 }
 

If the resource type inherits from AutoCloseable and you invoke the accessor method from a repository default method, the Jakarta Data provider automatically closes the resource after the default method ends. If you invoke the accessor method from outside the scope of a default method, you are responsible for closing the resource instance.

Jakarta Validation

When a Jakarta Validation provider is present, constraints that are defined on repository method parameters and return values are validated according to the section, "Method and constructor validation", of the Jakarta Validation specification.

The jakarta.validation.Valid annotation opts in to cascading validation, causing constraints within the objects that are supplied as parameters or returned as results to also be validated.

Repository methods raise jakarta.validation.ConstraintViolationException if validation fails.

The following is an example of method validation, where the parameter to findByEmailIn must not be the empty set, and cascading validation, where the Email and NotNull constraints on the entity that is supplied to save are validated,

 import jakarta.validation.Valid;
 import jakarta.validation.constraints.Email;
 import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.NotNull;
 ...

 @Repository
 public interface AddressBook extends DataRepository<Contact, Long> {

     List<Contact> findByEmailIn(@NotEmpty Set<String> emails);

     void save(@Valid Contact c);
 }

 @Entity
 public class Contact {
     @Email
     @NotNull
     public String email;
     @Id
     public long id;
     ...
 }
 

Jakarta Transactions

Repository methods can participate in global transactions. If a global transaction is active on the thread where a repository method runs and the data source that backs the repository is capable of transaction enlistment, then the repository operation runs as part of the transaction. The repository operation does not commit or roll back a transaction that was already present on the thread, but it might mark the transaction for rollback only (jakarta.transaction.Status.STATUS_MARKED_ROLLBACK) if the repository operation fails.

When running in an environment where Jakarta Transactions and Jakarta CDI are available, you can annotate repository methods with jakarta.transaction.Transactional to define how the container manages transactions with respect to the repository method.

Interceptor Annotations on Repository Methods

Interceptor bindings such as jakarta.transaction.Transactional can annotate a repository method. The repository bean honors these annotations when running in an environment where the Jakarta EE technology that provides the interceptor is available.

  • Packages

    Package
    Exported To Modules
    Opened To Modules
    Description
    All Modules
    None
    Jakarta Data provides an API that makes data access easy.
    All Modules
    None
    Common data access exceptions.
    All Modules
    None
    A static metamodel for entities that are used in Jakarta Data repositories.
    All Modules
    None
    Splits query results into slices or pages.
    All Modules
    All Modules
    In Domain-driven design, DDD, a repository is an object that participates in the domain but abstracts away storage and infrastructure details.