Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@ public String paramAnnosForUpdate() {
return By.class.getName();
}

@Override
@Trivial
public String persistenceFeatureName() {
return "persistence-3.2";
}

@Override
@Trivial
public Set<Class<?>> resourceAccessorTypes(boolean stateful) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,12 @@ public String paramAnnosForUpdate() {
SubtractFrom.class.getSimpleName();
}

@Override
@Trivial
public String persistenceFeatureName() {
return "persistence-4.0";
}

@Override
@Trivial
public Set<Class<?>> resourceAccessorTypes(boolean stateful) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1220,3 +1220,24 @@ CWWKD1120.cursor.keyword.mismatch.explanation=Keywords such as GROUP BY, HAVING,
pagination.
CWWKD1120.cursor.keyword.mismatch.useraction=Rewrite the query to include only \
the clauses: SELECT, FROM, and WHERE.

CWWKD1121.record.lacks.id=CWWKD1121E: The {0} record cannot be used as an entity \
because it lacks a unqiue identifier. One of the record components ({1}) must \
Copy link

@helyarp helyarp Dec 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of One of the record components ({1}), change it to The ({1}) record component

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@helyarp thanks for reviewing so quickly.
{1} is actually a list of the record components, so this will look like,

CWWKD1121E: The org.example.Person record cannot be used as an entity
because it lacks a unqiue identifier. One of the record components (ssn, firstName, lastName, birthday) must ...

so it would be incorrect to say "The record component" because the value has multiple.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@njr-11 Hi, thanks for the explanation. Keep what you have. I'll add the ID reviewed label.

be named id, have a name that ends with _id, Id or ID, or be of the type \
java.util.UUID.
CWWKD1121.record.lacks.id.explanation=Entities are required to have a unique \
identifier attribute.
CWWKD1121.record.lacks.id.useraction=Add or rename a record component to \
follow one of the stated conventions for assigning a unique identifier.

CWWKD1122.entity.lacks.id=CWWKD1122E: The {0} class cannot be used as an \
entity because it lacks a unique identifier. To assign a unique identifier, \
ensure the {1} feature is enabled and add the @jakarta.persistence.Entity annotation \
to the entity class and add the @jakarta.persistence.Id annotation to at most one \
field or getter method of the entity class. \
Alternatively, use a META-INF/orm.xml file to \
define the entity and its unique identifier.
CWWKD1122.entity.lacks.id.explanation=Entities are required to have a unique \
identifier attribute.
CWWKD1122.entity.lacks.id.useraction=Enable the Jakarta Persistence feature \
and follow the Jakarta Persistence entity model to assign a unique identifier.
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@
import java.util.TreeSet;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.ibm.websphere.ras.Tr;
import com.ibm.websphere.ras.TraceComponent;
import com.ibm.websphere.ras.annotation.Trivial;

