jnosql icon indicating copy to clipboard operation
jnosql copied to clipboard

[BUG] Update and delete statements should return the number of affected rows

Open lrasku opened this issue 10 months ago • 1 comments

Which JNoSQL project the issue refers to?

JNoSQL (Core)

Bug description

The Jakarta Data spec says that update and delete statements should return the number of changed records to the client:

5.6.2. Update statements An update statement, with syntax given by update_statement, updates each record which satisfies the restriction imposed by the where clause, and returns the number of updated records to the client.

5.6.3. Delete statements A delete statement, with syntax given by delete_statement, deletes each record which satisfies the restriction imposed by the where clause, and returns the number of deleted records to the client.

(Emphasis mine.) However, trying this in current JNoSQL implementations gets me an UnsupportedOperationException.

JNoSQL Version

JNoSQL version 1.1.6

Steps To Reproduce

Running this program will exit with an exception:

package org.example;

import jakarta.data.repository.Insert;
import jakarta.data.repository.Param;
import jakarta.data.repository.Query;
import jakarta.data.repository.Repository;
import jakarta.enterprise.inject.se.SeContainer;
import jakarta.enterprise.inject.se.SeContainerInitializer;
import jakarta.nosql.Column;
import jakarta.nosql.Entity;
import jakarta.nosql.Id;

@Entity
public record Failure(
    @Id String name,
    @Column String value
) {
    @Repository
    public static interface Repo {
        @Insert
        Failure addRef(Failure f);

        @Query("update Failure set value = :newValue where name = :name and value = :oldValue")
        int updateRef(@Param("name") String name, @Param("oldValue") String oldValue, @Param("newValue") String newValue);

        @Query("delete from Failure where name = :name and value = :value")
        int deleteRef(@Param("name") String name, @Param("value") String value);
    }

    public static void main(String[] args) {
        try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
            var repo = container.select(Repo.class).get();

            var f = new Failure("test", "a");
            System.out.println("inserted: "+(f = repo.addRef(f)));
            try { System.out.println("inserted again: "+repo.addRef(f)); }
            catch (Exception e) { System.out.println("duplicate insert fails: "+e); }
            // The below fail with:
            // Exception in thread "main" java.lang.UnsupportedOperationException: The return type must be Void when the query is not a SELECT operation, due to the nature of DELETE and UPDATE operations. The query: update Failure set value = :newValue where name = :name and value = :oldValu
            System.out.println("refs updated: "+repo.updateRef(f.name(), "a", "b"));
            System.out.println("refs updated: "+repo.updateRef(f.name(), "a", "c"));
            System.out.println("refs deleted: "+repo.deleteRef(f.name(), "a"));
            System.out.println("refs deleted: "+repo.deleteRef(f.name(), "b"));
        }
    }
}

Expected Results

The program should complete silently & successfully.

Code example, screenshot, or link to a repository

Attached is a minimal Docker Compose project that demonstrates the issue.

  1. Run docker compose up --build -V in the (unzipped) example directory.
  2. The app-1 container will exit with an exception shortly after the database initialization. In the logs, this will look like:

app-1 | Mar 28, 2025 10:27:19 PM org.jboss.weld.environment.se.WeldContainer shutdown app-1 | INFO: WELD-ENV-002001: Weld SE container 75c5b4af-4099-400f-82fe-7e9db64e2122 shut down
app-1 | Exception in thread "main" java.lang.UnsupportedOperationException: The return type must be Void when the query is not a SELECT operation, due to the nature of DELETE and UPDATE operations. The query: delete from Failure where name = :name and value = :value
app-1 | at org.eclipse.jnosql.communication.semistructured.QueryType.checkValidReturn(QueryType.java:109)
app-1 | at org.eclipse.jnosql.mapping.semistructured.query.CustomRepositoryHandler.invoke(CustomRepositoryHandler.java:134)
app-1 | at jdk.proxy2/jdk.proxy2.$Proxy56.deleteRef(Unknown Source) app-1 | at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) app-1 | at java.base/java.lang.reflect.Method.invoke(Method.java:580) app-1 | at org.jboss.weld.bean.proxy.AbstractBeanInstance.invoke(AbstractBeanInstance.java:38)
app-1 | at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:109)
app-1 | at org.example.Failure$Repo$1121156340$Proxy$_$$_WeldClientProxy.deleteRef(Unknown Source)
app-1 | at org.example.Failure.main(Failure.java:42)

example.zip

lrasku avatar Mar 28 '25 22:03 lrasku

I will update the Jakarta NoSQL spec about this behavior and I will create a specialization for MongoDB once it does support this kind of capability

otaviojava avatar Mar 29 '25 08:03 otaviojava

After a couple of discussions on the Jakarta NoSQL spec, due to the eventual persistence capability of NoSQL databases, we will not, by default, support returning numbers on delete.

The discussion is still open for more, however, for now I will close this one.

otaviojava avatar Sep 14 '25 07:09 otaviojava