back to all blogsSee all blog posts

Jakarta Data 1.1 Beta Features and MicroProfile Health Endpoint Controls in 26.0.0.7-beta

image of author
Navaneeth S Nair on Jun 30, 2026
Post available in languages:

This beta introduces new Jakarta Data 1.1 beta capabilities, including stateful repositories, enhanced query restrictions, and the @First annotation. It also adds configuration-based control over MicroProfile Health REST endpoints when file-based health checks are enabled.

The Open Liberty 26.0.0.7-beta includes the following beta features (along with all GA features):

Preview of some Jakarta Data 1.1 M3 capabilities

The beta release previews some new capabilities at the Jakarta Data 1.1 Milestone 3 level:

  • Stateful repositories

  • The @First annotation

  • Restriction parameters on repository @Find and @Delete methods

  • Constraint subtype parameters on repository @Find and @Delete methods

Also included in prior betas are:

  • Retrieving a subset or projection of entity attributes

  • The @Is annotation

Previously, parameter-based @Find and @Delete methods had no way to filter on conditions other than equality. This limitation can now be overcome by typing the repository method parameter with a Constraint subtype or by indicating the Constraint subtype using the @Is annotation. Alternatively, the repository method can include a special parameter of type Restriction, allowing the application to supply one or more restrictions at run time when it invokes the repository method.

Prior to this release, all Jakarta Data repositories were stateless, operating without externalizing a persistence context to the application. Now, you can define a repository as stateful by using the repository lifecycle method annotations in the jakarta.data.repository.stateful package, enabling the repository to operate with a persistence context.

Previously, repository Find methods could not enforce returning only the first result or the first few results. That capability is now available with the @First annotation.

In Jakarta Data, you write simple Java objects that are called Entities to represent data, and interfaces that are called Repositories to define operations on data. You inject a Repository into your application and use it. The implementation of the Repository is automatically provided for you!

Start by defining an entity class that corresponds to your data. With relational databases, the entity class corresponds to a database table. The entity properties, which include the public methods and fields of the entity class, generally correspond to the columns of the table.

An entity class can be:

  • annotated with jakarta.persistence.Entity and related annotations from Jakarta Persistence.

  • a Java class without entity annotations, in which case the primary key is inferred from an entity property named id or ending with Id. Entity property named version designates an automatically incremented version column.

The following shows an example of a simple entity:

@Entity
public class Product {
    @Id
    public long id;

    public String name;

    public boolean onSale;

    public double price;

    public double weight;
}

After you define the entity to represent the data, it is usually helpful to have your IDE generate a static metamodel class for it. By convention, static metamodel classes begin with the underscore character, followed by the entity name. Because this beta is being made available well before the release of Jakarta Data 1.1, we cannot expect IDEs for this generation yet. However, we can provide the static metamodel class that an IDE would be expected to generate for the Product entity:

@StaticMetamodel(Product.class)
public interface _Product {
    String ID = "id";
    String NAME = "name";
    String ONSALE = "onSale";
    String PRICE = "price";
    String WEIGHT = "weight";

    NumericAttribute<Product, Long> id = NumericAttribute.of(
            Product.class, ID, long.class);
    TextAttribute<Product> name = TextAttribute.of(
            Product.class, NAME);
    BooleanAttribute<Product> onSale = BooleanAttribute.of(
            Product.class, ONSALE, boolean.class);
    NumericAttribute<Product, Double> price = NumericAttribute.of(
            Product.class, PRICE, double.class);
    NumericAttribute<Product, Double> weight = NumericAttribute.of(
            Product.class, WEIGHT, double.class);
}

The first half of the static metamodel class includes constants for each of the entity attribute names so that you don’t need to otherwise hardcode string values into your application. The second half of the static metamodel class provides a special instance for each entity attribute, from which you can build restrictions and sorting to apply to queries at run time. These repository queries enable powerful and flexible data operations.

