eclipse / jnosql

Eclipse JNoSQL is a framework which has the goal to help Java developers to create Jakarta EE applications with NoSQL.
Other
231 stars 72 forks source link

[BUG] Array columns are not supported onto entities that are implemented using record #544

Closed dearrudam closed 2 months ago

dearrudam commented 2 months ago

Which JNoSQL project the issue refers to?

JNoSQL (Core)

Bug description

Entities implemented as a Record class with array columns fail during the save process. The save process is executed partially, the data is stored on the database but an exception is raised breaking the execution flow.

The application sample uses MongoDB as the database. Here is the evidence with the stored data in the database:


olympus> db.Baggage.find({}).pretty()
[
  {
    _id: '0897964861',
    items: [
      {
        size: 'LARGE',
        description: 'Nostrum et quo omnis.',
        weight: 34
      },
      { size: 'MEDIUM', description: 'Voluptatem eum.', weight: 3 },
      {
        size: 'SMALL',
        description: 'Sapiente dolorem architecto tempore ut aperiam.',
        weight: 39
      },
      {
        size: 'SMALL',
        description: 'Corrupti non est ullam in omnis qui.',
        weight: 29
      },
      { size: 'SMALL', description: 'Id velit.', weight: 16 }
    ]
  }
]

Here's the showed exception:

Exception in thread "main" java.lang.UnsupportedOperationException: The type class [Lorg.jnosql.demo.se.BaggageItem; is not supported yet
    at org.eclipse.jnosql.communication.ValueReaderDecorator.lambda$read$2(ValueReaderDecorator.java:56)
    at java.base/java.util.Optional.orElseThrow(Optional.java:403)
    at org.eclipse.jnosql.communication.ValueReaderDecorator.read(ValueReaderDecorator.java:55)
    at org.eclipse.jnosql.communication.DefaultValue.get(DefaultValue.java:48)
    at org.eclipse.jnosql.communication.semistructured.DefaultElement.get(DefaultElement.java:27)
    at org.eclipse.jnosql.mapping.semistructured.ParameterConverter$1.lambda$convert$1(ParameterConverter.java:41)
    at java.base/java.util.Optional.ifPresentOrElse(Optional.java:198)
    at org.eclipse.jnosql.mapping.semistructured.ParameterConverter$1.convert(ParameterConverter.java:38)
    at org.eclipse.jnosql.mapping.semistructured.EntityConverter.lambda$convertEntityByConstructor$8(EntityConverter.java:187)
    at java.base/java.util.Optional.ifPresentOrElse(Optional.java:196)
    at org.eclipse.jnosql.mapping.semistructured.EntityConverter.convertEntityByConstructor(EntityConverter.java:185)
    at org.eclipse.jnosql.mapping.semistructured.EntityConverter.toEntity(EntityConverter.java:175)
    at org.eclipse.jnosql.mapping.semistructured.EntityConverter.toEntity(EntityConverter.java:116)
    at org.eclipse.jnosql.mapping.semistructured.DefaultEntityConverter$Proxy$_$$_WeldClientProxy.toEntity(Unknown Source)
    at org.eclipse.jnosql.mapping.semistructured.AbstractSemiStructuredTemplate.lambda$persist$8(AbstractSemiStructuredTemplate.java:341)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
    at java.base/java.util.stream.Streams$StreamBuilderImpl.tryAdvance(Streams.java:397)
    at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:129)
    at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
    at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:647)
    at org.eclipse.jnosql.mapping.semistructured.AbstractSemiStructuredTemplate.persist(AbstractSemiStructuredTemplate.java:343)
    at org.eclipse.jnosql.mapping.semistructured.AbstractSemiStructuredTemplate.insert(AbstractSemiStructuredTemplate.java:104)
    at org.eclipse.jnosql.mapping.document.DefaultDocumentTemplate$Proxy$_$$_WeldClientProxy.insert(Unknown Source)
    at org.eclipse.jnosql.mapping.core.query.AbstractRepository.save(AbstractRepository.java:112)
    at org.eclipse.jnosql.mapping.core.query.AnnotationOperation$4.invoke(AnnotationOperation.java:184)
    at org.eclipse.jnosql.mapping.semistructured.query.CustomRepositoryHandler.lambda$invoke$0(CustomRepositoryHandler.java:93)
    at org.eclipse.jnosql.mapping.semistructured.query.CustomRepositoryHandler.unwrapInvocationTargetException(CustomRepositoryHandler.java:160)
    at org.eclipse.jnosql.mapping.semistructured.query.CustomRepositoryHandler.invoke(CustomRepositoryHandler.java:93)
    at jdk.proxy2/jdk.proxy2.$Proxy46.register(Unknown Source)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at org.jboss.weld.bean.proxy.AbstractBeanInstance.invoke(AbstractBeanInstance.java:38)
    at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:106)
    at org.jnosql.demo.se.BaggageRepository$1745506090$Proxy$_$$_WeldClientProxy.register(Unknown Source)
    at org.jnosql.demo.se.App.main(App.java:31)

JNoSQL Version

1.1.2-SNAPSHOT

Steps To Reproduce

  1. Create a sample project with the following dependencies:
