jakartaee / jakartaee/persistence

TCK Department4 uses mixed access strategy without @Access, leading to undefined behavior

Open
#1,010 4 comments 0 reactions 0 assignees View on GitHub
accepted challenge
Dominant language
Java
Stars
267
Forks
78
Avg merge
1d 6h
Merged PRs (30d)
13

Description

## Summary
`https://github.com/jakartaee/persistence/blob/main/tck/spec-tests/src/main/java/ee/jakarta/tck/persistence/core/annotations/mapkeyenumerated/Client.java`

`https://github.com/jakartaee/persistence/blob/main/tck/spec-tests/src/main/java/ee/jakarta/tck/persistence/core/annotations/mapkeyenumerated/Department4.java`

`Department4` in the `mapkeyenumerated` test uses mixed access in a way that is not compliant with Jakarta Persistence access rules, and that is the root cause of the apparent standalone success.

Related to: https://github.com/jakartaee/platform-tck/issues/2678

both tested with hibernate 7.2.6

## Why it can appear to succeed

`Department4` declares `@Id` on the field, so the entity uses implicit **field access**.

At the same time:

- `lastNameEmployees` is marked `@Transient` on the field
- the actual mapping annotations for `lastNameEmployees` are placed on the getter:
- `@ElementCollection`
- `@CollectionTable`
- `@AttributeOverrides`
- `@MapKeyEnumerated`

Under field access, those getter annotations are ignored. That means `lastNameEmployees` is **not a persistent attribute**.

However, the test setup assigns the map in Java before persistence:

```java
deptRef4[0].setLastNameEmployees(link4);
```

So in standalone execution, the test can appear to pass if it reuses the same managed `Department4` instance or stale in-memory state created during setup. In that case, the plain Java field may still contain the map even though the provider never mapped or loaded it from the database.

Once the persistence context is cleared or a fresh entity instance is loaded, the field is `null` and the test fails.

In other words, the outcome can depend on persistence-context reuse rather than actual mapping correctness.

## How to check

Add a metamodel probe in `elementCollectionTest`:

```java
var entityType = getEntityManager().getMetamodel().entity(Department4.class);
logger.log(Logger.Level.INFO, "Department4 attributes:");
for (var a : entityType.getAttributes()) {
logger.log(Logger.Level.INFO, " " + a.getName() + " / " + a.getPersistentAttributeType());
}
logger.log(
Logger.Level.INFO,
"Has lastNameEmployees = " +
entityType.getAttributes().stream()
.anyMatch(a -> a.getName().equals("lastNameEmployees"))
);
```

### Observed result

```text
Department4 attributes:
id / BASIC
name / BASIC
Has lastNameEmployees = false
```

This shows that `lastNameEmployees` is not part of the persistent metamodel.

## How to reproduce the failure reliably

To eliminate false success caused by reused in-memory state, clear the persistence context before loading the entity:

```java
getEntityManager().clear(); // notice i have cleared context , this shouldn't matter because it would be fine to get emp from database if mapping worked
getEntityTransaction().begin();

Employee4 emp = getEntityManager().find(Employee4.class, 8);
logger.log(Logger.Level.TRACE, "Name:" + emp.getLastName());

Department4 dept = emp.getDepartment();
logger.log(Logger.Level.INFO, "dept managed = " + getEntityManager().contains(dept));
logger.log(Logger.Level.INFO, "map null = " + (dept.getLastNameEmployees() == null));

Map emps = dept.getLastNameEmployees();
if (emps == null) {
throw new IllegalStateException("Department4.lastNameEmployees is null after reload");
}
```

### Observed result

```text
dept managed = true
map null = true
```

This proves the collection is not being loaded from the database, because it is not mapped.

## Root cause

`Department4` mixes access styles:

- field access is selected by `@Id` on the field
- `lastNameEmployees` is excluded with field-level `@Transient`
- mapping annotations for `lastNameEmployees` are incorrectly placed on the getter

As a result, `lastNameEmployees` is ignored as a persistent attribute.

## Suggested fix

Make `Department4` use a single consistent access strategy.

Contributor guide

Open the contributing guide

Research direction

Start with tck/spec-tests/src/main/java/ee/jakarta/tck/persistence/core/annotations/mapkeyenumerated/Department4.java and its use in Client.java, then inspect the elementCollectionTest path. Check the Department4 metamodel and reload behavior after clearing the persistence context; done means lastNameEmployees is persistent and is populated from the database rather than reused in-memory state.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
database, testing
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
54/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.