Built-in interfaces like BasicRepository and CrudRepository can be extended to add general-purpose methods for inserting, updating, deleting, and querying entities. To compose a stateful repository, extend the built-in DataRepository interface. This interface allows you to define at least one stateful repository method using the @Detach, @Merge, @Persist, @Refresh, or @Remove annotations. Once the repository is stateful, any find methods you define will return entities that operate within a persistence context.

@Repository(dataStore = "java:app/jdbc/my-example-data")
public interface Products extends DataRepository<Product, Long> {

    // Filtering is pre-defined at development time by the @Is annotation,
    @Find
    @First
    @OrderBy(_Product.PRICE)
    @OrderBy(_Product.NAME)
    Optional<Product> leastExpensiveUpTo(
        @By(_Product.PRICE) @Is(AtMost.class) double maxPrice,
        @By(_Product.NAME) @Is(Like.class) String namePattern);

    // Constraint types, such as Like, can also pre-define filtering.
    // Allow additional filtering at run time by including a Restriction,
    @Find
    Page<Product> named(@By(_Product.NAME) Like namePattern,
                        Restriction<Product> filter,
                        Order<Product> sorting,
                        PageRequest pageRequest);

    @Persist
    @Transactional
    void persist(Product product);

    // Retrieve a single entity attribute, identified by @Select,
    @Find
    @Select(_Product.PRICE)
    Optional<Double> priceOf(@By(_Product.ID) long productNum);

    // Retrieve multiple entity attributes as a Java record,
    @Find
    @Select(_Product.WEIGHT)
    @Select(_Product.NAME)
    Optional<WeightInfo> weightAndNameOf(@By(_Product.ID) long productNum);

    static record WeightInfo(double itemWeight, String itemName) {}
}

Following is an example of using the stateful repository and static metamodel.

@DataSourceDefinition(name = "java:app/jdbc/my-example-data",
                      className = "org.postgresql.xa.PGXADataSource",
                      databaseName = "ExampleDB",
                      serverName = "localhost",
                      portNumber = 5432,
                      user = "${example.database.user}",
                      password = "${example.database.password}")
public class MyServlet extends HttpServlet {
    @Inject
    Products products;

    @Inject
    UserTransaction tx;

    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        // Persist:
        Product prod = Product.of(1, name, false, 99.99, 10.5);
        products.persist(prod);

        // Filter by supplying values only:
        tx.begin();
        products.leastExpensiveUpTo(1000.00, "%computer%")
                .ifPresent(p -> {
                        p.setOnSale(false);
                        p.setPrice(p.getPrice() + 50.00);
                });
        tx.commit();

        // Compute filtering at run time,
        Page<Product> page1 = products.named(
            Like.suffix("printer"),
            Restrict.all(_Product.price.times(taxRate).plus(shipping).lessThan(250.0),
                        _Product.weight.times(kgToPounds).between(10.0, 20.0)),
            Order.by(_Product.price.desc(),
                     _Product.name.asc(),
                     _Product.id.asc()),
            PageRequest.ofSize(10));

        // Find one entity attribute:
        price = products.priceOf(prod.id).orElseThrow();

        // Find multiple entity attributes as a Java record:
        products.weightAndNameOf(prod.id).ifPresent(info ->
                System.out.println(info.itemName() + " weighs " +
                                   info.itemWeight() + " kg.")
        );
    }
}

To learn more about this feature, see the following resources:

<dependency>
    <groupId>jakarta.data</groupId>
    <artifactId>jakarta.data-api</artifactId>
    <version>1.1.0-M3</version>
</dependency>

Disabling mpHealth-4.0 REST endpoints when file-based health checks are enabled

The mpHealth-4.0 feature enables support for MicroProfile Health 4.0 in Open Liberty. The feature also includes a file-based health check mechanism as an alternative to the /health REST endpoints. When the file-based health check mechanism is enabled, you can now disable the /health/started, /health/live, and /health/ready REST endpoints using server configuration attributes.

