convert-jpa-to-spring-data-cosmos — GitHub Copilot rule
A step-by-step Copilot rule for moving a Spring Boot app off JPA and onto Azure Cosmos DB with Spring Data Cosmos. Useful once the migration is already decided.
.github/instructions/convert-jpa-to-spring-data-cosmos.instructions.mdWhat convert-jpa-to-spring-data-cosmos does
The rule lays out a JPA to Azure Cosmos DB migration for Spring Boot. It runs through nine steps: swap build dependencies, add a cosmos profile and properties, write a config class with Azure Identity authentication, reshape entities and repositories, add a service layer for relationships, update every test, and seed data with a CommandLineRunner.
Entities change a lot. The id field goes from Integer to String, @Entity becomes @Container, a @PartitionKey field appears, and @OneToMany or @ManyToOne relationships turn into embedded collections or reference IDs. There is a dedicated section on authentication entities: @JsonIgnore on the password or authorities breaks serialization to Cosmos, so the guide points you at @JsonProperty and storing authorities as Set<String>. For Thymeleaf or JSP apps it suggests keeping a transient @JsonIgnore property next to the stored ID list, so template access still works.
A guard sits at the top: apply the guide only when the user explicitly asks for an Azure Cosmos DB migration. Finding JPA in a project is not enough. Setup needs the com.azure:azure-identity dependency and DefaultAzureCredential; key-based auth is not the path shown here. Because IDs become strings, expect to rewrite tests and any code that assumed a numeric identifier.
What it helps with
- swap JPA dependencies for Azure Cosmos DB in Maven or Gradle
- convert entities to
@Containerand@PartitionKey - fix serialization of authentication entities for Cosmos
- keep relationship access working in Thymeleaf templates after switching to IDs
- rewrite tests around string identifiers
Example prompts for the agent
- Apply the convert-jpa-to-spring-data-cosmos rule and migrate this project to Azure Cosmos DB
- Replace
JpaRepositorywithCosmosRepositoryin this module's repositories - Check the User entity and drop
@JsonIgnorefrom fields that must persist to Cosmos
How to install convert-jpa-to-spring-data-cosmos
Manually: put the files in place
.github/instructions/convert-jpa-to-spring-data-cosmos.instructions.mdThe applyTo field sets which files it applies to
Manually: put the files in place
.claude/rules/convert-jpa-to-spring-data-cosmos.mdThe paths frontmatter field limits the files: move applyTo from the source into paths
AGENTS.md
Codex reads AGENTS.md: paste the rule text there
Manually: put the files in place
.cursor/rules/convert-jpa-to-spring-data-cosmos.mdcFrontmatter: description, globs, alwaysApply. Move applyTo from the source into globs, or the rule will not attach on its own
Manually: put the files in place
.devin/rules/convert-jpa-to-spring-data-cosmos.mdManually: put the files in place
.clinerules/convert-jpa-to-spring-data-cosmos.mdStep by step, with commands for each platform: AGENTS.md vs CLAUDE.md: what they are and which one to use
Questions about convert-jpa-to-spring-data-cosmos
How do I install convert-jpa-to-spring-data-cosmos in GitHub Copilot?
Place the file at .github/instructions/convert-jpa-to-spring-data-cosmos.instructions.md inside your project repository.
What does this rule do?
It walks Copilot through a Spring Boot migration from JPA to Azure Cosmos DB: dependencies, configuration, entities, repositories, a service layer and tests.
When does the rule actually apply?
Only when you explicitly ask for an Azure Cosmos DB migration. Detecting JPA in the project does not trigger it on its own.
Source text
convert-jpa-to-spring-data-cosmos.instructions.md · MIT
---
description: 'Step-by-step guide for converting Spring Boot JPA applications to use Azure Cosmos DB with Spring Data Cosmos'
applyTo: '**/*.java,**/pom.xml,**/build.gradle,**/application*.properties'
---
# Convert Spring JPA project to Spring Data Cosmos
> **Guard:** Apply this guide ONLY when the user explicitly asks to migrate to Azure Cosmos DB. Detecting JPA in a project does not authorize a migration. Otherwise, follow the `java-hibernate-jpa` and Spring Boot guidance.
This generalized guide applies to any JPA to Spring Data Cosmos DB conversion project.
## High-level plan
1. Swap build dependencies (remove JPA, add Cosmos + Identity).
2. Add `cosmos` profile and properties.
3. Add Cosmos config with proper Azure identity authentication.
4. Transform entities (ids → `String`, add `@Container` and `@PartitionKey`, remove JPA mappings, adjust relationships).
5. Convert repositories (`JpaRepository` → `CosmosRepository`).
6. **Create service layer** for relationship management and template compatibility.
7. **CRITICAL**: Update ALL test files to work with String IDs and Cosmos repositories.
8. Seed data via `CommandLineRunner`.
9. **CRITICAL**: Test runtime functionality and fix template compatibility issues.
## Step-by-step
### Step 1 — Build dependencies
- **Maven** (`pom.xml`):
- Remove dependency `spring-boot-starter-data-jpa`
- Remove database-specific dependencies (H2, MySQL, PostgreSQL) unless needed elsewhere
- Add `com.azure:azure-spring-data-cosmos:5.17.0` (or latest compatible version)
- Add `com.azure:azure-identity:1.15.4` (required for DefaultAzureCredential)
- **Gradle**: Apply same dependency changes for Gradle syntax
- Remove testcontainers and JPA-specific test dependencies
### Step 2 — Properties and Configuration
- Create `src/main/resources/application-cosmos.properties`:
```properties
azure.cosmos.uri=${COSMOS_URI:https://localhost:8081}
azure.cosmos.database=${COSMOS_DATABASE:petclinic}
azure.cosmos.populate-query-metrics=false
azure.cosmos.enable-multiple-write-locations=false
```
- Update `src/main/resources/application.properties`:
```properties
spring.profiles.active=cosmos
```
### Step 3 — Configuration class with Azure Identity
- Create `src/main/java/<rootpkg>/config/CosmosConfiguration.java`:
```java
@Configuration
@EnableCosmosRepositories(basePackages = "<rootpkg>")
public class CosmosConfiguration extends AbstractCosmosConfiguration {
@Value("${azure.cosmos.uri}")
private String uri;
@Value("${azure.cosmos.database}")
private String dbName;
@Bean
public CosmosClientBuilder getCosmosClientBuilder() {
return new CosmosClientBuilder().endpoint(uri).credential(new DefaultAzureCredentialBuilder().build());
}
@Override
protected String getDatabaseName() {
return dbName;
}
@Bean
public CosmosConfig cosmosConfig() {
return CosmosConfig.builder().enableQueryMetrics(false).build();
}
}
```
- **IMPORTANT**: Use `DefaultAzureCredentialBuilder().build()` instead of key-based authentication for production security
### Step 4 — Entity transformation
- Target all classes with JPA annotations (`@Entity`, `@MappedSuperclass`, `@Embeddable`)
- **Base entity changes**:
- Change `id` field type from `Integer` to `String`
- Add `@Id` and `@GeneratedValue` annotations
- Add `@PartitionKey` field (typically `String partitionKey`)
- Remove all `jakarta.persistence` imports
- **CRITICAL - Cosmos DB Serialization Requirements**:
- **Remove ALL `@JsonIgnore` annotations** from fields that need to be persisted to Cosmos DB
- **Authentication entities (User, Authority) MUST be fully serializable** - no `@JsonIgnore` on password, authorities, or other persisted fields
- **Use `@JsonProperty` instead of `@JsonIgnore`** when you need to control JSON field names but still persist the data
- **Common authentication serialization errors**: `Cannot pass null or empty values to constructor` usually means `@JsonIgnore` is blocking required field serialization
- **Entity-specific changes**:
- Replace `@Entity` with `@Container(containerName = "<plural-entity-name>")`
- Remove `@Table`, `@Column`, `@JoinColumn`, etc.
- Remove relationship annotations (`@OneToMany`, `@ManyToOne`, `@ManyToMany`)
- For relationships:
- Embed collections for one-to-many (e.g., `List<Pet> pets` in Owner)
- Use reference IDs for many-to-one (e.g., `String ownerId` in Pet)
- **For complex relationships**: Store IDs but add transient properties for templates
- Add constructor to set partition key: `setPartitionKey("entityType")`
- **CRITICAL - Authentication Entity Pattern**:
- **For User entities with Spring Security**: Store authorities as `Set<String>` instead of `Set<Authority>` objects
- **Example User entity transformation**:
```java
@Container(containerName = "users")
public class User {
@Id
private String id;
@PartitionKey
private String partitionKey = "user";
private String login;
private String password; // NO @JsonIgnore - must be serializable
@JsonProperty("authorities") // Use @JsonProperty, not @JsonIgnore
private Set<String> authorities = new HashSet<>(); // Store as strings
// Add transient property for Spring Security compatibility if needed
// @JsonIgnore - ONLY for transient properties not persisted to Cosmos
private Set<Authority> authorityObjects = new HashSet<>();
// Conversion methods between string authorities and Authority objects
public void setAuthorityObjects(Set<Authority> authorities) {
this.authorityObjects = authorities;
this.authorities = authorities.stream().map(Authority::getName).collect(Collectors.toSet());
}
}
```
- **CRITICAL - Template Compatibility for Relationship Changes**:
- **When converting relationships to ID references, preserve template access**
- **Example**: If entity had `List<Specialty> specialties` → convert to:
- Storage: `List<String> specialtyIds` (persisted to Cosmos)
- Template: `@JsonIgnore private List<Specialty> specialties = new ArrayList<>()` (transient)
- Add getters/setters for both properties
- **Update entity method logic**: `getNrOfSpecialties()` should use the transient list
- **CRITICAL - Template Compatibility for Thymeleaf/JSP Applications**:
- **Identify template property access**: Search for `${entity.relationshipProperty}` in `.html` files
- **For each relationship property accessed in templates**:
- **Storage**: Keep ID-based storage (e.g., `List<String> specialtyIds`)
- **Template Access**: Add transient property with `@JsonIgnore` (e.g., `private List<Specialty> specialties = new ArrayList<>()`)
- **Example**:
```java
// Stored in Cosmos (persisted)
private List<String> specialtyIds = new ArrayList<>();
// For template access (transient)
@JsonIgnore
private List<Specialty> specialties = new ArrayList<>();
// Getters/setters for both properties
public List<String> getSpecialtyIds() {
return specialtyIds;
}
public List<Specialty> getSpecialties() {
return specialties;
}
```
- **Update count methods**: `getNrOfSpecialties()` should use transient list, not ID list
- **CRITICAL - Method Signature Conflicts**:
- **When converting ID types from Integer to String, check for method signature conflicts**
- **Common conflict**: `getPet(String name)` vs `getPet(String id)` - both have same signature
- **Solution**: Rename methods to be specific:
- `getPet(String id)` for ID-based lookup
- `getPetByName(String name)` for name-based lookup
- `getPetByName(String name, boolean ignoreNew)` for conditional name-based lookup
- **Update ALL callers** of renamed methods in controllers and tests
- **Method updates for entities**:
- Update `addVisit(Integer petId, Visit visit)` to `addVisit(String petId, Visit visit)`
- Ensure all ID comparison logic uses `.equals()` instead of `==`
### Step 5 — Repository conversion
- Change all repository interfaces:
- From: `extends JpaRepository<Entity, Integer>`
- To: `extends CosmosRepository<Entity, String>`
- **Query method updates**:
- Remove pagination parameters from custom queries
- Change `Page<Entity> findByX(String param, Pageable pageable)` to `List<Entity> findByX(String param)`
- Update `@Query` annotations to use Cosmos SQL syntax
- **Replace custom method names**: `findPetTypes()` → `findAllOrderByName()`
- **Update ALL references** to changed method names in controllers and formatters
### Step 6 — **Create service layer** for relationship management and template compatibility
- **CRITICAL**: Create service classes to bridge Cosmos document storage with existing template expectations
- **Purpose**: Handle relationship population and maintain template compatibility
- **Service pattern for each entity with relationships**:
```java
@Service
public class EntityService {
private final EntityRepository entityRepository;
private final RelatedRepository relatedRepository;
public EntityService(EntityRepository entityRepository, RelatedRepository relatedRepository) {
this.entityRepository = entityRepository;
this.relatedRepository = relatedRepository;
}
public List<Entity> findAll() {
List<Entity> entities = entityRepository.findAll();
entities.forEach(this::populateRelationships);
return entities;
}
public Optional<Entity> findById(String id) {
Optional<Entity> entityOpt = entityRepository.findById(id);
if (entityOpt.isPresent()) {
Entity entity = entityOpt.get();
populateRelationships(entity);
return Optional.of(entity);
}
return Optional.empty();
}
private void populateRelationships(Entity entity) {
if (entity.getRelatedIds() != null && !entity.getRelatedIds().isEmpty()) {
List<Related> related = entity
.getRelatedIds()
.stream()
.map(relatedRepository::findById)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
// Set transient property for template access
entity.setRelated(related);
}
}
}
```
### Step 6.5 — **Spring Security Integration** (CRITICAL for Authentication)
- **UserDetailsService Integration Pattern**:
```java
@Service
@Transactional
public class DomainUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
private final AuthorityRepository authorityRepository;
@Override
public UserDetails loadUserByUsername(String login) {
log.debug("Authenticating user: {}", login);
return userRepository
.findOneByLogin(login)
.map(user -> createSpringSecurityUser(login, user))
.orElseThrow(() -> new UsernameNotFoundException("User " + login + " was not found"));
}
private org.springframework.security.core.userdetails.User createSpringSecurityUser(String lowercaseLogin, User user) {
if (!user.isActivated()) {
throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated");
}
// Convert string authorities back to GrantedAuthority objects
List<GrantedAuthority> grantedAuthorities = user
.getAuthorities()
.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
return new org.springframework.security.core.userdetails.User(user.getLogin(), user.getPassword(), grantedAuthorities);
}
}
```
- **Key Authentication Requirements**:
- User entity must be fully serializable (no `@JsonIgnore` on password/authorities)
- Store authorities as `Set<String>` for Cosmos DB compatibility
- Convert between string authorities and `GrantedAuthority` objects in UserDetailsService
- Add comprehensive debugging logs to trace authentication flow
- Handle activated/deactivated user states appropriately
#### **Template Relationship Population Pattern**
Each service method that returns entities for template rendering MUST populate transient properties:
```java
private void populateRelationships(Entity entity) {
// For each relationship used in templates
if (entity.getRelatedIds() != null && !entity.getRelatedIds().isEmpty()) {
List<Related> relatedObjects = entity
.getRelatedIds()
.stream()
.map(relatedRepository::findById)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
entity.setRelated(relatedObjects); // Set transient property
}
}
```
#### **Critical Service Usage in Controllers**
- **Replace ALL direct repository calls** with service calls in controllers
- **Never return entities from repositories directly** to templates without relationship population
- **Update controllers** to use service layer instead of repositories directly
- **Controller pattern change**:
```java
// OLD: Direct repository usage
@Autowired
private EntityRepository entityRepository;
// NEW: Service layer usage
@Autowired
private EntityService entityService;
// Update method calls
// OLD: entityRepository.findAll()
// NEW: entityService.findAll()
```
### Step 7 — Data seeding
- Create `@Component` implementing `CommandLineRunner`:
```java
@Component
public class DataSeeder implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
if (ownerRepository.count() > 0) {
return; // Data already exists
}
// Seed comprehensive test data with String IDs
// Use meaningful ID patterns: "owner-1", "pet-1", "pettype-1", etc.
}
}
```
- **CRITICAL - BigDecimal Reflection Issues with JDK 17+**:
- **If using BigDecimal fields**, you may encounter reflection errors during seeding
- **Error pattern**: `Unable to make field private final java.math.BigInteger java.math.BigDecimal.intVal accessible`
- **Solutions**:
1. Use `Double` or `String` instead of `BigDecimal` for monetary values
2. Add JVM argument: `--add-opens java.base/java.math=ALL-UNNAMED`
3. Wrap BigDecimal operations in try-catch and handle gracefully
- **The application will start successfully even if seeding fails** - check logs for seeding errors
### Step 8 — Test file conversion (CRITICAL SECTION)
**This step is often overlooked but essential for successful conversion**
#### A. **COMPILATION CHECK STRATEGY**
- **After each major change, run `mvn test-compile` to catch issues early**
- **Fix compilation errors systematically before proceeding**
- **Don't rely on IDE - Maven compilation reveals all issues**
#### B. **Search and Update ALL test files systematically**
**Use search tools to find and update every occurrence:**
- Search for: `int.*TEST.*ID` → Replace with: `String.*TEST.*ID = "test-xyz-1"`
- Search for: `setId\(\d+\)` → Replace with: `setId("test-id-X")`
- Search for: `findById\(\d+\)` → Replace with: `findById("test-id-X")`
- Search for: `\.findPetTypes\(\)` → Replace with: `.findAllOrderByName()`
- Search for: `\.findByLastNameStartingWith\(.*,.*Pageable` → Remove pagination parameter
#### C. Update test annotations and imports
- Replace `@DataJpaTest` with `@SpringBootTest` or appropriate slice test
- Remove `@AutoConfigureTestDatabase` annotations
- Remove `@Transactional` from tests (unless single-partition operations)
- Remove imports from `org.springframework.orm` package
#### D. Fix entity ID usage in ALL test files
**Critical files that MUST be updated (search entire test directory):**
- `*ControllerTests.java` - Path variables, entity creation, mock setup
- `*ServiceTests.java` - Repository interactions, entity IDs
- `EntityUtils.java` - Utility methods for ID handling
- `*FormatterTests.java` - Repository method calls
- `*ValidatorTests.java` - Entity creation with String IDs
- Integration test classes - Test data setup
#### E. **Fix Controller and Service classes affected by repository changes**
- **Update controllers that call repository methods with changed signatures**
- **Update formatters/converters that use repository methods**
- **Common files to check**:
- `PetTypeFormatter.java` - often calls `findPetTypes()` method
- `*Controller.java` - may have pagination logic to remove
- Service classes that use repository methods
#### F. Update repository mocking in tests
- Remove pagination from repository mocks:
- `given(repository.findByX(param, pageable)).willReturn(pageResult)`
- → `given(repository.findByX(param)).willReturn(listResult)`
- Update method names in mocks:
- `given(petTypeRepository.findPetTypes()).willReturn(types)`
- → `given(petTypeRepository.findAllOrderByName()).willReturn(types)`
#### G. Fix utility classes used by tests
- Update `EntityUtils.java` or similar:
- Remove JPA-specific exception imports (`ObjectRetrievalFailureException`)
- Change method signatures from `int id` to `String id`
- Update ID comparison logic: `entity.getId() == entityId` → `entity.getId().equals(entityId)`
- Replace JPA exceptions with standard exceptions (`IllegalArgumentException`)
#### H. Update assertions for String IDs
- Change ID assertions:
- `assertThat(entity.getId()).isNotZero()` → `assertThat(entity.getId()).isNotEmpty()`
- `assertThat(entity.getId()).isEqualTo(1)` → `assertThat(entity.getId()).isEqualTo("test-id-1")`
- JSON path assertions: `jsonPath("$.id").value(1)` → `jsonPath("$.id").value("test-id-1")`
### Step 8 — Test file conversion (CRITICAL SECTION)
**This step is often overlooked but essential for successful conversion**
#### A. **COMPILATION CHECK STRATEGY**
- **After each major change, run `mvn test-compile` to catch issues early**
- **Fix compilation errors systematically before proceeding**
- **Don't rely on IDE - Maven compilation reveals all issues**
#### B. **Search and Update ALL test files systematically**
**Use search tools to find and update every occurrence:**
- Search for: `setId\(\d+\)` → Replace with: `setId("test-id-X")`
- Search for: `findById\(\d+\)` → Replace with: `findById("test-id-X")`
- Search for: `\.findPetTypes\(\)` → Replace with: `.findAllOrderByName()`
- Search for: `\.findByLastNameStartingWith\(.*,.*Pageable` → Remove pagination parameter
#### C. Update test annotations and imports
- Replace `@DataJpaTest` with `@SpringBootTest` or appropriate slice test
- Remove `@AutoConfigureTestDatabase` annotations
- Remove `@Transactional` from tests (unless single-partition operations)
- Remove imports from `org.springframework.orm` package
#### D. Fix entity ID usage in ALL test files
**Critical files that MUST be updated (search entire test directory):**
- `*ControllerTests.java` - Path variables, entity creation, mock setup
- `*ServiceTests.java` - Repository interactions, entity IDs
- `EntityUtils.java` - Utility methods for ID handling
- `*FormatterTests.java` - Repository method calls
- `*ValidatorTests.java` - Entity creation with String IDs
- Integration test classes - Test data setup
#### E. **Fix Controller and Service classes affected by repository changes**
- **Update controllers that call repository methods with changed signatures**
- **Update formatters/converters that use repository methods**
- **Common files to check**:
- `PetTypeFormatter.java` - often calls `findPetTypes()` method
- `*Controller.java` - may have pagination logic to remove
- Service classes that use repository methods
#### F. Update repository mocking in tests
- Remove pagination from repository mocks:
- `given(repository.findByX(param, pageable)).willReturn(pageResult)`
- → `given(repository.findByX(param)).willReturn(listResult)`
- Update method names in mocks:
- `given(petTypeRepository.findPetTypes()).willReturn(types)`
- → `given(petTypeRepository.findAllOrderByName()).willReturn(types)`
#### G. Fix utility classes used by tests
- Update `EntityUtils.java` or similar:
- Remove JPA-specific exception imports (`ObjectRetrievalFailureException`)
- Change method signatures from `int id` to `String id`
- Update ID comparison logic: `entity.getId() == entityId` → `entity.getId().equals(entityId)`
- Replace JPA exceptions with standard exceptions (`IllegalArgumentException`)
#### H. Update assertions for String IDs
- Change ID assertions:
- `assertThat(entity.getId()).isNotZero()` → `assertThat(entity.getId()).isNotEmpty()`
- `assertThat(entity.getId()).isEqualTo(1)` → `assertThat(entity.getId()).isEqualTo("test-id-1")`
- JSON path assertions: `jsonPath("$.id").value(1)` → `jsonPath("$.id").value("test-id-1")`
### Step 9 — **Runtime Testing and Template Compatibility**
#### **CRITICAL**: Test the running application after compilation success
- **Start the application**: `mvn spring-boot:run`
- **Navigate through all pages** in the web interface to identify runtime errors
- **Common runtime issues after conversion**:
- Templates trying to access properties that no longer exist (e.g., `vet.specialties`)
- Service layer not populating transient relationship properties
- Controllers not using service layer for relationship loading
#### **Template compatibility fixes**:
- **If templates access relationship properties** (e.g., `entity.relatedObjects`):
- Ensure transient properties exist on entities with proper getters/setters
- Verify service layer populates these transient properties
- Update `getNrOfXXX()` methods to use transient lists instead of ID lists
- **Check for SpEL (Spring Expression Language) errors** in logs:
- `Property or field 'xxx' cannot be found` → Add missing transient property
- `EL1008E` errors → Service layer not populating relationships
#### **Service layer verification**:
- **Ensure all controllers use service layer** instead of direct repository access
- **Verify service methods populate relationships** before returning entities
- **Test all CRUD operations** through the web interface
### Step 9.5 — **Template Runtime Validation** (CRITICAL)
#### **Systematic Template Testing Process**
After successful compilation and application startup:
1. **Navigate to EVERY page** in the application systematically
2. **Test each template that displays entity data**:
- List pages (e.g., `/vets`, `/owners`)
- Detail pages (e.g., `/owners/{id}`, `/vets/{id}`)
- Forms and edit pages
3. **Look for specific template errors**:
- `Property or field 'relationshipName' cannot be found on object of type 'EntityName'`
- `EL1008E` Spring Expression Language errors
- Empty or missing data where relationships should appear
#### **Template Error Resolution Checklist**
When encountering template errors:
- [ ] **Identify the missing property** from error message
- [ ] **Check if property exists as transient field** in entity
- [ ] **Verify service layer populates the property** before returning entity
- [ ] **Ensure controller uses service layer**, not direct repository access
- [ ] **Test the specific page again** after fixes
#### **Common Template Error Patterns**
- `Property or field 'specialties' cannot be found` → Add `@JsonIgnore private List<Specialty> specialties` to Vet entity
- `Property or field 'pets' cannot be found` → Add `@JsonIgnore private List<Pet> pets` to Owner entity
- Empty relationship data displayed → Service not populating transient properties
### Step 10 — **Systematic Error Resolution Process**
#### When compilation fails:
1. **Run `mvn compile` first** - fix main source issues before tests
2. **Run `mvn test-compile`** - systematically fix each test compilation error
3. **Focus on most frequent error patterns**:
- `int cannot be converted to String` → Change test constants and entity setters
- `method X cannot be applied to given types` → Remove pagination parameters
- `cannot find symbol: method Y()` → Update to new repository method names
- Method signature conflicts → Rename conflicting methods
### Step 10 — **Systematic Error Resolution Process**
#### When compilation fails:
1. **Run `mvn compile` first** - fix main source issues before tests
2. **Run `mvn test-compile`** - systematically fix each test compilation error
3. **Focus on most frequent error patterns**:
- `int cannot be converted to String` → Change test constants and entity setters
- `method X cannot be applied to given types` → Remove pagination parameters
- `cannot find symbol: method Y()` → Update to new repository method names
- Method signature conflicts → Rename conflicting methods
#### When runtime fails:
1. **Check application logs** for specific error messages
2. **Look for template/SpEL errors**:
- `Property or field 'xxx' cannot be found` → Add transient property to entity
- Missing relationship data → Service layer not populating relationships
3. **Verify service layer usage** in controllers
4. **Test navigation through all application pages**
#### Common error patterns and solutions:
- **`method findByLastNameStartingWith cannot be applied`** → Remove `Pageable` parameter
- **`cannot find symbol: method findPetTypes()`** → Change to `findAllOrderByName()`
- **`incompatible types: int cannot be converted to String`** → Update test ID constants
- **`method getPet(String) is already defined`** → Rename one method (e.g., `getPetByName`)
- **`cannot find symbol: method isNotZero()`** → Change to `isNotEmpty()` for String IDs
- **`Property or field 'specialties' cannot be found`** → Add transient property and populate in service
- **`ClassCastException: reactor.core.publisher.BlockingIterable cannot be cast to java.util.List`** → Fix repository `findAllWithEagerRelationships()` method to use StreamSupport
- **`Unable to make field...BigDecimal.intVal accessible`** → Replace BigDecimal with Double throughout application
- **Health check database failure** → Remove 'db' from health check readiness configuration
#### **Template-Specific Runtime Errors**
- **`Property or field 'XXX' cannot be found on object of type 'YYY'`**:
- Root cause: Template accessing relationship property that was converted to ID storage
- Solution: Add transient property to entity + populate in service layer
- Prevention: Always check template usage before converting relationships
- **`EL1008E` Spring Expression Language errors**:
- Root cause: Service layer not populating transient properties
- Solution: Verify `populateRelationships()` methods are called and working
- Prevention: Test all template navigation after service layer implementation
- **Empty/null relationship data in templates**:
- Root cause: Controller bypassing service layer or service not populating relationships
- Solution: Ensure all controller methods use service layer for entity retrieval
- Prevention: Never return repository results directly to templates
### Step 11 — Validation checklist
After conversion, verify:
- [ ] **Main application compiles**: `mvn compile` succeeds
- [ ] **All test files compile**: `mvn test-compile` succeeds
- [ ] **No compilation errors**: Address every single compilation error
- [ ] **Application starts successfully**: `mvn spring-boot:run` without errors
- [ ] **All web pages load**: Navigate through all application pages without runtime errors
- [ ] **Service layer populates relationships**: Transient properties are correctly set
- [ ] **All template pages render without errors**: Navigate through entire application
- [ ] **Relationship data displays correctly**: Lists, counts, and related objects show properly
- [ ] **No SpEL template errors in logs**: Check application logs during navigation
- [ ] **Transient properties are @JsonIgnore annotated**: Prevents JSON serialization issues
- [ ] **Service layer used consistently**: No direct repository access in controllers for template rendering
- [ ] No remaining `jakarta.persistence` imports
- [ ] All entity IDs are `String` type consistently
- [ ] All repository interfaces extend `CosmosRepository<Entity, String>`
- [ ] Configuration uses `DefaultAzureCredential` for authentication
- [ ] Data seeding component exists and works
- [ ] Test files use String IDs consistently
- [ ] Repository mocks updated for Cosmos methods
- [ ] **No method signature conflicts** in entity classes
- [ ] **All renamed methods updated** in callers (controllers, tests, formatters)
### Common pitfalls to avoid
1. **Not checking compilation frequently** - Run `mvn test-compile` after each major change
2. **Method signature conflicts** - Method overloading issues when converting ID types
3. **Forgetting to update method callers** - When renaming methods, update ALL callers
4. **Missing repository method renames** - Custom repository methods must be updated everywhere called
5. **Using key-based authentication** - Use `DefaultAzureCredential` instead
6. **Mixing Integer and String IDs** - Be consistent with String IDs everywhere, especially in tests
7. **Not updating controller pagination logic** - Remove pagination from controllers when repositories change
8. **Leaving JPA-specific test annotations** - Replace with Cosmos-compatible alternatives
9. **Incomplete test file updates** - Search entire test directory, not just obvious files
10. **Skipping runtime testing** - Always test the running application, not just compilation
11. **Missing service layer** - Don't access repositories directly from controllers
12. **Forgetting transient properties** - Templates may need access to relationship data
13. **Not testing template navigation** - Compilation success doesn't mean templates work
14. **Missing transient properties for templates** - Templates need object access, not just IDs
15. **Service layer bypassing** - Controllers must use services, never direct repository access
16. **Incomplete relationship population** - Service methods...
© github · MIT · github/awesome-copilot