This page shows how to lay the first stone: an entity, a Jakarta Data repository, a Mansart DataSource, and a transaction. Targets H2 in-memory for speed; switching to PostgreSQL only changes the configuration.
Prerequisites
-
Java 25 (Temurin) + Maven 3.9.16.
-
JDBC driver provided by the application (
com.h2database:h2ororg.postgresql:postgresql). -
cd mansart && sdk env(or equivalent.sdkmanrc) to pin the JVM and Maven.
Maven dependencies
<dependencies>
<dependency>
<groupId>io.vidocq.mansart</groupId>
<artifactId>mansart-data-core</artifactId>
<version>${mansart.version}</version>
</dependency>
<dependency>
<groupId>io.vidocq.mansart</groupId>
<artifactId>mansart-data-dialect-h2</artifactId>
<version>${mansart.version}</version>
</dependency>
<dependency>
<groupId>io.vidocq.mansart</groupId>
<artifactId>mansart-pool-core</artifactId>
<version>${mansart.version}</version>
</dependency>
<dependency>
<groupId>io.vidocq.mansart</groupId>
<artifactId>mansart-transactions-cdi</artifactId>
<version>${mansart.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.vidocq.mansart</groupId>
<artifactId>mansart-data-processor</artifactId>
<version>${mansart.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
The mansart-data-processor APT generates, at compile time, the metamodel (_Author) and the AuthorRepositoryImpl implementation. No runtime generation.
An entity
package shop;
import jakarta.persistence.*;
@Entity
@Table(name = "authors")
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String name;
// getters / setters / no-arg constructor
}
A repository
package shop;
import jakarta.data.repository.BasicRepository;
import jakarta.data.repository.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface AuthorRepository extends BasicRepository<Author, Long> {
long count();
boolean existsById(Long id);
List<Author> findByName(String name);
Optional<Author> findOneByName(String name);
long deleteByName(String name);
}
The APT generates shop.AuthorRepositoryImpl next to it, plus an entry in META-INF/mansart-repositories.list.
Standalone bootstrap
import io.vidocq.mansart.pool.PoolConfig;
import io.vidocq.mansart.pool.core.MansartDataSource;
import io.vidocq.mansart.data.core.MansartData;
var dataSource = MansartDataSource.of(PoolConfig.builder()
.jdbcUrl("jdbc:h2:mem:shop;DB_CLOSE_DELAY=-1")
.username("sa").password("")
.minIdle(2).maxSize(10)
.build());
var mansart = MansartData.builder().dataSource(dataSource).build();
var authors = mansart.repository(AuthorRepository.class);
authors.save(new Author("Sylvie Germain"));
authors.findByName("Sylvie Germain").forEach(System.out::println);
CDI bootstrap (Vauban)
@Inject AuthorRepository authors;
@Transactional
public Author register(String name) {
return authors.save(new Author(name));
}
The mansart-data-cdi BCE reads META-INF/mansart-repositories.list and declares an @ApplicationScoped bean for each @Repository interface detected. The mansart-transactions interceptor materializes @Transactional. See Vauban for the CDI container details.
Wiring the BCE into the Vauban build
Add mansart-data-cdi to your dependencies — and, crucially, to the annotation-processor path. Vauban resolves injections at compile time inside its annotation processor, which discovers CDI Build Compatible Extensions via ServiceLoader on the processor path: the moment your pom declares <annotationProcessorPaths> (it does, for mansart-data-processor), javac sees only those entries, and a BCE left on the plain compile path silently never runs. The tell-tale failure is a build-time
[Vauban] Unsatisfied dependency: … of type ClassType[name=io.vidocq.mansart.data.core.RepositoryRuntime].
<annotationProcessorPaths>
<path>
<groupId>io.vidocq.mansart</groupId>
<artifactId>mansart-data-processor</artifactId>
<version>${mansart.version}</version>
</path>
<path><!-- vauban-processor, if not already on the processor path -->
<groupId>io.vidocq.vauban</groupId>
<artifactId>vauban-processor</artifactId>
<version>${vauban.version}</version>
</path>
<path><!-- the BCE contributing MansartRuntimeProducer must be visible to the Vauban APT -->
<groupId>io.vidocq.mansart</groupId>
<artifactId>mansart-data-cdi</artifactId>
<version>${mansart.version}</version>
</path>
</annotationProcessorPaths>
Two related notes:
-
if some other cross-module bean still trips the build-time validator, pass
-Avauban.validation=falseto the compiler to defer validation to runtime (that is what Mansart’s own module-path integration tests do); -
at runtime, the default
RepositoryRuntimeneeds a@Defaultjavax.sql.DataSourcebean (a@Produces DataSourcebacked by your driver ormansart-pool) — the producer fails fast with an explicit message otherwise. Norequires io.vidocq.mansart.data.cdiis needed in yourmodule-info.java: boot-layer service binding pulls the module in through itsprovides BuildCompatibleExtension.