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;
     ...
 }
 
 A repository is an interface annotated with the Repository annotation.
 A repository declares methods which perform queries and other operations on entities.
 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 SET price = price * (1.0 - ?1) WHERE 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 programming models for entity classes that may be used with Jakarta Data:
jakarta.persistence.Entityand the corresponding entity-related annotations of the Jakarta Persistence specification may be used to define entities stored in a relational database, orjakarta.nosql.Entityand the corresponding entity-related annotations of the Jakarta NoSQL specification may be used to define entities stored in a NoSQL database.
A Jakarta Data provider may define its own programming model for entity classes representing some other arbitrary kind of data.
Methods of repository interfaces must be styled according to a well-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 placed upon the method and its parameters.
Built-in repository superinterfaces, such as DataRepository,
 are provided as a convenient way to inherit commonly used methods and
 are parameterized by the entity type and by its id type. Other built-in
 repository interfaces, such as BasicRepository, may be used in
 place of DataRepository and provide a base set of predefined
 repository operations serving as an optional starting point. The Java
 application programmer may extend these built-in interfaces, adding
 custom methods. Alternatively, the programmer may define a repository
 interface without inheriting the built-in superinterfaces. A programmer
 may even copy individual method signatures from the built-in repositories
 to a repository which does not inherit any built-in superinterface. This
 is possible because the methods of the built-in repository superinterfaces
 respect the conventions defined for custom repository methods.
The following example shows an entity class, an embeddable class, and a repository interface:
 @Entity
 public class Purchase {
     @Id
     public String purchaseId;
     @Embedded
     public Address address;
     ...
 }
 @Embeddable
 public class Address {
     public int zipCode;
     ...
 }
 @Repository
 public interface Purchases {
     @OrderBy("address.zipCode")
     List<Purchase> findByAddressZipCodeIn(List<Integer> zipCodes);
     @Query("WHERE address.zipCode = ?1")
     List<Purchase> forZipCode(int zipCode);
     @Save
     Purchase checkout(Purchase purchase);
 }
 
 Entities
An entity programming model typically specifies an entity-defining
 annotation that is used to identify entity classes. For Jakarta Persistence,
 this is jakarta.persistence.Entity. For Jakarta NoSQL, it is
 jakarta.nosql.Entity. A provider may even have no entity-defining
 annotation and feature a programming model for entity classes where the
 entity classes are unannotated.
Furthermore, an entity programming model must define an annotation which
 identifies the field or property holding the unique identifier of an entity.
 For Jakarta Persistence, it is jakarta.persistence.Id or
 jakarta.persistence.EmbeddedId. For Jakarta NoSQL, it is
 jakarta.nosql.Id. Alternatively, an entity programming model might
 allow the identifier field or property to be identified via some convention.
 Every entity has a unique identifier.
An entity has an arbitrary number of persistent fields or properties.
Persistent field names
Each persistent field of an entity or embeddable class is assigned a name:
- when direct field access is used, the name of a persistent field is simply the name of the Java field, but
 - when property-based access is used, the name of the field is derived from the accessor methods, according to JavaBeans conventions.
 
Within a given entity class or embeddable class, names assigned to persistent fields must be unique ignoring case.
Furthermore, within the context of a given entity, each persistent field of an embeddable class reachable by navigation from the entity class may be assigned a compound name. The compound name is obtained by concatenating the names assigned to each field traversed by navigation from the entity class to the persistent field of the embedded class, optionally joined by a delimiter.
- For parameters of a 
Findmethod, the delimiter is_. - For path expressions within a query, the delimiter
     is 
.. - For method names in Query by Method Name, the delimiter is
     
_and it is optional. For example,findByAddress_ZipCodeorfindByAddressZipCodeare both legal. - For arguments to constructor methods of 
Sort, the delimiter is_or.. - For the 
valuemember of theOrderByorByannotation the delimiter is_or.. 
A persistent field name used in a Query by Method Name must not contain a keyword reserved by Query by Method Name.
Persistent field types (basic types)
The following is a list of valid basic entity attribute types. These can be used as the types of repository method parameters for the respective entity attribute.
| Category | Basic Types | Notes | 
| Primitives and primitive wrappers | boolean and Boolean
 byte and Byte
 char and Character
 double and Double
 float and Float
 int and Integer
 long and Long
 short and Short | 
 |
| Binary data | byte[] | 
 Not sortable. | 
| Enumerated types | enum types | 
 It is provider-specific whether sorting is based on
 Enum.ordinal() or Enum.name().
 The Jakarta Persistence default of ordinal can be
 overridden with the jakarta.persistence.Enumerated
 annotation. | 
