Module jakarta.data.api
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 CrudRepository<Product, Long> { @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; ... 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, as well as annotations that are placed upon the method and its parameters.
Built-in repository interfaces, such as DataRepository
, are
parameterized with the entity type and id type. Other built-in repository
interfaces, such as CrudRepository
, 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 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); void save(Order order); }
In queries by method name, 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.
Reserved Keywords for 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) |
save |
for save operations | save(Product newProduct) |
updateBy |
for simple update operations | updateByIdSetModifiedOnAddPrice(productId, now, 10.0) |
Keyword | Applies to | Description | Example |
And |
conditions | Requires both conditions to be satisfied in order to match an entity. | findByNameLikeAndPriceLessThanEqual(namePattern, maxPrice) |
Asc |
sorting | Specifies ascending sort order for findBy queries |
findByAgeOrderByFirstNameAsc(age) |
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) |
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") |
Desc |
sorting | Specifies descending sort order for findBy queries |
findByAuthorLastNameOrderByYearPublishedDesc(surname) |
Empty |
collections | Requires that the entity's attribute is an empty collection or has a null value. | countByPhoneNumbersEmpty()
findByInviteesNotEmpty() |
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) |
False |
boolean | Requires that the entity's attribute value has a boolean value of false. | findByCanceledFalse() |
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) |
GreaterThan |
numeric, strings, time | Requires that the entity's attribute value be larger than the parameter value. | findByStartTimeGreaterThan(startedAfter) |
GreaterThanEqual |
numeric, strings, time | Requires that the entity's attribute value be at least as big as the parameter value. | findByAgeGreaterThanEqual(minimumAge) |
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) |
In |
all attribute types | Requires that the entity's attribute value be within the list that is the parameter value. | findByNameIn(names) |
LessThan |
numeric, strings, time | Requires that the entity's attribute value be less than the parameter value. | findByStartTimeLessThan(startedBefore) |
LessThanEqual |
numeric, strings, time | Requires that the entity's attribute value be at least as small as the parameter value. | findByAgeLessThanEqual(maximumAge) |
Like |
strings | Requires that the entity's attribute value match the parameter value, which can be a pattern. | findByNameLike(namePattern) |
Not |
condition | Negates a condition. | deleteByNameNotLike(namePattern)
findByStatusNot("RUNNING") |
Null |
nullable types | Requires that the entity's attribute has a null value. | findByEndTimeNull()
findByAgeNotNull() |
Or |
conditions | Requires at least one of the two conditions to be satisfied in order to match an entity. | findByPriceLessThanEqualOrDiscountGreaterThanEqual(maxPrice, minDiscount) |
OrderBy |
sorting | 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) |
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) |
True |
boolean | Requires that the entity's attribute value has a boolean value of true. | findByAvailableTrue() |
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.
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 |
|
find...By... |
E ,
Optional<E> |
For queries returning a single item (or none) |
find...By... |
E[] ,
Iterable<E> ,
Streamable<E> ,
Collection<E> |
|
find...By... |
Stream<E> |
The caller must arrange to close
all streams that it obtains from repository methods. |
find...By... |
Collection subtypes |
The subtype must have a public default constructor and support addAll or add |
find...By... |
Page<E> , Iterator<E> |
For use with pagination |
find...By... |
LinkedHashMap<K, E> |
Ordered map of Id attribute value to entity |
save(E) |
E ,
void , Void |
For saving a single entity. |
save(E...) ,
save(Iterable<E>) ,
save(Stream<E>) |
void , Void ,
E[] ,
Iterable<E> ,
Stream<E> ,
Collection<E>
Collection subtypes |
For saving multiple entities.
Collection subtypes must have a public default constructor and support addAll or add |
Parameters to Repository Methods
The parameters to a repository method correspond to the conditions that are
defined within the name of repository method (see reserved keywords above),
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.
After all conditions are matched up with 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"));
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
PackageExported To ModulesOpened To ModulesDescriptionAll ModulesNoneCommon data access exceptions.All ModulesAll ModulesIn Domain-driven design, DDD, a repository is an object that participates in the domain but abstracts away storage and infrastructure details.