.
This code was generated using the JHipster blueprint generator-jhipster-cassandra.
The source code for the underlying JHipster blueprint is available at: https://github.com/amarpatel-xx/generator-jhipster-cassandra.
The blueprint for generating the composite primary key with Cassandra entities is open source software made with love by Amar Premsaran Patel.
This code in this example has a JDL which shows 2 Cassandra entities that have composite primary keys and 3 Cassandra entities that have single-value primary keys. There are also other entities which show composite primary keys using various combinations of partition keys and clustering keys. The example entities in the JDL is based on
Matt Raible's frequently used the blog and store examples in his capability demonstrations.
The current blueprint now supports a multiple fields of type PrimaryKeyType.PARTITIONED; a field which is the partition column is specified as such with the @customAnnotation("PrimaryKeyType.PARTITIONED") custom annotation. If a entity needs to specify additional fields with type PrimaryKeyType.CLUSTERED, they are specified using @customAnnotation("PrimaryKeyType.CLUSTERED"). The greater than, less than and equal to query methods are autogenerated for the clustering columns. There are no relationships between Cassandra entities, as such relationships cannot be specified. The blueprint also support the Cassandra type CassandraType.Name.SET and CassandraType.Name.MAP; see below for examples.
Below are various examples of defining JDL entities using the @customAnnotation methodology to specify the details of the Cassandra composite primary key. Also, below is an example of a single-value primary key entity. Some example entities are of composite primary keys using Map fields and some are using a Set field. There are also examples of single-value primary key entities using Maps and Set data structures.
// Composite Primary Key Example:
entity Post {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATE") @customAnnotation("0") createdDate Long
// Do not name composite primary key fields as 'id' as it conflicts with the 'id' field in the JHipster entity.
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATETIME") @customAnnotation("1") addedDateTime Long
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") @customAnnotation("2") postId UUID
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") @customAnnotation("") title String required
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") @customAnnotation("") content String required
@customAnnotation("") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATETIME") @customAnnotation("") publishedDateTime Long
@customAnnotation("") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATE") @customAnnotation("") sentDate Long
}
// Single-value Primary Key Example:
entity Product {
// Primary Key field can be named 'id'. JHipster natively supports single-value primary keys. This blueprint also supports single-value primary keys.
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") @customAnnotation("") id UUID
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") @customAnnotation("") title String required
@customAnnotation("") @customAnnotation("CassandraType.Name.DECIMAL") @customAnnotation("") @customAnnotation("") price BigDecimal required min(0)
@customAnnotation("") @customAnnotation("CassandraType.Name.BLOB") @customAnnotation("image") @customAnnotation("") image ImageBlob
@customAnnotation("") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATE") @customAnnotation("") addedDate Long required
}
// Composite Primary Key Example with TIMEUUID clustered key, multiple partitioned keys, with multiple clustered keys.
entity SaathratriEntity2 {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") entityTypeId UUID
@customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("") yearOfDateAdded Long
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATE") arrivalDate Long
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.TIMEUUID") @customAnnotation("TIMEUUID") blogId UUID
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") entityName String
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") entityDescription String
@customAnnotation("") @customAnnotation("CassandraType.Name.DECIMAL") @customAnnotation("") entityCost BigDecimal
@customAnnotation("") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATE") departureDate Long
}
// Example showing a text/string set.
entity SaathratriEntity3 {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") entityType String
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.TIMEUUID") @customAnnotation("") createdTimeId UUID
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") entityName String
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") entityDescription String
@customAnnotation("") @customAnnotation("CassandraType.Name.DECIMAL") @customAnnotation("") entityCost BigDecimal
@customAnnotation("") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATE") departureDate Long
@customAnnotation("CassandraType.Name.SET") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") tags String,
}
// Example showing key-value data structure.
entity SaathratriEntity4 {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") organizationId UUID
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") attributeKey String
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") attributeValue String
}
// Example showing text/string, boolean, numeric and date-time maps.
entity AddOnsAvailableByOrganization {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") organizationId UUID
@customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") entityType String
@customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") entityId UUID
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") addOnId UUID
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") addOnType String
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") addOnDetailsText String
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.DECIMAL") @customAnnotation("") addOnDetailsDecimal BigDecimal
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.BOOLEAN") @customAnnotation("") addOnDetailsBoolean Boolean
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATETIME") addOnDetailsBigInt Long
}
// Another example showing text/string, boolean, numeric and date-time maps.
entity AddOnsSelectedByOrganization {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") organizationId UUID
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATE") arrivalDate Long
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") accountNumber String
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.TIMEUUID") @customAnnotation("") createdTimeId UUID
@customAnnotation("") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATE") departureDate Long
@customAnnotation("") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") customerId UUID
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") customerFirstName String
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") customerLastName String
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") customerUpdatedEmail String
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") customerUpdatedPhoneNumber String
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") customerEstimatedArrivalTime String
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") tinyUrlShortCode String
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") addOnDetailsText String
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.DECIMAL") @customAnnotation("") addOnDetailsDecimal BigDecimal
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.BOOLEAN") @customAnnotation("") addOnDetailsBoolean Boolean
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATETIME") addOnDetailsBigInt Long
}
// Single-value Primary Key with Maps
entity LandingPageByOrganization {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") organizationId UUID
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") detailsText String
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.DECIMAL") @customAnnotation("") detailsDecimal BigDecimal
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.BOOLEAN") @customAnnotation("") detailsBoolean Boolean
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATETIME") detailsBigInt Long
}
// Single-value Primary Key with Set
entity SetEntityByOrganization {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") organizationId UUID
@customAnnotation("CassandraType.Name.SET") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") tags String
}
The blueprint generates custom Angular UI components for each Cassandra MAP value type. The following screenshots demonstrate the AddOnsAvailableByOrganization entity, which uses all four supported MAP types.
Edit string-to-string map entries with inline key and value fields. Each entry can be added, edited, or removed.
Edit string-to-decimal map entries for numeric data such as mileage, cost, or quantity.
Edit string-to-boolean map entries using toggle switches for true/false values.
Edit string-to-datetime map entries with a full date and time picker (date, hours, minutes, AM/PM).
The list page renders all MAP columns with their key-value pairs displayed inline.
The detail/view page renders all MAP fields with their key-value pairs.
The underlying generator-jhipster-cassandra blueprint has received significant improvements since the last open-source tagged release (v1.0.13). This example has been regenerated with generator-jhipster@9.0.0 and generator-jhipster-cassandra@1.0.16.
- Added support for Cassandra VECTOR type fields using
@customAnnotation("VECTOR")with configurable dimensions. - Generates CQL schema with
VECTOR<FLOAT, N>columns and SAI (Storage Attached Index) for similarity search. - Repository methods use ANN OF (Approximate Nearest Neighbor) queries for vector similarity.
- Service layer auto-generates embeddings on save/update via a configurable
EmbeddingService. - REST endpoint
/api/<entity>/ai-searchwith field selection support for semantic search. - Angular AI search bar with checkboxes for selecting which fields to search.
- Integrated with Spring AI 2.0.0-M3 and
EmbeddingConfigurationfor OpenAI-compatible embedding providers. - Graceful degradation when an API key is not configured.
- Consistent vector display: list/detail pages show first 3 floats with 5 decimal places; update page shows full vector in a readonly textarea.
Supplying the OpenAI API key — any of these ways (checked in this order):
-
application-dev.ymlin the service:spring: ai: openai: api-key: sk-your-key-here
-
Environment variable, set before starting the services:
export OPENAI_API_KEY=sk-your-key-here -
A
.envfile in the app root (e.g.cassandrablog/.env). Each vector-enabled app has a checked-in.env.example— copy it to.envand fill in the key..envis git-ignored, so the key can never be committed; only.env.example(with an empty value) is checked in.
Without the API key the applications run normally — embedding generation and AI search are simply disabled.
Offline/e2e alternative — fake embedding model. Set OPENAI_EMBEDDING_FAKE=true to replace the OpenAI model with a deterministic offline one (same text → same vector), so exact-text semantic search works with no key and no API cost. The Cypress suite includes an embedding-lifecycle e2e that requires this mode (skipped unless CYPRESS_fakeEmbeddings=true); saathratri-run-all-tests.sh enables both automatically during its e2e phase.
- Added support for AnyBlob fields using
@customAnnotation("any"), enabling file uploads of any content type (PDF, DOCX, etc.). - Generates PDF badge styling on list, detail, and update pages for PDF files.
- Full file upload/download support with content type detection.
- Fixed detail page rendering of MAP and SET fields by converting legacy
*ngIf/*ngFordirectives to modern@if/@forcontrol flow. - Fixed
KeyValuePipeimport for standalone Angular components with MAP fields. - All four MAP value types (TEXT, DECIMAL, BOOLEAN, BIGINT/UTC_DATETIME) render correctly on list, detail, and update pages.
- Replaced page-number-based pagination with native Cassandra Slice pagination using paging state tokens, which is the correct approach for Cassandra's distributed architecture.
- Added a dedicated
/sliceendpoint for backward-compatible paginated queries. - Replaced automatic infinite scroll with a Load More button for better UX control.
- Fixed paging state extraction to use
CassandraPageRequestwith the correct public API. - Fixed infinite "Load More" loop by properly checking for empty results.
- Added findBy search methods for Cassandra composite keys with a full search widget on entity list pages.
- Added date pickers and comparison operators (equals, greater than, less than, etc.) for clustering key fields.
- Clustering key fields are automatically disabled when an inequality operator is selected on a preceding clustering column (respecting Cassandra's query restrictions).
- Navbar entities are now sorted alphabetically for easier navigation.
- Added
UTC_DATEdisplay support that prevents timezone shifting -- dates are rendered exactly as stored. - Added a custom
FormatUtcDatePipefor consistent date-only formatting in Angular templates. - Fixed UTC_DATE form handling to use dayjs throughout the stack (create, update, and search forms).
- Configured
DayjsDateAdapterfor Material Datepicker integration.
- Added column sorting for Cassandra entities, with proper data array clearing when sort changes.
- Sort by composite key fields is now fully supported in the Angular list view.
- Fixed malformed CQL
@Querygeneration infindLatestByrepository methods. - Removed expensive
count()queries from Cassandra resource templates (not supported efficiently by Cassandra). - Fixed Cassandra pagination to use an explicit
pagingStatequery parameter for clean API design. - Improved translation key generation for Cassandra entities using i18n entity labels.
- Updated JHipster version reference from 8.6.0 to 9.0.0.
- Java 21+
- Node.js 20+
- Docker Desktop
- JHipster 9.0.0
- generator-jhipster-cassandra 1.0.16
Build Java Microservices using the Cassandra Composite Primary Key Blueprint
- To generate a microservices architecture with Cassandra composite primary key support, run the following command:
Linux / macOS:
npm install -g generator-jhipster-cassandra
git clone https://github.com/amarpatel-xx/jhipster-cassandra-example.git
cd jhipster-cassandra-example
sh saathratri-generate-code-dev-cassandra.shWindows:
npm install -g generator-jhipster-cassandra
git clone https://github.com/amarpatel-xx/jhipster-cassandra-example.git
cd jhipster-cassandra-example
.\saathratri-generate-code-dev-cassandra.bat- You should see the message:
Congratulations, JHipster execution is complete!- When the process is complete, cd into the
gatewaydirectory and start Keycloak and Eureka using Docker Compose.
Linux / macOS:
cd cassandragateway
docker compose -f src/main/docker/keycloak.yml up -d
docker compose -f src/main/docker/jhipster-registry.yml up -dWindows:
cd cassandragateway
docker compose -f src\main\docker\keycloak.yml up -d
docker compose -f src\main\docker\jhipster-registry.yml up -dPlease make sure the jhipster-registry-1 Docker Container is started; sometimes that container does not run after the above command and needs to be started manually in Docker Desktop. The jhipster-registry-1 container should appear under the gateway application within Docker Desktop.
- Start
cassandragatewaydatabase with Docker by opening a terminal and navigating to its directory and running the Docker command. Then start thecassandragatewayby running the Maven command.
Linux / macOS:
npm run docker:db:up
./mvnw spring-boot:run -Dspring-boot.run.profiles=devWindows:
npm run docker:db:up
.\mvnw.cmd spring-boot:run "-Dspring-boot.run.profiles=dev"- Start
cassandrablogdatabase with Docker by opening a terminal and navigating to its directory and running the Docker command. Then, start thecassandrablogmicroservice.
Linux / macOS:
cd cassandrablog
npm run docker:db:up
./mvnw spring-boot:run -Dspring-boot.run.profiles=devWindows:
cd cassandrablog
npm run docker:db:up
.\mvnw.cmd spring-boot:run "-Dspring-boot.run.profiles=dev"- Start
cassandrastoredatabase with Docker by opening a terminal and navigating to its directory and running the Docker command. Then, start thecassandrastoremicroservice.
Linux / macOS:
cd cassandrastore
npm run docker:db:up
./mvnw spring-boot:run -Dspring-boot.run.profiles=devWindows:
cd cassandrastore
npm run docker:db:up
.\mvnw.cmd spring-boot:run "-Dspring-boot.run.profiles=dev"Every application generated into this example ships with a full test suite — both
backend (JUnit + Testcontainers) and frontend (ESLint + Vitest). All three
services (cassandragateway, cassandrablog, cassandrastore) are tested the same way.
Integration tests use Testcontainers, so Docker Desktop must be running. The
*ResourceITtests start a real Cassandra 5 container automatically for the two microservices (the gateway uses PostgreSQL) — no manual database setup is required.
Run from each service directory (cassandragateway, cassandrablog, cassandrastore):
Linux / macOS:
./mvnw -ntp -DskipTests -Dskip.npm package # compile + package only (no Docker)
./mvnw -ntp -Dskip.npm verify # unit + integration tests (Docker required)Windows:
.\mvnw.cmd -ntp -DskipTests "-Dskip.npm" package
.\mvnw.cmd -ntp "-Dskip.npm" verifyverify runs the domain, DTO, security and structural tests plus the composite-key
entity REST CRUD integration tests (*ResourceIT): create / get-one / get-all /
update (PUT) / partial update (PATCH) / delete and their negative cases, for both
single-value and composite primary keys (including auto-generated TIMEUUID clustering
keys, and Set / Map columns).
Each service has an Angular microfrontend. Run from each service directory:
npm install
npm test # runs once and exits (eslint pretest + Vitest)
npm run test:watch # same, but keeps Vitest in watch mode for iterative TDDnpm test runs eslint . first (the pretest hook) — if lint fails, the unit tests
never run — then the Angular unit tests on Vitest (ng test --coverage --watch=false),
one-shot. The --watch=false flag is added by the cassandra blueprint to work around
an upstream JHipster bug: their Angular generator emits plain "ng test --coverage", which
after the Karma→Vitest switch defaults to watch mode and never exits. The lint gate fails
only on errors, not warnings. To run just one half: npx eslint . (lint only) or
npx ng test --watch=false (Vitest only — bare npx ng test re-enters watch mode).
Each service also ships a Cypress E2E suite under src/test/javascript/cypress/
(account, administration, and per-entity CRUD specs). Unlike the unit tests, Cypress drives
a running, fully-assembled app in a real browser, so the whole stack must be up first. A
local Chrome is required for the headless run, and Docker Desktop must be running.
1. Build and start the full stack. You can bring up the infrastructure, databases, and
backends exactly as described in the Run your Cassandra Composite Primary Key Entities
Example steps above — start
Keycloak and the JHipster Registry, then for each service run npm run docker:db:up followed
by ./mvnw spring-boot:run -Dspring-boot.run.profiles=dev (mvnw.cmd on Windows).
The helper scripts do the same thing in one go (packaging first so each gateway/remote serves its compiled Angular bundle, which micro frontend module federation needs at runtime):
Linux / macOS:
sh saathratri-compile-dev.sh # package all three apps (backend + Angular client)
sh saathratri-deploy.sh # Keycloak + JHipster Registry, then each DB + mvnw spring-boot:runWindows (PowerShell):
.\saathratri-compile-dev.bat
.\saathratri-deploy.batPowerShell note. PowerShell does not auto-run executables from the current directory (
.) — the leading.\is required. PowerShell also parses-Dskip.npmas-Dskip+ a.npmmember-access token, so wrap-Dproperties containing dots in quotes ("-Dskip.npm"). Equivalently, use the stop-parsing token:.\mvnw.cmd --% -ntp -Dskip.npm verify.
Either way, wait until all three services appear in the registry at http://localhost:8761
and the gateway UI loads at http://localhost:8080. Login uses the bundled Keycloak realm
(admin/admin).
2. Run the suite — from each service directory (cassandragateway, cassandrablog,
cassandrastore), after a one-time npm install (which also fetches the Cypress binary):
npm run e2e # cypress run (headed) against the gateway at http://localhost:8080
npm run cypress # interactive Cypress runner (cypress open)Micro frontend note: every app (gateway and each remote) carries its own entity specs but points
baseUrlat the gateway (port 8080), so the specs run against the assembled shell. The gateway and the service whose entities you are testing must both be running.
To exercise just the gateway's account/admin specs without the full fleet, run
npm run e2e:devserver from cassandragateway; it starts that app's backend plus Angular
dev server (port 9000) and runs Cypress against it.
The per-entity specs carry the blueprint's composite-key DELETE handling — the
afterEach cleanup URL uses every compositeId segment and the delete intercept matches the
multi-segment path. See
generator-jhipster-cassandra/TESTING.md (§5.2).
This example is generated code — do not fix a failing test by hand-editing the
generated app, because the next regeneration overwrites it. Instead, fix the blueprint
template that produced the code, then regenerate. The full debugging runbook (the
generate-sample tight loop, the backend composite-key bug patterns, and the Angular
frontend compile / runtime / lint bug catalogue) lives in the blueprint repo:
generator-jhipster-cassandra/TESTING.md.
All shell scripts have corresponding Windows batch file equivalents:
| Linux / macOS | Windows |
|---|---|
sh saathratri-generate-code-dev-cassandra.sh |
saathratri-generate-code-dev-cassandra.bat |
sh saathratri-generate-code-dev-cassandra-mf.sh |
saathratri-generate-code-dev-cassandra-mf.bat |
sh saathratri-cleanup-dev-main.sh |
saathratri-cleanup-dev-main.bat |
sh saathratri-cleanup-dev-cassandra.sh |
saathratri-cleanup-dev-cassandra.bat |
sh saathratri-compile-dev.sh |
saathratri-compile-dev.bat |
sh saathratri-deploy.sh |
saathratri-deploy.bat |
sh docker-remove-orphans.sh |
docker-remove-orphans.bat |
Note on regen prompts:
saathratri-generate-code-dev-cassandra.{sh,bat}callssaathratri-cleanup-dev-main.{sh,bat}first, which now also wipes the root-level.prettierignore,.prettierrc.yml, and.gitattributesfiles in addition to the per-service dirs. Without those deletions, JHipster's monorepo mode (--monorepository --workspaces) would promptOverwrite .prettierignore? (ynadxreiH)on every regen because the previous run left them at the repo root. If you still see overwrite prompts for files inside a Cassandra service dir (cassandragateway/,cassandrablog/,cassandrastore/), it usually means an IDE ormvnwprocess has a file handle on a file inside — close it and re-run.
JHipster 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.
- Click on cassandrablog > Taj User. Create a user by providing a ID (UUID) and a login name (string).
- Then, add a blog by giving it a category (string), blog ID (UUID), handle (string) and content (string).
- Add a tag by giving it a ID (UUID) and name (string).
- Create a post by giving it a created date, added date time, post ID (UUID), title (string), content (string), published date time, and sent date.
- Finally, create a product (in the cassandrastore menu) by giving it an ID (UUID), title (string), price (number with decimal), image (choose an image file), and added date.
Notice the Blog and Post entities show the required composite primary key fields during the create, update and delete process. That is success!
I hope you enjoyed this demo, and it helped you understand how to build better microservice architectures with composite primary keys.
☕️ Find the code for the underlying blueprint for this example on GitHub: https://github.com/amarpatel-xx/generator-jhipster-cassandra
☕️ Find the example code that uses the blueprint to generate a JHipster application on GitHub: https://github.com/amarpatel-xx/jhipster-cassandra-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 yelhouti, Jeremy Artero, Matt Raible, Gaël Marziou, Cedrick Lunven, Christophe Borne, Disha Patel and Catherine Guevara for your invaluable contributions to this example and the underlying JHipster blueprint.





