This code was generated using the JHipster blueprint generator-jhipster-ai-postgresql.
The source code for the underlying JHipster generator that is used in this example is available at: https://github.com/amarpatel-xx/generator-jhipster-ai-postgresql
This code has a JDL which shows 2 foreign keys that will concatenated and shown, in the Angular user interface, in replacement of the UUID. The JDL can be modified and the @customAnnotation("DISPLAY_IN_GUI_RELATIONSHIP_LINK") can be used with any fields of an entity which would make it easier to identify that entity when displayed (as part of a relationship). Sometimes having a UUID makes it difficult for the human in the loop to figure out what the entity on a relationship's other side actually is. If multiple entity fields are necessary to replace the UUID, the fields can be delimmited via a specified delimiter using a @customAnnotation, as well (see the example JDL file included as part of this project).
Matt Raible's frequently used the blog and store examples in his capability demonstrations.
- Below is the example using the @customAnnotation and specifying the delimiter also.
entity Blog {
@customAnnotation("DISPLAY_IN_GUI_RELATIONSHIP_LINK") @customAnnotation("-") name String required minlength(3)
@customAnnotation("DISPLAY_IN_GUI_RELATIONSHIP_LINK") @customAnnotation("-") handle String required minlength(2)
}
entity Post {
title String required
content TextBlob required
date Instant required
}
relationship ManyToOne {
Blog{user(login)} to User
Post{blog} to Blog
}The underlying generator-jhipster-ai-postgresql blueprint has received significant improvements since the last open-source tagged release (v2.0.14). Regenerating this example with the latest blueprint version will include:
- Added full PostgreSQL pgvector support for AI-powered semantic search on entity fields.
- Automatic embedding generation on create and update -- when an entity with vector fields is saved, embeddings are generated from source text fields (e.g.,
name->nameEmbedding) using the OpenAI Embedding API. - AI semantic search bar on list pages for entities with vector fields -- users can type natural language queries and find semantically similar records.
- Vector embedding fields are automatically excluded from DTOs to keep payloads clean, while remaining in JPA entities for database operations.
- Cosine similarity search with distance threshold filters out unrelated results -- only semantically relevant matches are returned.
- HNSW indexes are automatically created on vector columns for fast approximate nearest neighbor search.
- Generates
EmbeddingConfigurationwith Spring AI and OpenAI embeddings (text-embedding-3-small, 1536 dimensions). PgVectorConverterwithautoApply=truehandlesfloat[]<-> PostgreSQLvectorserialization transparently.- Automatic embedding migration on startup -- similar to how Liquibase runs, embeddings are generated for any rows missing them.
- Angular UI shows vector fields as readonly on update forms and truncates long vector arrays on list and detail pages.
The Tag entity in this example uses @customAnnotation("VECTOR") to enable AI semantic search:
entity Tag {
id UUID
@customAnnotation("DISPLAY_IN_GUI_RELATIONSHIP_LINK") @customAnnotation("") name String maxlength(100) required
description String maxlength(255)
@customAnnotation("VECTOR") @customAnnotation("1536") nameEmbedding Blob
@customAnnotation("VECTOR") @customAnnotation("1536") descriptionEmbedding Blob
}
How the annotations work:
@customAnnotation("VECTOR")-- Marks the field as a pgvector embedding field. The blueprint converts it fromBlobtofloat[], generates thevector(N)column type in Liquibase, and adds the AI search infrastructure.@customAnnotation("1536")-- Specifies the vector dimension (1536 for OpenAI'stext-embedding-3-smallmodel).- The embedding field name must follow the pattern
<sourceField>Embedding(e.g.,nameEmbeddingderives fromname,descriptionEmbeddingderives fromdescription). The blueprint auto-generates embeddings from the source field's text value on every create and update. - The AI search bar queries all embedding fields in the entity, merges results, and deduplicates by ID -- so a match on either
nameordescriptionwill surface the entity.
Searching for "camry" returns Toyota (a car brand) and Cat (less relevant, ranked lower):
Searching for "cheetah" returns Cat first (both are felines), then Dog and Toyota ranked by similarity:
Searching for "german shepherd" returns Dog first (a dog breed), then Cat (also an animal), then Toyota (least similar):
- Added PDF thumbnail and download support for
blobContentTypeAnyfields in list, detail, and update page templates. - PDF icon styling matches across list and detail views with shadow and download link.
- Added null-safe
openFile()for blob fields.
- Added Entity Graph backend repository support for eager-loading related entities in a single query, avoiding N+1 problems.
- Added a feature to ignore massive entity relationship lists on view and update pages, keeping the UI responsive and performant.
- Fixed
toDTOmapping performance issues by preventing MapStruct infinite recursion on bidirectional relationships. - Added a non-paginated criteria endpoint for cases where full result sets are needed without pagination overhead.
- Added navbar menu grouping and alphabetical sorting for microfrontend entity menus.
- Simplified entity graph handling and REST resource templates.
- Added
ExceptionTranslatorpatching to log full stack traces at ERROR level for better debugging.
- Java 21+
- Node.js 20+
- Docker Desktop
- JHipster 9.0.0
To enable AI-powered semantic search, set your OpenAI API key as an environment variable:
export OPENAI_API_KEY=sk-your-key-hereOr add it to your microservice's application-dev.yml:
openai:
api-key: sk-your-key-hereWithout the API key, the application runs normally but embedding generation and AI search are disabled.
- To generate a microservices architecture with human-readable foreign key fields support, run the following commands:
npm install -g generator-jhipster-ai-postgresql
git clone https://github.com/amarpatel-xx/jhipster-ai-postgresql-example.git
cd jhipster-ai-postgresql-exampleMac / Linux:
sh saathratri-generate-code-dev-sql.shWindows:
saathratri-generate-code-dev-sql.bat- You should see the message:
Congratulations, JHipster execution is complete!- When the process is complete, cd into the
psqlgatewaydirectory and start Keycloak and Eureka using Docker Compose.
cd psqlgateway
docker compose -f src/main/docker/keycloak.yml up -d
docker compose -f src/main/docker/jhipster-registry.yml up -d- Start
psqlgatewaydatabase with Docker by opening a terminal and navigating to its directory and running the Docker command. Then start thegatewayby running the Maven command.
npm run docker:db:up
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev- Start
pslqblogdatabase with Docker by opening a terminal and navigating to its directory and running the Docker command. Then, start theblogmicroservice.
cd psqlblog
npm run docker:db:up
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev- Start
psqlstoredatabase with Docker by opening a terminal and navigating to its directory and running the Docker command. Then, start thestoremicroservice.
cd psqlstore
npm run docker:db:up
./mvnw spring-boot:run -Dspring-boot.run.profiles=devJHipster ships with Keycloak when you choose OAuth 2.0 / OIDC as the authentication type.
If you'd like to use Okta for your identity provider, see JHipster's documentation.
You can configure JHipster quickly with the Okta CLI:
okta apps create jhipsterNow you can open your favorite browser to http://localhost:8080, and log in with the credentials displayed on the page.
- Open your favorite browser to http://localhost:8080, and log in with the credentials displayed on the page. Then navigate to the psqlblog menu item.
- Then, add a user by giving it a login name.
- Then, add a blog by giving it a name, handle and selecting the user.
- Add a tag by giving it a name and description. If the OpenAI API key is configured, embeddings are automatically generated when you save.
- Finally, add a post by providing a title, content, selecting the blog and the tag.
Notice the blog column of the post shows <blog-name>-<blog-handle> and not the UUID of the blog. That is success!
If you configured the OpenAI API key, go to the Tag list page and use the AI Search bar. Type a natural language query (e.g., "animals" or "vehicles") and the search will find tags with semantically similar names or descriptions using cosine similarity against pgvector embeddings.
- Open your favorite browser to http://localhost:8080, and log in with the credentials displayed on the page. Then navigate to the psqlstore menu item.
- Then, add a product by giving it a title, a price and an image.
I hope you enjoyed this demo, and it helped you understand how to build better microservice architectures with human-readable foreign key fields.
☕️ Find the code for the underlying blueprint used here to generate a JHipster application on GitHub: https://github.com/amarpatel-xx/generator-jhipster-ai-postgresql
☕️ Find the example code that uses the blueprint to generate a JHipster application on GitHub: https://github.com/amarpatel-xx/jhipster-ai-postgresql-example
🤓 Read the following blog post, by Matt Raible, that was used as inspiration for this project: Micro Frontends for Java Microservices
Thank you to Matt Raible and Gaël Marziou for your invaluable contributions to this example and the underlying JHipster blueprint.