| Large numbers | BigDecimal
 BigInteger | 
 |
| Textual data | String | 
 |
| Time and Dates | Instant
 LocalDate
 LocalDateTime
 LocalTime | 
 |
| Universally unique identifier | UUID | 
 
A Jakarta Data provider might allow additional entity attribute types.
Lifecycle methods
A lifecycle method makes changes to persistent data in the data store.
 A lifecycle method must be annotated with a lifecycle annotation such as
 Insert, Update, Save, or Delete. The
 method must accept a single parameter, whose type is either:
- the class of the entity, or
 List<E>orE[]whereEis the class of the entities.
The annotated method must be declared void, or, except in the
 case of @Delete, have a return type that is the same as the type
 of its parameter.
| Annotation | Description | Example | 
Delete | 
 deletes entities | @Deletepublic void remove(person); | 
Insert | 
 creates new entities | @Insertpublic List<Employee> add(List<Employee> newEmployees); | 
Save | 
 update if exists, otherwise insert | @SaveProduct[] saveAll(Product... products) | 
Update | 
 updates an existing entity | @Updatepublic boolean modify(Product modifiedProduct); | 
Refer to the API documentation for Insert, Update, Delete,
 and Save for further information about these annotations.
JDQL query methods
The Query annotation specifies that a method executes a query written
 in Jakarta Data Query Language (JDQL) or Jakarta Persistence Query Language (JPQL).
 A Jakarta Data provider is not required to support the complete JPQL language,
 which targets relational data stores.
Each parameter of the annotated method must either:
- have exactly the same name (the parameter name in the Java source, or a name
     assigned by 
@Param) and type as a named parameter of the query, - have exactly the same type and position within the parameter list of the method as a positional parameter of the query, or
 - be of type 
Limit,Order,PageRequest, orSort. 
The Param annotation associates a method parameter with a named parameter.
 The Param annotation is unnecessary when the method parameter name matches the
 name of a named parameter and the application is compiled with the -parameters
 compiler option making parameter names available at runtime.
 // example using named parameters
 @Query("where age between :min and :max order by age")
 List<Person> peopleInAgeRange(int min, int max);
 
 
 // example using an ordinal parameter
 @Query("where ssn = ?1 and deceased = false")
 Optional<Person> person(String ssn);
 
 Refer to the API documentation for @Query for further
 information.
Query by Method Name
Repository methods following the Query by Method Name pattern
 must include the By keyword in the method name and must not include
 the @Find annotation, @Query annotation, or
 any lifecycle annotations on the method or any data access related annotations
 on the method parameters. Query conditions
 are determined by the portion of the method name following the By keyword.
| 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) | 
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
| 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, inclusive of the parameters. The minimum is listed first, then the maximum. | findByAgeBetween(minAge, maxAge) | 
 Key-value Wide-Column  | 
Contains | 
 strings | Requires that a substring of the entity's attribute value matches the parameter value, which can be a pattern. | findByNameContains(middleName) | 
 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  | 
| 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  | 
| 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, Empty
 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 Query by Method Name
The following is a table of valid return types.
 The Method column shows name patterns for Query by Method Name.
 For example, to identify the valid return types for a method,
 findNamed(String name, PageRequest pagination), refer to the row for
 find...By...(..., PageRequest).
| Method | Return Types | Notes | 
countBy... | 
 long,
 int | 
 Jakarta Persistence providers limit the maximum to Integer.MAX_VALUE | 
deleteBy...,
 updateBy... | 
 void,
 boolean,
 long,
 int | 
 Jakarta Persistence providers limit the maximum to Integer.MAX_VALUE | 
existsBy... | 
 boolean | 
 For determining existence. | 
find...By... | 
 E,
 Optional<E> | 
 For queries returning a single item (or none) | 
find...By... | 
 E[],
 List<E> | 
 For queries where it is possible to return more than 1 item. | 
find...By... | 
 Stream<E> | 
 The caller must arrange to close
 all streams that it obtains from repository methods. | 
find...By...(..., PageRequest) | 
 Page<E>, CursoredPage<E> | 
 For use with pagination | 
Parameter-based automatic query methods
The Find annotation indicates that the repository method is
 a parameter-based automatic query method. In this case, the method name
 does not determine the semantics of the method, and the query conditions
 are determined by the method parameters.
