diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index c32dd3f48..f457ea7d3 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -8,6 +8,10 @@ and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Version == [Unreleased] +=== Fixed + +- Update query at Oracle NoSQL to support parameter with enum type + == [1.1.8] - 2025-05-21 === Changed diff --git a/jnosql-oracle-nosql/src/main/java/org/eclipse/jnosql/databases/oracle/communication/AbstractQueryBuilder.java b/jnosql-oracle-nosql/src/main/java/org/eclipse/jnosql/databases/oracle/communication/AbstractQueryBuilder.java index 0793a691f..d731cf0b7 100644 --- a/jnosql-oracle-nosql/src/main/java/org/eclipse/jnosql/databases/oracle/communication/AbstractQueryBuilder.java +++ b/jnosql-oracle-nosql/src/main/java/org/eclipse/jnosql/databases/oracle/communication/AbstractQueryBuilder.java @@ -94,8 +94,8 @@ protected void predicateBetween(StringBuilder query,List params, Ele ((Iterable) document.get()).forEach(values::add); query.append(name).append(" BETWEEN ? AND ? "); - FieldValue fieldValue = FieldValueConverter.INSTANCE.of(values.get(ORIGIN)); - FieldValue fieldValue2 = FieldValueConverter.INSTANCE.of(values.get(1)); + FieldValue fieldValue = FieldValueConverter.of(values.get(ORIGIN)); + FieldValue fieldValue2 = FieldValueConverter.of(values.get(1)); params.add(fieldValue); params.add(fieldValue2); } @@ -125,7 +125,7 @@ protected void predicate(StringBuilder query, List params) { String name = identifierOf(document.name()); Object value = document.get(); - FieldValue fieldValue = FieldValueConverter.INSTANCE.of(value); + FieldValue fieldValue = FieldValueConverter.of(value); if(fieldValue.isArray()){ query.append(name).append(condition).append(" ?[] "); } else { diff --git a/jnosql-oracle-nosql/src/main/java/org/eclipse/jnosql/databases/oracle/communication/DefaultOracleNoSQLDocumentManager.java b/jnosql-oracle-nosql/src/main/java/org/eclipse/jnosql/databases/oracle/communication/DefaultOracleNoSQLDocumentManager.java index 679f5decf..3a83420a6 100644 --- a/jnosql-oracle-nosql/src/main/java/org/eclipse/jnosql/databases/oracle/communication/DefaultOracleNoSQLDocumentManager.java +++ b/jnosql-oracle-nosql/src/main/java/org/eclipse/jnosql/databases/oracle/communication/DefaultOracleNoSQLDocumentManager.java @@ -247,7 +247,7 @@ public Stream sql(String query) { public Stream sql(String query, Object... params) { Objects.requireNonNull(query, "query is required"); Objects.requireNonNull(params, "params is required"); - List fields = Arrays.stream(params).map(FieldValueConverter.INSTANCE::of).toList(); + List fields = Arrays.stream(params).map(FieldValueConverter::of).toList(); return executeSQL(query, fields).stream(); } @@ -274,7 +274,7 @@ private List executeSQL(String sql, List params } for (Map.Entry entry : result) { if (isNotOracleField(entry)) { - entity.add(Element.of(entry.getKey(), FieldValueConverter.INSTANCE.of(entry.getValue()))); + entity.add(Element.of(entry.getKey(), FieldValueConverter.of(entry.getValue()))); } } var id = result.get(ORACLE_ID).asString().getValue().split(":")[1]; diff --git a/jnosql-oracle-nosql/src/main/java/org/eclipse/jnosql/databases/oracle/communication/FieldValueConverter.java b/jnosql-oracle-nosql/src/main/java/org/eclipse/jnosql/databases/oracle/communication/FieldValueConverter.java index d0fe18d38..a5a1d622d 100644 --- a/jnosql-oracle-nosql/src/main/java/org/eclipse/jnosql/databases/oracle/communication/FieldValueConverter.java +++ b/jnosql-oracle-nosql/src/main/java/org/eclipse/jnosql/databases/oracle/communication/FieldValueConverter.java @@ -27,47 +27,48 @@ import oracle.nosql.driver.values.StringValue; import java.lang.reflect.Array; +import java.util.List; import java.util.Map; -enum FieldValueConverter { +class FieldValueConverter { + private static final List MAPPERS = List.of( + new FieldValuePassthroughMapper(), + new StringValueMapper(), + new IntegerValueMapper(), + new LongValueMapper(), + new DoubleValueMapper(), + new BooleanValueMapper(), + new NumberValueMapper(), + new ByteArrayValueMapper(), + new EnumValueMapper(), + new IterableValueMapper(), + new ArrayValueMapper(), + new MapValueMapper() + ); - INSTANCE; + private FieldValueConverter() { + throw new AssertionError("Utility class"); + } - FieldValue of(Object value){ - if(value == null){ + static FieldValue of(Object value) { + if (value == null) { return NullValue.getInstance(); } - if (value instanceof String string) { - return new StringValue(string); - } else if (value instanceof Integer integer) { - return new IntegerValue(integer); - } else if (value instanceof Long longValue) { - return new LongValue(longValue); - } else if (value instanceof Double doubleValue) { - return new DoubleValue(doubleValue); - } else if (value instanceof Boolean booleanValue) { - return Boolean.TRUE.equals(booleanValue) ? BooleanValue.trueInstance() : BooleanValue.falseInstance(); - } else if (value instanceof Number) { - return new NumberValue(value.toString()); - } else if (value instanceof byte[]) { - return new BinaryValue((byte[]) value); - } else if (value instanceof Iterable values) { - return createList(values); - } else if (value.getClass().isArray()) { - return createArray(value); - } else if (value instanceof Map) { - return entries((Map) value); - }else if (value instanceof FieldValue) { - return (FieldValue) value; - } else { - throw new UnsupportedOperationException("There is not support to: " + value.getClass()); + + for (FieldValueMapper mapper : MAPPERS) { + if (mapper.supports(value)) { + return mapper.toFieldValue(value); + } } + + throw new UnsupportedOperationException("Unsupported value type: " + value.getClass()); } - Object toObject(FieldValue value) { - if (value.isNull()) { + public static Object toJavaObject(FieldValue value) { + if (value == null || value.isNull()) { return null; } + return switch (value.getType()) { case STRING -> value.asString(); case INTEGER -> value.asInteger(); @@ -78,31 +79,153 @@ Object toObject(FieldValue value) { case BINARY -> value.asBinary(); case ARRAY -> value.asArray(); case MAP -> value.asMap(); - default -> throw new UnsupportedOperationException("There is not support to: " + value.getType()); + default -> throw new UnsupportedOperationException("Unsupported FieldValue type: " + value.getType()); }; } - private MapValue entries(Map value) { - MapValue mapValue = new MapValue(); - for (Map.Entry entry : value.entrySet()) { - mapValue.put(entry.getKey(), of(entry.getValue())); + + private interface FieldValueMapper { + boolean supports(Object value); + FieldValue toFieldValue(Object value); + } + + private static final class FieldValuePassthroughMapper implements FieldValueMapper { + public boolean supports(Object value) { + return value instanceof FieldValue; + } + + public FieldValue toFieldValue(Object value) { + return (FieldValue) value; + } + } + + private static final class StringValueMapper implements FieldValueMapper { + public boolean supports(Object value) { + return value instanceof String; + } + + public FieldValue toFieldValue(Object value) { + return new StringValue((String) value); + } + } + + private static final class IntegerValueMapper implements FieldValueMapper { + public boolean supports(Object value) { + return value instanceof Integer; + } + + public FieldValue toFieldValue(Object value) { + return new IntegerValue((Integer) value); + } + } + + private static final class LongValueMapper implements FieldValueMapper { + public boolean supports(Object value) { + return value instanceof Long; + } + + public FieldValue toFieldValue(Object value) { + return new LongValue((Long) value); + } + } + + private static final class DoubleValueMapper implements FieldValueMapper { + public boolean supports(Object value) { + return value instanceof Double; + } + + public FieldValue toFieldValue(Object value) { + return new DoubleValue((Double) value); + } + } + + private static final class BooleanValueMapper implements FieldValueMapper { + public boolean supports(Object value) { + return value instanceof Boolean; + } + + public FieldValue toFieldValue(Object value) { + return Boolean.TRUE.equals(value) + ? BooleanValue.trueInstance() + : BooleanValue.falseInstance(); + } + } + + private static final class NumberValueMapper implements FieldValueMapper { + public boolean supports(Object value) { + return value instanceof Number && + !(value instanceof Integer || value instanceof Long || value instanceof Double); + } + + public FieldValue toFieldValue(Object value) { + return new NumberValue(value.toString()); + } + } + + private static final class ByteArrayValueMapper implements FieldValueMapper { + public boolean supports(Object value) { + return value instanceof byte[]; + } + + public FieldValue toFieldValue(Object value) { + return new BinaryValue((byte[]) value); + } + } + + private static final class EnumValueMapper implements FieldValueMapper { + public boolean supports(Object value) { + return value instanceof Enum; + } + + public FieldValue toFieldValue(Object value) { + return new StringValue(((Enum) value).name()); + } + } + + private static final class IterableValueMapper implements FieldValueMapper { + public boolean supports(Object value) { + return value instanceof Iterable; + } + + public FieldValue toFieldValue(Object value) { + ArrayValue array = new ArrayValue(); + for (Object item : (Iterable) value) { + array.add(FieldValueConverter.of(item)); + } + return array; } - return mapValue; } - private ArrayValue createArray(Object value) { - var arrayValue = new ArrayValue(); - int length = Array.getLength(value); - for (int i = 0; i < length; i ++) { - arrayValue.add(of(Array.get(value, i))); + private static final class ArrayValueMapper implements FieldValueMapper { + public boolean supports(Object value) { + return value != null && value.getClass().isArray(); + } + + public FieldValue toFieldValue(Object value) { + int length = Array.getLength(value); + ArrayValue array = new ArrayValue(); + for (int i = 0; i < length; i++) { + array.add(FieldValueConverter.of(Array.get(value, i))); + } + return array; } - return arrayValue; } - private ArrayValue createList(Iterable values) { - var arrayValue = new ArrayValue(); - for (Object value : values) { - arrayValue.add(of(value)); + private static final class MapValueMapper implements FieldValueMapper { + public boolean supports(Object value) { + return value instanceof Map; + } + + public FieldValue toFieldValue(Object value) { + Map map = (Map) value; + MapValue mapValue = new MapValue(); + for (Map.Entry entry : map.entrySet()) { + Object key = entry.getKey(); + if (!(key instanceof String keyStr)) { + throw new IllegalArgumentException("Map keys must be strings. Found: " + key); + } + mapValue.put(keyStr, FieldValueConverter.of(entry.getValue())); + } + return mapValue; } - return arrayValue; } } diff --git a/jnosql-oracle-nosql/src/test/java/org/eclipse/jnosql/databases/oracle/communication/OracleNoSQLDocumentManagerTest.java b/jnosql-oracle-nosql/src/test/java/org/eclipse/jnosql/databases/oracle/communication/OracleNoSQLDocumentManagerTest.java index 7b916e6fd..7478e32e9 100644 --- a/jnosql-oracle-nosql/src/test/java/org/eclipse/jnosql/databases/oracle/communication/OracleNoSQLDocumentManagerTest.java +++ b/jnosql-oracle-nosql/src/test/java/org/eclipse/jnosql/databases/oracle/communication/OracleNoSQLDocumentManagerTest.java @@ -563,6 +563,48 @@ void shouldQueryParams(){ assertThat(names).contains("Poliana"); } + @Test + void shouldInsertAndRetrieveWithEnum() { + var entity = CommunicationEntity.of(COLLECTION_NAME); + String id = UUID.randomUUID().toString(); + entity.add("_id", id); + entity.add("name", "Test Name"); + entity.add("contact_type", ContactType.EMAIL); + entityManager.insert(entity); + + var query = select().from(COLLECTION_NAME) + .where("_id").eq(id).build(); + Optional optional = entityManager.select(query).findFirst(); + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(optional).isPresent(); + CommunicationEntity documentEntity = optional.get(); + soft.assertThat(documentEntity.find("name").orElseThrow().get(String.class)).isEqualTo("Test Name"); + soft.assertThat(documentEntity.find("contact_type").orElseThrow().get(ContactType.class)) + .isEqualTo(ContactType.EMAIL); + }); + } + + @Test + void shouldDoQueryUsingEnumAsParameter() { + var entity = CommunicationEntity.of(COLLECTION_NAME); + String id = UUID.randomUUID().toString(); + entity.add("_id", id); + entity.add("name", "Test Name"); + entity.add("contact_type", ContactType.EMAIL); + entityManager.insert(entity); + + var query = select().from(COLLECTION_NAME) + .where("contact_type").eq(ContactType.EMAIL).build(); + Optional optional = entityManager.select(query).findFirst(); + SoftAssertions.assertSoftly(soft -> { + soft.assertThat(optional).isPresent(); + CommunicationEntity documentEntity = optional.get(); + soft.assertThat(documentEntity.find("name").orElseThrow().get(String.class)).isEqualTo("Test Name"); + soft.assertThat(documentEntity.find("contact_type").orElseThrow().get(ContactType.class)) + .isEqualTo(ContactType.EMAIL); + }); + } + private CommunicationEntity createDocumentList() { var entity = CommunicationEntity.of("AppointmentBook"); entity.add(Element.of("_id", new Random().nextInt()));