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. This is a very fresh first release - if the dependency doesn't resolve immediately, Central's mirrors can take a little while to fully sync after a new version goes live; wait a few minutes and retry.
Building from source (for contributing to SynthForge itself, not needed just to use it):
git clone https://github.com/ThembaTman0/synthforge.git
cd synthforge
mvn installWithout 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.