Each parameter of the annotated method must either:
- have exactly the same type and name (the parameter name in the Java
     source, or a name assigned by 
@By) as a persistent field or property of the entity class, or - be of type 
Limit,Sort,Order, orPageRequest. 
A parameter may be annotated with the By annotation to specify
 the name of the entity attribute that the argument is to be compared with.
 If the By annotation is missing, the method parameter name must
 match the name of an entity attribute and the repository must be compiled
 with the -parameters compiler option so that parameter names are
 available at runtime.
Each parameter determines a query condition, and each such condition is an equality condition. All conditions must match for a record to satisfy the query.
 @Find
 @OrderBy("lastName")
 @OrderBy("firstName")
 List<Person> peopleByAgeAndNationality(int age, Country nationality);
 
 @Find Optional<Person> person(String ssn);
The _ character may be used in a method parameter name to
 reference an embedded attribute.
 @Find
 @OrderBy("address.zip")
 Stream<Person> peopleInCity(String address_city);
 
 The following examples illustrate the difference between Query By Method Name and parameter-based automatic query methods. 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@FindVehicle[] searchFor(String make, String model, int year,Sort<?>...sorts);
For further information, refer to the API documentation
 for @Find.
Special parameters
A repository method annotated @Query, @Find or
 following the Query by Method Name pattern may have special
 parameters of type Limit, Order, Sort, or
 PageRequest if the method return type indicates that the method may
 return multiple entities. Special parameters occur after parameters related
 to query conditions and JDQL query parameters, and enable capabilities such
 as pagination, limits, and sorting.
Limits
The number of results returned by a single invocation of a repository
 find method may be limited by adding a parameter of type Limit.
 The results may even be limited to a positioned range. For example,
 @Query("WHERE (fullPrice - salePrice) / fullPrice >= ?1 ORDER BY 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
A repository find method with a parameter of type PageRequest
 allows its results to be split and retrieved in pages. For example,
 Product[] findByNameLikeOrderByAmountSoldDescNameAsc(
           String pattern, PageRequest<Product> pageRequest);
 ...
 page1 = products.findByNameLikeOrderByAmountSoldDescNameAsc(
                  "%phone%", PageRequest.of(Product.class).size(20));
 
 Sorting
When a page is requested with a PageRequest, dynamic sorting
 criteria may be supplied via the method PageRequest.sortBy(Sort)
 and its overloads. For example,
Product[] findByNameLike(String pattern,PageRequest<Product>pagination); ...PageRequest<Product>page1Request = PageRequest.of(Product.class) .size(25) .sortBy(Sort.desc("price"), Sort.asc("name")); page1 = products.findByNameLikeAndPriceBetween( namePattern, minPrice, maxPrice, page1Request);
An alternative when using the StaticMetamodel is to obtain the
 page request from an Order instance, as follows,
 PageRequest<Product> pageRequest = Order.by(_Product.price.desc(),
                                             _Product.name.asc())
                                         .pageSize(25));
 
 To supply sort criteria dynamically without using pagination, an
 instance of Order may be populated with one or more instances
 of Sort and passed to the repository find method. For example,
 Product[] findByNameLike(String pattern, Limit max, Order<Product> sortBy);
 ...
 found = products.findByNameLike(namePattern, Limit.of(25), Order.by(
                                 Sort.desc("price"),
                                 Sort.desc("amountSold"),
                                 Sort.asc("name")));
 
 Generic, untyped Sort criteria can be supplied directly to a
 repository method with a variable arguments Sort<?>... parameter.
 For example,
 Product[] findByNameLike(String pattern, Limit max, Sort<?>... sortBy);
 ...
 found = products.findByNameLike(namePattern, Limit.of(25),
                                 Sort.desc("price"),
                                 Sort.desc("amountSold"),
                                 Sort.asc("name"));
 
 Repository default methods
A repository interface may declare any number of default methods
 with user-written implementations.
Resource accessor methods
In advanced scenarios, the application program might make direct use of
 some underlying resource acquired by the Jakarta Data provider, such as a
 javax.sql.DataSource, java.sql.Connection, or even an
 jakarta.persistence.EntityManager.
To expose access to an instance of such a resource, the repository interface may declare an accessor method, a method with no parameters whose return type is the type of the resource, for example, one of the types listed above. When this method is called, 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 the
 accessor method is called from within an invocation of a default method
 of the repository, the Jakarta Data provider automatically closes the
 resource after the invocation of the default method ends. On the other
 hand, if the accessor method is called from outside the scope of a
 default method of the repository, it is not automatically closed, and
 the application programmer is responsible for closing the resource
 instance.