<dependency>
    <groupId>org.eclipse.jnosql.databases</groupId>
    <artifactId>jnosql-mongodb</artifactId>
    <version>${jnosql.version}</version>
</dependency>
<!-- For test purposes -->
<dependency>
    <groupId>net.datafaker</groupId>
    <artifactId>datafaker</artifactId>
    <version>2.0.2</version>
</dependency>
  1. And create the following entities and repository:
package org.jnosql.demo.se;

import jakarta.nosql.Column;
import jakarta.nosql.Entity;
import jakarta.nosql.Id;
import net.datafaker.Faker;

import java.util.stream.IntStream;

@Entity
public record Baggage(
        @Id String ticket,
        @Column BaggageItem[] items
) {
    public static Baggage create(Faker faker) {
        return new Baggage(
                faker.code().isbn10(),
                IntStream.rangeClosed(0, faker.random().nextInt(1, 5))
                        .mapToObj(i -> BaggageItem.of(faker))
                        .toArray(BaggageItem[]::new)
        );
    }
}
package org.jnosql.demo.se;

import jakarta.nosql.Column;
import jakarta.nosql.Entity;
import net.datafaker.Faker;

@Entity
public record BaggageItem(
        @Column String description,
        @Column Integer weight,
        @Column BaggageItemSize size
) {
    public static BaggageItem of(Faker faker) {
        return new BaggageItem(
                faker.lorem().sentence(faker.random().nextInt(1, 2)),
                faker.random().nextInt(1, 50),
                BaggageItemSize.values()[faker.random().nextInt(0, 2)]);
    }
}
package org.jnosql.demo.se;

import jakarta.data.repository.By;
import jakarta.data.repository.Delete;
import jakarta.data.repository.Find;
import jakarta.data.repository.Repository;
import jakarta.data.repository.Save;

import java.util.Arrays;
import java.util.List;

@Repository
public interface BaggageRepository {

    @Save
    Baggage register(Baggage baggage);

    default void registerAll(Baggage... baggages) {
        Arrays.stream(baggages).forEach(this::register);
    }

    @Find
    Baggage findByTicket(@By("ticket") String ticket);

    @Find
    List<Baggage> findAll();

    @Delete
    void unregister(Baggage baggage);

    default void unregisterAll(List<Baggage> baggageList){
        baggageList.forEach(this::unregister);
    }
}
  1. Create the Main class:
package org.jnosql.demo.se;

import jakarta.enterprise.inject.se.SeContainer;
import jakarta.enterprise.inject.se.SeContainerInitializer;
import net.datafaker.Faker;
import org.eclipse.jnosql.mapping.DatabaseQualifier;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

public class App {

    public static void main(String[] args) {

        Faker faker = new Faker();
        try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {

            var baggageRepository = container.select(BaggageRepository.class, DatabaseQualifier.ofDocument()).get();

            System.out.println("=".repeat(50));
            System.out.println("baggageRepository.register()");
            var baggage01 = baggageRepository.register(Baggage.create(faker));
            System.out.println("Baggage " + baggage01.ticket());
            System.out.println("Baggage items: ");
            Arrays.stream(baggage01.items()).forEach(System.out::println);

            System.out.println("=".repeat(50));
            System.out.println("baggageRepository.findByTicket()");
            Baggage registeredBaggage = baggageRepository.findByTicket(baggage01.ticket());

            System.out.println("Baggage " + registeredBaggage.ticket());
            System.out.println("Baggage items: ");
            Arrays.stream(registeredBaggage.items()).forEach(System.out::println);
            System.out.println("-".repeat(50));

            baggageRepository.registerAll(Baggage.create(faker), Baggage.create(faker));

            System.out.println("=".repeat(50));
            System.out.println("baggageRepository.findAll()");
            List<Baggage> baggageList = baggageRepository.findAll();
            System.out.println("Baggage List: " + baggageList.size());
            System.out.println("-".repeat(50));
            for (var baggage : baggageList) {
                System.out.println("Baggage " + baggage.ticket());
                System.out.println("Baggage items: ");
                Arrays.stream(registeredBaggage.items()).forEach(System.out::println);
                System.out.println("-".repeat(50));
            }
            System.out.println("=".repeat(50));
            System.out.println("baggageRepository.unregisterAll()");
            baggageRepository.unregisterAll(baggageList);

            System.out.println("=".repeat(50));
            System.out.println("baggageRepository.findAll()");
            List<Baggage> actualBaggageList = baggageRepository.findAll();
            System.out.println("Baggage List: " + actualBaggageList.size());

        }
    }
}
  1. Execute the Main class.

Expected Results

It's expected to store and retrieve data from the database loading them into the record classes with array columns embedded.

Code example, screenshot, or link to a repository

No response

dearrudam commented 2 months ago

@otaviojava , I'm working on this solution already.

otaviojava commented 2 months ago

Thanks @dearrudam for fix it. Please, let me know if we can close thie one.

dearrudam commented 2 months ago

@otaviojava I'm going to push a sample app into jnosql-se with this feature and then I'll close it!

dearrudam commented 2 months ago

@otaviojava , here's the sample project that uses this feature: https://github.com/JNOSQL/demos-se/tree/main/mongodb-record I'm closing this one.