import io.openliberty.data.internal.persistence.DataProvider;
import io.openliberty.data.internal.persistence.Util;
import io.openliberty.data.internal.persistence.orm.Models.AccessType;
import io.openliberty.data.internal.persistence.orm.Models.Attribute;
Expand Down Expand Up @@ -90,6 +92,7 @@ public class EntityParser {
private final LinkedHashSet<String> classNames;

// Global configurations
private final DataProvider provider;
private final String tablePrefix;

// State controls flow from initialization to generation
Expand All @@ -99,8 +102,14 @@ public class EntityParser {
private Class<?> currentEntity;
private Set<Attribute> idAttributes;

/**
* Construct a new entity parser.
*
* @param tablePrefix the table prefix or empty string if none.
* @param provider OSGi service representing this Data provider.
*/
@Trivial
public EntityParser(String tablePrefix) {
public EntityParser(String tablePrefix, DataProvider provider) {
this.mappedSuperclasses = new HashMap<>();
this.entities = new HashMap<>();
this.embeddables = new HashMap<>();
Expand All @@ -113,6 +122,7 @@ public EntityParser(String tablePrefix) {
this.tableNames = new LinkedHashSet<>();
this.classNames = new LinkedHashSet<>();

this.provider = provider;
this.tablePrefix = tablePrefix;

this.doneParsing = false;
Expand Down Expand Up @@ -543,17 +553,24 @@ private void foundConverter(Convert convert) {
@Trivial
private void verify() {
if (idAttributes.isEmpty()) {
EntityRecord invalid = entities.get(currentEntity);
Set<Class<?>> supers = entitiesSuperclasses.get(currentEntity);
Set<MappedSuperclass> invalidSupers = supers == null || supers.isEmpty() ? //
Set.of() : //
supers.stream() //
.map(c -> mappedSuperclasses.get(c))//
.collect(Collectors.toSet());

//TODO NLS
throw new MappingException("The entity " + invalid + " had no id attribute"
+ (invalidSupers.isEmpty() ? " " : " nor was any id attribute found on any mapped superclass " + invalidSupers));
Class<?> recordEntityClass = entityToRecord.get(currentEntity);
if (recordEntityClass == null) {
throw exc(MappingException.class,
"CWWKD1122.entity.lacks.id",
currentEntity.getName(),
provider.compat.persistenceFeatureName());
} else { // a Java record entity
String recordComponentNames = Stream //
.of(recordEntityClass.getRecordComponents()) //
.map(RecordComponent::getName) //
.reduce("", (s, n) -> s.length() == 0 //
? n //
: (s + ", " + n));
throw exc(MappingException.class,
"CWWKD1121.record.lacks.id",
recordEntityClass.getName(),
recordComponentNames);
}
}

if (idAttributes.size() > 1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ public DBStoreEMBuilder(DataProvider provider,

ArrayList<InMemoryMappingFile> generatedEntities = new ArrayList<InMemoryMappingFile>();

EntityParser parser = new EntityParser(tablePrefix);
EntityParser parser = new EntityParser(tablePrefix, provider);

for (Class<?> c : entityTypes) {
if (c.isAnnotationPresent(Entity.class)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class EntityParserErrorTests {

@Test
public void noIdEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);

try {
p.parseUnannotatedEntity(WithoutId.class);
Expand All @@ -37,7 +37,7 @@ public void noIdEntityTest() {

@Test
public void noIdInMappedSuperclassEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);

try {
p.parseUnannotatedEntity(WithoutIdMappedSuperclass.class);
Expand All @@ -53,7 +53,7 @@ public void noIdInMappedSuperclassEntityTest() {

@Test
public void multipleIdInMappedSuperclassEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);

try {
p.parseUnannotatedEntity(WithMultipleIds.class);
Expand All @@ -69,7 +69,7 @@ public void multipleIdInMappedSuperclassEntityTest() {

@Test
public void invalidConverterEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);

try {
p.parseUnannotatedEntity(WithConverterInvalid.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class EntityParserTests {

@Test
public void simpleEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseUnannotatedEntity(Simple.class);
List<String> xmls = p.generateView();

Expand All @@ -48,7 +48,7 @@ public void simpleEntityTest() {

@Test
public void simpleEntityWithPrefixTest() {
EntityParser p = new EntityParser("prefix");
EntityParser p = new EntityParser("prefix", null);
p.parseUnannotatedEntity(Simple.class);
List<String> xmls = p.generateView();

Expand All @@ -74,7 +74,7 @@ public void simpleEntityWithPrefixTest() {

@Test
public void versionedEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseUnannotatedEntity(Versioned.class);
List<String> xmls = p.generateView();

Expand All @@ -101,7 +101,7 @@ public void versionedEntityTest() {

@Test
public void recordEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseRecord(RecordEntity.class, RecordEntityEntity.class);
List<String> xmls = p.generateView();

Expand Down Expand Up @@ -129,7 +129,7 @@ public void recordEntityTest() {

@Test
public void recordComplexEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseRecord(RecordComplex.class, RecordComplexEntity.class);
List<String> xmls = p.generateView();

Expand Down Expand Up @@ -174,7 +174,7 @@ public void recordComplexEntityTest() {

@Test
public void collectionEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseUnannotatedEntity(Collection.class);
List<String> xmls = p.generateView();

Expand Down Expand Up @@ -202,7 +202,7 @@ public void collectionEntityTest() {

@Test
public void collectionEmbeddedEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseUnannotatedEntity(CollectionEmbedded.class);
List<String> xmls = p.generateView();

Expand Down Expand Up @@ -248,7 +248,7 @@ public void collectionEmbeddedEntityTest() {

@Test
public void propertyEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseUnannotatedEntity(Property.class);
List<String> xmls = p.generateView();

Expand Down Expand Up @@ -278,7 +278,7 @@ public void propertyEntityTest() {

@Test
public void embeddedEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseUnannotatedEntity(WithEmbedded.class);
List<String> xmls = p.generateView();

Expand Down Expand Up @@ -319,7 +319,7 @@ public void embeddedEntityTest() {

@Test
public void embeddedIdEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseUnannotatedEntity(WithEmbeddedId.class);
List<String> xmls = p.generateView();

Expand Down Expand Up @@ -359,7 +359,7 @@ public void embeddedIdEntityTest() {

@Test
public void embeddedRecordEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseUnannotatedEntity(WithEmbeddedRecord.class);
List<String> xmls = p.generateView();

Expand Down Expand Up @@ -402,7 +402,7 @@ public void embeddedRecordEntityTest() {

@Test
public void mappedSuperClassEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseUnannotatedEntity(WithMappedSuperclass.class);
List<String> xmls = p.generateView();

Expand Down Expand Up @@ -478,7 +478,7 @@ public void mappedSuperClassEntityTest() {

@Test
public void mappedSuperClassEmbeddedIdEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseUnannotatedEntity(WithMappedSuperclassPrime.class);
List<String> xmls = p.generateView();

Expand Down Expand Up @@ -572,7 +572,7 @@ public void mappedSuperClassEmbeddedIdEntityTest() {

@Test
public void converterEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseUnannotatedEntity(WithConverter.class);
List<String> xmls = p.generateView();

Expand Down Expand Up @@ -619,7 +619,7 @@ public void converterEntityTest() {

@Test
public void converterComplexEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseUnannotatedEntity(WithConverterComplex.class);
List<String> xmls = p.generateView();

Expand Down Expand Up @@ -685,7 +685,7 @@ public void converterComplexEntityTest() {

@Test
public void annotatedWithConverterEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseAnnotatedEntity(WithEntityAnnotation.class);
List<String> xmls = p.generateView();

Expand All @@ -712,7 +712,7 @@ public void annotatedWithConverterEntityTest() {

@Test
public void multilayerEmbeddedEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseUnannotatedEntity(WithMultilayerEmbedded.class);
List<String> xmls = p.generateView();

Expand Down Expand Up @@ -782,7 +782,7 @@ public void multilayerEmbeddedEntityTest() {

@Test
public void multilayerEmbeddedCollectionEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseUnannotatedEntity(WithMultilayerEmbeddedCollection.class);
List<String> xmls = p.generateView();

Expand Down Expand Up @@ -854,7 +854,7 @@ public void multilayerEmbeddedCollectionEntityTest() {

@Test
public void embeddedMulilayerCollectionEntityTest() {
EntityParser p = new EntityParser("");
EntityParser p = new EntityParser("", null);
p.parseUnannotatedEntity(WithEmbeddedMultilayerCollection.class);
List<String> xmls = p.generateView();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,14 @@ boolean isSpecialParamValid(Class<?> paramType,
*/
String paramAnnosForUpdate();

/**
* Returns the name of the Liberty feature that provides Jakarta Persistence.
* For example, persistence-3.2.
*
* @return the name of the Liberty feature that provides Jakarta Persistence.
*/
String persistenceFeatureName();

/**
* List of valid return types for resource accessor methods.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ public class DataErrPathsTest extends FATServletClient {
"CWWKD1120E.*groupedByAddress", // cursor pagination with GROUP BY
"CWWKD1120E.*unionOfAddresses", // cursor pagination with UNION
"CWWKD1120E.*withNameAndAddress", // cursor pagination with INTERSECT
"CWWKD1120E.*withNameNotAddress" // cursor pagination with EXCEPT
"CWWKD1120E.*withNameNotAddress", // cursor pagination with EXCEPT
"CWWKD1121E.*VoterRegistration" // record entity without id
};

@Server("io.openliberty.data.internal.fat.errpaths")
Expand Down
Loading