Precedence of repository methods
The following order, with the lower number having higher precedence, is used to interpret the meaning of repository methods.
- If the method is a Java 
defaultmethod, then the provided implementation is used. - If a method has a resource accessor method return type recognized by the Jakarta Data provider, then the method is implemented as a resource accessor method.
 - If a method is annotated with a query annotation
     recognized by the Jakarta Data provider, such as 
Query, then the method is implemented to execute the query specified by the query annotation. - If the method is annotated with an automatic query annotation,
     such as 
Find, or with a lifecycle annotation declaring the type of operation, for example, withInsert,Update,Save, orDelete, and the provider recognizes the annotation, then the annotation determines how the method is implemented. - If a method is named according to the conventions of Query by Method Name, then the implementation follows the Query by Method Name pattern.
 
A repository method which does not fit any of the listed patterns
 and is not handled as a vendor-specific extension must either cause
 an error at build time or raise UnsupportedOperationException
 at runtime.
Identifying the type of entity
Most repository methods perform operations related to a type of entity.
 In some cases, the entity type is explicit within the signature of the
 repository method, and in other cases, such as countBy... and
 existsBy... the entity type cannot be determined from the method
 signature and a primary entity type must be defined for the repository.
Methods where the entity type is explicitly specified
In the following cases, the entity type is determined by the signature of the repository method.
- For repository methods annotated with 
Insert,Update,Save, orDeletewhere the method parameter type is a type, an array of a type, or is parameterized with a type annotated as an entity, such asMyEntity,MyEntity[], orList<MyEntity>, the entity type is determined by the method parameter type. - For 
findanddeletemethods where the return type is a type, an array of a type, or is parameterized with a type annotated as an entity, such asMyEntity,MyEntity[], orPage<MyEntity>, the entity type is determined by the method return type. 
Identifying a primary entity type:
The following precedence, from highest to lowest, is used to determine a primary entity type for a repository.
- The primary entity type for a repository interface may be specified
 explicitly by having the repository interface inherit a superinterface
 like 
CrudRepository, where the primary entity type is the argument to the first type parameter of the superinterface. For example,Product, in,@Repository public interface Products extends CrudRepository<Product, Long> { // applies to the primary entity type: Product int countByPriceLessThan(float max); } - Otherwise, if the repository declares lifecycle methods—that is,
 has methods annotated with a lifecycle annotation like 
Insert,Update,Save, orDelete, where the method parameter type is a type, an array of a type, or is parameterized with a type annotated as an entity—and all of these methods share the same entity type, then the primary entity type for the repository is that entity type. For example,@Repository public interface Products { @Insert List<Product> add(List<Product> p); @Update Product modify(Product p); @Save Product[] save(Product... p); // applies to the primary entity type: Product boolean existsByName(String name); } 
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);
     @Save
     void save(@Valid Contact c);
 }
 @Entity
 public class Contact {
     @Email
     @NotNull
     public String email;
     @Id
     public long id;
     ...
 }
 
 Jakarta Interceptors
A repository interface or method of a repository interface may be annotated with an
 interceptor binding annotation. In the Jakarta EE environment, or in any other environment
 where Jakarta Interceptors is available and integrated with Jakarta CDI, the repository
 implementation is instantiated by the CDI bean container, and the interceptor binding type
 is declared @Inherited, the interceptor binding annotation is inherited by the
 repository implementation, and the interceptors bound to the annotation are applied
 automatically by the implementation of Jakarta Interceptors.
Jakarta Transactions
When Jakarta Transactions is available, repository methods can participate in global transactions. If a global transaction is active on the thread of execution in which a repository method is called, and the data source backing the repository is capable of transaction enlistment, then the repository operation is performed within the context of the global transaction.
The repository operation must not not commit or roll back a transaction which was
 already associated with the thread in which the repository operation was called, but it
 might cause the transaction to be marked for rollback if the repository operation fails,
 that is, it may set the transaction status to Status.STATUS_MARKED_ROLLBACK.
A repository interface or method of a repository interface may be marked with the
 annotation jakarta.transaction.Transactional. When a repository operation marked
 @Transactional is called in an environment where both Jakarta Transactions and
 Jakarta CDI are available, the semantics of this annotation are observed during execution
 of the repository operation.
- 
Packages
PackageExported To ModulesOpened To ModulesDescriptionAll ModulesNoneJakarta Data provides an API that simplifies data access.All ModulesNoneCommon data access exceptions.All ModulesNoneA static metamodel for entities that are used in Jakarta Data repositories.All ModulesNoneAll ModulesNoneSplits query results into pages.All ModulesNoneAll ModulesAll ModulesA repository is an interface annotated withRepositorythat defines operations on entities.