diff --git a/metamodel/generator-plugin/pom.xml b/metamodel/generator-plugin/pom.xml
new file mode 100644
index 00000000000..c5b584394aa
--- /dev/null
+++ b/metamodel/generator-plugin/pom.xml
@@ -0,0 +1,109 @@
+
+ 4.0.0
+
+ org.hibernate.search
+ hibernate-search-parent-public
+ 7.2.0-SNAPSHOT
+ ../../build/parents/public
+
+ hibernate-search-metamodel-generator-plugin
+ maven-plugin
+
+ Hibernate Search Metamodel Generator Plugin
+ Hibernate Search Metamodel Generator Plugin, creates static metamodel classes
+
+
+
+ false
+ true
+ org.hibernate.search.metamodel.generator.plugin
+
+
+
+
+ org.hibernate.search
+ hibernate-search-engine
+
+
+ org.hibernate.search
+ hibernate-search-mapper-orm
+ true
+
+
+ org.hibernate.search
+ hibernate-search-mapper-pojo-standalone
+ true
+
+
+ org.jboss.logging
+ jboss-logging
+
+
+ org.jboss.logging
+ jboss-logging-annotations
+
+
+
+ org.hibernate.search
+ hibernate-search-util-internal-integrationtest-mapper-orm
+
+
+
+ org.apache.maven
+ maven-plugin-api
+ ${maven.min.version}
+
+
+ org.apache.maven
+ maven-core
+ ${maven.min.version}
+
+
+ org.apache.maven.plugin-tools
+ maven-plugin-annotations
+ 3.13.0
+ provided
+
+
+ org.codehaus.plexus
+ plexus-utils
+ 3.5.1
+
+
+
+ org.hibernate.search
+ hibernate-search-util-internal-test-common
+
+
+ org.hibernate.search
+ hibernate-search-util-internal-integrationtest-backend-lucene
+
+
+
+ com.h2database
+ h2
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.moditect
+ moditect-maven-plugin
+
+
+ add-module-infos
+
+ none
+
+
+
+
+
+
diff --git a/metamodel/generator-plugin/src/main/java/org/hibernate/search/metamodel/generator/plugin/HibernateSearchMetamodelGeneratorMojo.java b/metamodel/generator-plugin/src/main/java/org/hibernate/search/metamodel/generator/plugin/HibernateSearchMetamodelGeneratorMojo.java
new file mode 100644
index 00000000000..7ed7ee193cf
--- /dev/null
+++ b/metamodel/generator-plugin/src/main/java/org/hibernate/search/metamodel/generator/plugin/HibernateSearchMetamodelGeneratorMojo.java
@@ -0,0 +1,135 @@
+/*
+ * Hibernate Search, full-text search for your domain model
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later
+ * See the lgpl.txt file in the root directory or .
+ */
+package org.hibernate.search.metamodel.generator.plugin;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Properties;
+
+import org.hibernate.SessionFactory;
+import org.hibernate.search.engine.backend.metamodel.IndexDescriptor;
+import org.hibernate.search.engine.backend.metamodel.IndexFieldDescriptor;
+import org.hibernate.search.mapper.orm.Search;
+import org.hibernate.search.mapper.orm.entity.SearchIndexedEntity;
+import org.hibernate.search.mapper.orm.mapping.SearchMapping;
+import org.hibernate.search.util.impl.integrationtest.backend.lucene.LuceneBackendConfiguration;
+import org.hibernate.search.util.impl.integrationtest.mapper.orm.OrmSetupHelper;
+
+import org.apache.maven.model.Dependency;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+
+@Mojo(name = "generate-metamodel", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
+public class HibernateSearchMetamodelGeneratorMojo extends AbstractMojo {
+
+ @Parameter(defaultValue = "${project}", required = true, readonly = true)
+ MavenProject project;
+
+ @Parameter(property = "annotatedTypes")
+ List annotatedTypes;
+
+ @Parameter(property = "properties")
+ Properties properties;
+
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ getLog().info( "Hibernate Search Metamodel Generator" );
+ getLog().info( "Dependencies: " + project.getDependencies() );
+
+ if ( hasOrmMapper( project.getDependencies() ) ) {
+ getLog().info( "Sources: " + project.getCompileSourceRoots() );
+ OrmSetupHelper.SetupContext setupContext =
+ OrmSetupHelper.withSingleBackend( new LuceneBackendConfiguration() ).start();
+
+ properties.forEach( (k, v) -> setupContext.withProperty( Objects.toString( k ), v ) );
+
+ Path generatedMetamodelLocation =
+ Path.of( project.getBuild().getOutputDirectory() ).resolveSibling( "generated-metamodel-sources" );
+ project.addCompileSourceRoot( generatedMetamodelLocation.toString() );
+
+
+ try ( SessionFactory sessionFactory = setupContext.setup( annotatedTypes() ) ) {
+ SearchMapping mapping = Search.mapping( sessionFactory );
+
+ Collection extends SearchIndexedEntity>> indexedEntities = mapping.allIndexedEntities();
+
+ for ( SearchIndexedEntity> indexedEntity : indexedEntities ) {
+ createClass( indexedEntity, generatedMetamodelLocation );
+ }
+
+ getLog().info( "Indexed entities: " + indexedEntities );
+
+ }
+
+ }
+ }
+
+ private void createClass(SearchIndexedEntity> indexedEntity, Path root) {
+ getLog().info( "Creating class for entity: " + indexedEntity.jpaName() );
+
+ IndexDescriptor descriptor = indexedEntity.indexManager().descriptor();
+
+ StringBuilder fields = new StringBuilder();
+
+ for ( IndexFieldDescriptor staticField : descriptor.staticFields() ) {
+ fields.append( '\n' )
+ .append( '\t' ).append( "public String " ).append( staticField.relativeName() ).append( ";" );
+ }
+
+ try {
+ Class> javaClass = indexedEntity.javaClass();
+ Path pckg = root.resolve( Path.of( javaClass.getPackageName().replace( '.', '/' ) ) );
+ Files.createDirectories( pckg );
+ try ( FileOutputStream os =
+ new FileOutputStream( pckg.resolve( javaClass.getSimpleName() + ".java" ).toFile() ); ) {
+ os.write( new StringBuilder().append( "package " ).append( javaClass.getPackageName() ).append( ";\n\n" )
+ .append( "class " ).append( javaClass.getSimpleName() ).append( "__ {\n" )
+ .append( fields )
+ .append( "\t\n}" )
+ .toString().getBytes( StandardCharsets.UTF_8 ) );
+ }
+
+ }
+ catch (IOException e) {
+ throw new RuntimeException( e );
+ }
+
+ }
+
+ private Class>[] annotatedTypes() {
+ try {
+ Class>[] types = new Class>[annotatedTypes.size()];
+ for ( int i = 0; i < annotatedTypes.size(); i++ ) {
+ types[i] = Class.forName( annotatedTypes.get( i ) );
+ }
+ return types;
+ }
+ catch (ClassNotFoundException e) {
+ throw new RuntimeException( e );
+ }
+ }
+
+ private boolean hasOrmMapper(List dependencies) {
+ for ( Dependency dependency : dependencies ) {
+ if ( "hibernate-search-mapper-orm".equals( dependency.getArtifactId() ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/metamodel/generator-test/generation/pom.xml b/metamodel/generator-test/generation/pom.xml
new file mode 100644
index 00000000000..7ace37ce175
--- /dev/null
+++ b/metamodel/generator-test/generation/pom.xml
@@ -0,0 +1,82 @@
+
+ 4.0.0
+
+ org.hibernate.search
+ hibernate-search-metamodel-generator-test-parent
+ 7.2.0-SNAPSHOT
+ ..
+
+ hibernate-search-metamodel-generator-test-generation
+
+ Hibernate Search Metamodel Generator TEST
+
+
+
+ false
+ org.hibernate.search.metamodel.generator.test
+ h2
+
+
+
+
+ org.hibernate.search
+ hibernate-search-metamodel-generator-model
+ ${project.version}
+
+
+ org.hibernate.search
+ hibernate-search-mapper-orm
+
+
+
+
+
+
+ org.hibernate.search
+ hibernate-search-metamodel-generator-plugin
+ ${project.version}
+
+
+ generate
+
+ generate-metamodel
+
+ generate-sources
+
+
+ org.hibernate.search.metamodel.generator.model.MyEntity
+ org.hibernate.search.metamodel.generator.model.MyProgrammaticEntity
+
+
+ org.hibernate.search.metamodel.generator.model.MyConfigurer
+
+
+
+
+
+
+ org.hibernate.search
+ hibernate-search-metamodel-generator-model
+ ${project.version}
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.moditect
+ moditect-maven-plugin
+
+
+ add-module-infos
+
+ none
+
+
+
+
+
+
diff --git a/metamodel/generator-test/model/pom.xml b/metamodel/generator-test/model/pom.xml
new file mode 100644
index 00000000000..69b0abaae2a
--- /dev/null
+++ b/metamodel/generator-test/model/pom.xml
@@ -0,0 +1,70 @@
+
+ 4.0.0
+
+ org.hibernate.search
+ hibernate-search-metamodel-generator-test-parent
+ 7.2.0-SNAPSHOT
+ ..
+
+ hibernate-search-metamodel-generator-model
+
+ Hibernate Search Metamodel Generator Model
+
+
+
+ false
+ org.hibernate.search.metamodel.generator
+
+
+
+
+ org.hibernate.search
+ hibernate-search-engine
+
+
+ org.hibernate.search
+ hibernate-search-mapper-orm
+ true
+
+
+ org.hibernate.search
+ hibernate-search-mapper-pojo-standalone
+ true
+
+
+ org.jboss.logging
+ jboss-logging
+
+
+ org.jboss.logging
+ jboss-logging-annotations
+
+
+
+ org.hibernate.search
+ hibernate-search-util-internal-test-common
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ org.moditect
+ moditect-maven-plugin
+
+
+ add-module-infos
+
+ none
+
+
+
+
+
+
diff --git a/metamodel/generator-test/model/src/main/java/org/hibernate/search/metamodel/generator/model/MyConfigurer.java b/metamodel/generator-test/model/src/main/java/org/hibernate/search/metamodel/generator/model/MyConfigurer.java
new file mode 100644
index 00000000000..b2a031f0156
--- /dev/null
+++ b/metamodel/generator-test/model/src/main/java/org/hibernate/search/metamodel/generator/model/MyConfigurer.java
@@ -0,0 +1,28 @@
+/*
+ * Hibernate Search, full-text search for your domain model
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later
+ * See the lgpl.txt file in the root directory or .
+ */
+package org.hibernate.search.metamodel.generator.model;
+
+import org.hibernate.search.engine.backend.types.Projectable;
+import org.hibernate.search.engine.backend.types.Sortable;
+import org.hibernate.search.mapper.orm.mapping.HibernateOrmMappingConfigurationContext;
+import org.hibernate.search.mapper.orm.mapping.HibernateOrmSearchMappingConfigurer;
+import org.hibernate.search.mapper.pojo.mapping.definition.programmatic.ProgrammaticMappingConfigurationContext;
+import org.hibernate.search.mapper.pojo.mapping.definition.programmatic.TypeMappingStep;
+
+public class MyConfigurer implements HibernateOrmSearchMappingConfigurer {
+ @Override
+ public void configure(HibernateOrmMappingConfigurationContext context) {
+ ProgrammaticMappingConfigurationContext mapping = context.programmaticMapping();
+ TypeMappingStep bookMapping = mapping.type( MyProgrammaticEntity.class );
+ bookMapping.indexed();
+ bookMapping.property( "text" )
+ .fullTextField()
+ .projectable( Projectable.YES );
+ bookMapping.property( "number" )
+ .genericField().sortable( Sortable.YES );
+ }
+}
diff --git a/metamodel/generator-test/model/src/main/java/org/hibernate/search/metamodel/generator/model/MyEntity.java b/metamodel/generator-test/model/src/main/java/org/hibernate/search/metamodel/generator/model/MyEntity.java
new file mode 100644
index 00000000000..04d9416076b
--- /dev/null
+++ b/metamodel/generator-test/model/src/main/java/org/hibernate/search/metamodel/generator/model/MyEntity.java
@@ -0,0 +1,23 @@
+/*
+ * Hibernate Search, full-text search for your domain model
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later
+ * See the lgpl.txt file in the root directory or .
+ */
+package org.hibernate.search.metamodel.generator.model;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+
+import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
+import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
+
+@Indexed
+@Entity
+public class MyEntity {
+ @Id
+ Long id;
+
+ @FullTextField
+ String text;
+}
diff --git a/metamodel/generator-test/model/src/main/java/org/hibernate/search/metamodel/generator/model/MyProgrammaticEntity.java b/metamodel/generator-test/model/src/main/java/org/hibernate/search/metamodel/generator/model/MyProgrammaticEntity.java
new file mode 100644
index 00000000000..3d715e2da4e
--- /dev/null
+++ b/metamodel/generator-test/model/src/main/java/org/hibernate/search/metamodel/generator/model/MyProgrammaticEntity.java
@@ -0,0 +1,20 @@
+/*
+ * Hibernate Search, full-text search for your domain model
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later
+ * See the lgpl.txt file in the root directory or .
+ */
+package org.hibernate.search.metamodel.generator.model;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+
+@Entity
+public class MyProgrammaticEntity {
+ @Id
+ Long id;
+
+ String text;
+
+ Integer number;
+}
diff --git a/metamodel/generator-test/pom.xml b/metamodel/generator-test/pom.xml
new file mode 100644
index 00000000000..dc1884f4985
--- /dev/null
+++ b/metamodel/generator-test/pom.xml
@@ -0,0 +1,21 @@
+
+ 4.0.0
+
+ org.hibernate.search
+ hibernate-search-parent-public
+ 7.2.0-SNAPSHOT
+ ../../build/parents/public
+
+ pom
+ hibernate-search-metamodel-generator-test-parent
+
+
+ false
+
+
+
+ model
+ generation
+
+
diff --git a/pom.xml b/pom.xml
index 6e977aeb25d..456950cda08 100644
--- a/pom.xml
+++ b/pom.xml
@@ -187,6 +187,8 @@
build/parents/integrationtest
build/parents/springtest
integrationtest
+ metamodel/generator-test
+ metamodel/generator-plugin
documentation
diff --git a/util/internal/integrationtest/common/src/main/java/org/hibernate/search/util/impl/integrationtest/common/TestConfigurationProvider.java b/util/internal/integrationtest/common/src/main/java/org/hibernate/search/util/impl/integrationtest/common/TestConfigurationProvider.java
index b94a2d09493..36a7fa76e25 100644
--- a/util/internal/integrationtest/common/src/main/java/org/hibernate/search/util/impl/integrationtest/common/TestConfigurationProvider.java
+++ b/util/internal/integrationtest/common/src/main/java/org/hibernate/search/util/impl/integrationtest/common/TestConfigurationProvider.java
@@ -38,7 +38,7 @@ public final class TestConfigurationProvider
private static final String STARTUP_TIMESTAMP = new SimpleDateFormat( "yyyy-MM-dd-HH-mm-ss.SSS", Locale.ROOT )
.format( new Date() );
- private String testId;
+ private String testId = UUID.randomUUID().toString();
@Override
public void afterAll(ExtensionContext context) {
diff --git a/util/internal/integrationtest/mapper/orm/src/main/java/org/hibernate/search/util/impl/integrationtest/mapper/orm/DatabaseContainer.java b/util/internal/integrationtest/mapper/orm/src/main/java/org/hibernate/search/util/impl/integrationtest/mapper/orm/DatabaseContainer.java
index 399c3662fa1..0077c60328f 100644
--- a/util/internal/integrationtest/mapper/orm/src/main/java/org/hibernate/search/util/impl/integrationtest/mapper/orm/DatabaseContainer.java
+++ b/util/internal/integrationtest/mapper/orm/src/main/java/org/hibernate/search/util/impl/integrationtest/mapper/orm/DatabaseContainer.java
@@ -50,7 +50,7 @@ private DatabaseContainer() {
static {
- String name = System.getProperty( "org.hibernate.search.integrationtest.orm.database.kind", "" );
+ String name = System.getProperty( "org.hibernate.search.integrationtest.orm.database.kind", "h2" );
Path containers = Path.of( System.getProperty( "org.hibernate.search.integrationtest.container.directory", "" ) );
DATABASE = SupportedDatabase.from( name );