Search code examples
nosqlspring-dataoracle-nosql

Spring Data : @NosqlTable add Map variable


I am trying to add a Map field in my oracle nosqltable (in the example given here https://docs.oracle.com/en/database/other-databases/nosql-database/21.1/java-driver-table/accessing-nosql-using-sdf.html) but when saving its not getting saved properly by Spring data.

Customer.java

@NosqlTable(storageGB = 1, writeUnits = 10, readUnits = 10)
 public class Customer {
        @NosqlId(generated = true)
        long customerId;
        String firstName;
        String lastName;
        Map hashMap;
        Date createdAt;

        @Override
        public String toString() {
            return "Customer{" +
               "customerId=" + customerId +
               ", firstName='" + firstName + '\'' +
               ", lastName='" + lastName + '\'' +
               ", createdAt='" + createdAt + '\'' +
               ", hashMap='" + hashMap + '\'' +
               '}';
        }
}

CustomerRepostory.java

import com.oracle.nosql.spring.data.repository.NosqlRepository;

public interface CustomerRepository
    extends NosqlRepository<Customer, Long>
{
    Iterable<Customer> findByLastName(String lastname);
}

When I call the following code to create a customer row :

Customer s1 = new Customer();
        s1.firstName = "John";
        s1.lastName = "Doe";
        HashMap s1Map = new HashMap() ;
        s1Map.put("name", "myMap") ;
        s1Map.put("use", true);
        s1.hashMap = s1Map;

        repo.save(s1);

It gets saved as

{
          "createdAt": null,
          "firstName": "John",
          "hashMap": {
                    "entrySet": null,
                    "keySet": null,
                    "loadFactor": 0.75,
                    "modCount": 2,
                    "size": 2,
                    "table": [
                              null,
                              null,
                              null,
                              null,
                              null,
                              null,
                              {
                                        "hash": 116102,
                                        "key": "use",
                                        "next": null,
                                        "value": true
                              },
                              null,
                              {
                                        "hash": 3373752,
                                        "key": "name",
                                        "next": null,
                                        "value": "myMap"
                              },
                              null,
                              null,
                              null,
                              null,
                              null,
                              null,
                              null
                    ],
                    "threshold": 12,
                    "values": null
          },
          "lastName": "Doe"
}

Can someone please help me with correct data type to use for nosql map ?


Solution

  • Map (java.util.Map) is not currently supported, but it is a key roadmap item.

    see here https://github.com/oracle/nosql-spring-sdk/issues/18

    You can find the current Mapping Between Java and NoSQL JSON Types in the documentation - https://docs.oracle.com/en/database/other-databases/nosql-database/22.2/springsdk/persistence-model.html

    In the meantime, it is possible to use the class oracle.nosql.driver.values.MapValue. Here is an example

    import com.oracle.nosql.spring.data.core.mapping.NosqlId;
    import oracle.nosql.driver.values.MapValue ;
    
    @NosqlTable(storageGB = 1, writeUnits = 10, readUnits = 10)
    public class Customer {
        @NosqlId(generated = true)
        long customerId;
        String firstName;
        String lastName;
        MapValue map;
    
        @Override
        public String toString() {
            return "Customer{" +
                "customerId=" + customerId +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", map='" + map + '\'' +
                '}';
        }
    }
    

    Here is an example of a call to create a customer row - using multiple types, including other nosql Classes oracle.nosql.driver.values.ArrayValue . As you can see the syntax is very similar to java.util.Map. When java.util.Map will be supported you can migrate easily

    Customer c2 = new Customer();
    c2.firstName = "John";
    c2.lastName = "Josh";
    c2.map = new MapValue();
    c2.map.put("number field", 1);
    c2.map.put("string_field", "string value");
    c2.map.put("boolean_field", true);
    ArrayValue arrayValue = new ArrayValue();
    arrayValue.add(100);
    arrayValue.add("102");
    arrayValue.add(true);
    c2.map.put("json-field", arrayValue);
    repo.save(c2);
    System.out.println("\nsaved: " + c2); 
    
    System.out.println("\nfindAll:");
    Iterable<Customer> customers = repo.findAll();
    
    for (Customer s : customers) {
        System.out.println("  Customer: " + s);
    }
    
    System.out.println("\nfindByLastName: Josh");
    customers = repo.findByLastName("Josh");
    
    for (Customer s : customers) {
        System.out.println("  Customer: " + s);
    }
    

    Here are the outputs - application

    saved: Customer{customerId=10, firstName='John', lastName='Doe', map='null'}
    
    saved: Customer{customerId=11, firstName='John', lastName='Smith', map='null'}
    
    saved: Customer{customerId=12, firstName='John', lastName='Josh', map='{"number field":1,"json-field":[100,"102",true],"string_field":"string value"}'}
    
    findAll:
      Customer: Customer{customerId=12, firstName='John', lastName='Josh', map='{"json-field":[100,"102",true],"number field":1,"string_field":"string value"}'}
      Customer: Customer{customerId=10, firstName='John', lastName='Doe', map='null'}
      Customer: Customer{customerId=11, firstName='John', lastName='Smith', map='null'}
    
    findByLastName: Josh
      Customer: Customer{customerId=12, firstName='John', lastName='Josh', map='{"json-field":[100,"102",true],"number field":1,"string_field":"string value"}'}
    

    reading using the SQL for Oracle NoSQL Database Shell - if Cloud, you can use the OCI Console

    sql-> mode json -pretty
    Query output mode is pretty JSON
    sql-> select * from customer;
    {
      "customerId" : 13,
      "kv_json_" : {
        "firstName" : "John",
        "lastName" : "Doe",
        "map" : null
      }
    }
    
    {
      "customerId" : 14,
      "kv_json_" : {
        "firstName" : "John",
        "lastName" : "Smith",
        "map" : null
      }
    }
    
    {
      "customerId" : 15,
      "kv_json_" : {
        "firstName" : "John",
        "lastName" : "Josh",
        "map" : {
          "boolean_field" : true,
          "json-field" : [100, "102", true],
          "number field" : 1,
          "string_field" : "string value"
        }
      }
    }
    
    

    In order to run this example, you need to apply a fix, Otherwise, you will have the following error

    Caused by: java.lang.IllegalArgumentException: Entity must not be null!
    

    A fix will be published soon on this GitHub repository https://github.com/oracle/nosql-spring-sdk. Again more information here https://github.com/oracle/nosql-spring-sdk/issues/18

    to learn more on MapValue