Previously, when file-based health checks were enabled, the REST-based MicroProfile Health endpoints were still registered and accessible. This behavior was often redundant for Kubernetes deployments that rely on file-based health checks, where users typically configure probes using the exec strategy rather than httpGet or gRPC. In these scenarios, the REST health endpoints are not used.

To address this redundancy, the beta release introduces a new optional mpHealth server configuration attribute that allows REST health endpoints to be disabled when file-based health checks are enabled.

The mpHealth-4.0 feature adds a new optional enableEndpoints="false" attribute to the mpHealth server configuration element in server.xml file. An equivalent environment variable, MP_HEALTH_ENABLE_ENDPOINTS=false, is also supported.

When this option is set to false and file-based health checks are enabled, the server does not register or expose the MicroProfile Health REST endpoints (/health, /health/live, /health/ready, /health/started). Requests to these endpoints return '404 Not Found' response.

File-based health checks can be enabled with the checkInterval configuration attribute or the MP_HEALTH_CHECK_INTERVAL environment variable.

If both the environment variable and server.xml file configuration are specified, the server.xml setting takes precedence at runtime.

<server>
    <featureManager>
        <feature>mpHealth-4.0</feature>
    </featureManager>

    <!-- Enable file-based health checks but disable HTTP endpoints -->
    <mpHealth startupCheckInterval="1s" checkInterval="5s" enableEndpoints="false" />
 ...
</server>

If either the enableEndpoints configuration attribute or the MP_HEALTH_ENABLE_ENDPOINTS environment variable is not set, the default value is true, and the health check REST endpoints are enabled and exposed.

Note: This capability is supported only when the file‑based health check mechanism is enabled. If file-based health checks are not enabled and the new enableEndpoints configuration attribute or environment variable is specified, a warning message is logged. The REST health endpoints remain enabled by default.

For more information, see the documentation.

Try it now

To try out these features, update your build tools to pull the Open Liberty All Beta Features package instead of the main release. The beta works with Java SE 21, Java SE 17, Java SE 11, and Java SE 8.

If you’re using Maven, you can install the All Beta Features package using:

<plugin>
    <groupId>io.openliberty.tools</groupId>
    <artifactId>liberty-maven-plugin</artifactId>
    <version>3.12.0</version>
    <configuration>
        <runtimeArtifact>
          <groupId>io.openliberty.beta</groupId>
          <artifactId>openliberty-runtime</artifactId>
          <version>26.0.0.7-beta</version>
          <type>zip</type>
        </runtimeArtifact>
    </configuration>
</plugin>

You must also add dependencies to your pom.xml file for the beta version of the APIs that are associated with the beta features that you want to try. For example, the following block adds dependencies for two example beta APIs:

<dependency>
    <groupId>org.example.spec</groupId>
    <artifactId>exampleApi</artifactId>
    <version>7.0</version>
    <type>pom</type>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>example.platform</groupId>
    <artifactId>example.example-api</artifactId>
    <version>11.0.0</version>
    <scope>provided</scope>
</dependency>

Or for Gradle:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'io.openliberty.tools:liberty-gradle-plugin:4.0.0'
    }
}
apply plugin: 'liberty'
dependencies {
    libertyRuntime group: 'io.openliberty.beta', name: 'openliberty-runtime', version: '[26.0.0.7-beta,)'
}

Or if you’re using container images:

FROM icr.io/appcafe/open-liberty:beta

Or take a look at our Downloads page.

If you’re using IntelliJ IDEA, Visual Studio Code or Eclipse IDE, you can also take advantage of our open source Liberty developer tools to enable effective development, testing, debugging and application management all from within your IDE.

For more information on using a beta release, refer to the Installing Open Liberty beta releases documentation.

We welcome your feedback

Let us know what you think on our mailing list. If you hit a problem, post a question on StackOverflow. If you hit a bug, please raise an issue.