pom.xml

<dependencies>
  <dependency>
    <groupId>org.hibernate.orm</groupId>
    <artifactId>hibernate-jpamodelgen</artifactId>
    <scope>provided</scope>
  </dependency>
</dependencies>

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>${java.version}</source>
        <target>${java.version}</target>
        <annotationProcessorPaths>
          <!-- Path to Lombok processor -->
          <!-- ... -->

          <!-- Path to Mapstruct processor -->
          <!-- ... -->

          <!-- Path to Hibernate JPA Metamodel processor -->
          <path>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-jpamodelgen</artifactId>
            <version>${hibernate.version}</version>
          </path>
        </annotationProcessorPaths>
      </configuration>
    </plugin>
  </plugins>
</build>

annotationProcessorPaths order matters, place hibernate-jpamodelgen processor after Lombok’s.

Usage

mvn clean package
...
[INFO] Hibernate compile-time tooling 6.6.36.Final
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
...


ls target/generated-sources/annotations/com/asimiotech/demo/domain/
ActorInfoId_.java		Country_.java			FilmCategoryId_.java		Language_.java			SalesByFilmCategory_.java	Store_.java
ActorInfo_.java			CustomerListId_.java		FilmCategory_.java		NicerButSlowerFilmListId_.java	SalesByStoreId_.java
Actor_.java			CustomerList_.java		FilmListId_.java		NicerButSlowerFilmList_.java	SalesByStore_.java
Address_.java			Customer_.java			FilmList_.java			Payment_.java			StaffListId_.java
Category_.java			FilmActorId_.java		Film_.java			Rental_.java			StaffList_.java
City_.java			FilmActor_.java			Inventory_.java			SalesByFilmCategoryId_.java	Staff_.java


Generated JPA Metamodel class example:

cat target/generated-sources/annotations/com/asimiotech/demo/domain/Film_.java
...

@StaticMetamodel(Film.class)
@Generated("org.hibernate.processor.HibernateProcessor")
public abstract class Film_ {

	public static final String RENTAL_RATE = "rentalRate";
	public static final String RENTAL_DURATION = "rentalDuration";
	public static final String FILM_CATEGORIES = "filmCategories";
	public static final String LENGTH = "length";
	public static final String RATING = "rating";
	public static final String DESCRIPTION = "description";
	public static final String REPLACEMENT_COST = "replacementCost";
	public static final String LANGUAGE = "language";
	public static final String TITLE = "title";
	public static final String FILM_ACTORS = "filmActors";
	public static final String SPECIAL_FEATURES = "specialFeatures";
	public static final String INVENTORIES = "inventories";
	public static final String FILM_ID = "filmId";
	public static final String LAST_UPDATE = "lastUpdate";
	public static final String FULLTEXT = "fulltext";
	public static final String RELEASE_YEAR = "releaseYear";


	/**
	 * @see com.asimiotech.demo.domain.Film#rentalRate
	 **/
	public static volatile SingularAttribute<Film, BigDecimal> rentalRate;

	/**
	 * @see com.asimiotech.demo.domain.Film#rentalDuration
	 **/
	public static volatile SingularAttribute<Film, Short> rentalDuration;

	/**
	 * @see com.asimiotech.demo.domain.Film#filmCategories
	 **/
	public static volatile SetAttribute<Film, FilmCategory> filmCategories;

	/**
	 * @see com.asimiotech.demo.domain.Film#length
	 **/
	public static volatile SingularAttribute<Film, Short> length;

	/**
	 * @see com.asimiotech.demo.domain.Film#rating
	 **/
	public static volatile SingularAttribute<Film, String> rating;

	/**
	 * @see com.asimiotech.demo.domain.Film#description
	 **/
	public static volatile SingularAttribute<Film, String> description;

// ...


Writing JPA/Hibernate type-safe queries with Criteria API and Metamodel generated classes:

FilmSpecifications.java

public final class FilmSpecifications {

  private FilmSpecifications() {
  }

  public static Specification<Film> createFilmSpecifications(FilmSearchCriteria searchCriteria) {
    return rentalRateBetween(searchCriteria.getMinRentalRate(), searchCriteria.getMaxRentalRate())
      .and(releaseYearEqualTo(searchCriteria.getReleaseYear()))
      .and(categoryIn(searchCriteria.getCategories()));
  }

  public static Specification<Film> rentalRateBetween(Optional<BigDecimal> minRate, Optional<BigDecimal> maxRate) {
    return (root, query, builder) -> {
      return minRate.map(min -> {
        return maxRate.map(max -> builder.between(root.get(Film_.rentalRate), min, max)
            ).orElse(null);
      }).orElse(null);
    };
  }

  public static Specification<Film> releaseYearEqualTo(Optional<Long> releaseYear) {
    return (root, query, builder) -> {
      return releaseYear.map(relYear -> builder.equal(root.get(Film_.releaseYear), String.valueOf(relYear))
        ).orElse(null);
    };
  }

  public static Specification<Film> categoryIn(Set<String> categories) {
    if (CollectionUtils.isEmpty(categories)) {
      return null;
    }
    return (root, query, builder) -> {
      Join<Film, FilmCategory> filmCategoryJoin = root.join(Film_.filmCategories);
      Join<FilmCategory, Category> categoryJoin = filmCategoryJoin.join(FilmCategory_.category);
      return categoryJoin.get(Category_.name).in(categories);
    };
  }

}

No need to copy the Metamodel classes to the project.