A JPA-aware fake data seeding library for Spring Boot.
Instancio (and its instancio-jpa extension) gives you an API to call from inside a test - build a graph, persist it, for that test. SynthForge runs itself: annotate the entity, start the app in a dev profile, and the database is already populated - no test method, no calling code, anywhere.
Faker and Datafaker generate realistic-looking values - names, emails,
addresses - but they have no idea your entities are related. The moment
one entity references another (Payment → Counterparty), you're back to
hand-writing a seed script: create parents first, hold onto their IDs,
wire them into children, hope you didn't violate a @NotNull or a unique
constraint along the way. That script rots the first time a field changes.
SynthForge reads your JPA entities directly - annotations, relationships,
and all - and generates a valid, related, constraint-respecting object
graph with a single annotation, so there's no script to write or maintain.
SynthForge is on Maven Central. Add the dependency to your own Spring Boot project:
Maven
<dependency>
<groupId>io.github.thembatman0</groupId>
<artifactId>synthforge-spring</artifactId>
<version>0.1.0</version>
</dependency>Gradle
implementation 'io.github.thembatman0:synthforge-spring:0.1.0'Requires Java 21 and Spring Boot with Spring Data JPA. The test suite passes in CI on both Spring Boot 4.1 and Spring Boot 3.5.16. Spring Boot 3.5 apps still need to run on Java 21 or newer, because the library is compiled for Java 21.
The repo includes a small demo app (Counterparty and Payment on an
in-memory H2 database). From a fresh clone:
git clone https://github.com/ThembaTman0/synthforge.git
cd synthforge
mvn -pl synthforge-demo -am install -DskipTests
mvn -pl synthforge-demo spring-boot:run -Dspring-boot.run.profiles=devAlong with the SQL it logs (show-sql is on), you should see:
SynthforgeAutoConfiguration : Seeding with random seed 1593487635758507864 (set synthforge.seed to this value to reproduce the run)
SynthforgeAutoConfiguration : Seeded 50 Counterparty rows
SynthforgeAutoConfiguration : Seeded 200 Payment rows
The seed value will differ on each run. Set synthforge.seed to a fixed
number to get identical data every time. The demo has no web layer, so it
seeds, logs and exits. To see seeding work in your own app, annotate an
entity as shown under "Before / after" below.
- You want a fresh object graph inside a single test method. That is what Instancio is for.
- You need data in production. Seeding is for dev and test profiles
only, and is off unless you list a profile under
synthforge.enabled-profiles. - You need to load very large volumes. Seeding is meant for dev and test
data sizes and goes through JPA's
EntityManager; it has not been designed or measured for bulk loads. - Your model depends on
@OneToMany/@ManyToManycollections or composite keys. Those fields are skipped in this release.
Building from source (for contributing to SynthForge itself, not
needed just to use it): mvn install.
Without SynthForge, seeding two related entities means a hand-wired script:
List<Counterparty> parents = new ArrayList<>();
for (int i = 0; i < 50; i++) {
parents.add(counterpartyRepo.save(new Counterparty(faker.name().fullName(), faker.internet().emailAddress())));
}
for (int i = 0; i < 200; i++) {
paymentRepo.save(new Payment(parents.get(random.nextInt(parents.size())), randomAmount()));
}With SynthForge, the entities are the seed script:
@Entity @Seed(count = 50)
public class Counterparty { /* fields only */ }
@Entity @Seed(count = 200)
public class Payment {
@ManyToOne(optional = false) private Counterparty counterparty; // wired automatically
}Enable it for the profiles you want (never production):
synthforge:
enabled-profiles: [dev, test]Start the app in an enabled profile and both tables are populated, in the right order, with realistic values, on every restart.
- Entity scanning - reads JPA-managed attributes through the
jakarta.persistence.metamodel.MetamodelAPI, never raw reflection, so only real persistent fields are ever touched. - Relationship ordering - builds a dependency graph from owning-side
@ManyToOne/@OneToOnerelationships and topologically sorts it, so parent rows always exist before a child is generated to reference them. - Constraint-aware generation -
@NotNull,@Size,@Email, and field-name heuristics (email,iban,amount,country, ...) drive realistic values via Datafaker; a@Column(unique = true)field gets a bounded retry loop instead of a constraint violation. - Idempotent restarts - a table that already has rows is skipped, so restarting against a persistent database never duplicates seed data.
Full technical detail - the exact resolution priority, relationship rules, and configuration reference - is in synthforge-v1-spec.md.