Apache Geode crud репозиторий findById() возвращает неправильный массив

#spring-data-gemfire

#spring-data-gemfire

Вопрос:

Я использую apache geode 1.13.0

Класс данных прост, он содержит 1 ArrayList и 1 HashMap

Данные правильно хранятся в geode, но затем repository.findById() возвращает неправильные массив и карту

repository.findAll() работает хорошо, хотя

данные

 id | someList  | someMap
-- | --------- | ---------------------------------
1  | [1,2,3,4] | {"1":"a","2":"b","3":"c","4":"d"}
2  | [2,3,4]   | {"4":"d","2":"b","3":"c"}
3  | [3,4]     | {"4":"d","3":"c"}
4  | null      | {"4":"d"}
5  | null      | null
  

в то время как findById извлекает

 Class1{id=1, someList=[3, 4], someMap={4=d}}
Class1{id=2, someList=[3, 4], someMap={4=d}}
Class1{id=3, someList=[3, 4], someMap={4=d}}
Class1{id=4, someList=null, someMap={4=d}}
Class1{id=5, someList=null, someMap=null}
  

логика в MyRunner.java

В чем может быть проблема?

вот класс

 ....
@Region("TestRegion")
public class Class1 implements PdxSerializable {
  @Id
  Integer id;
  ArrayList<Integer> someList;
  HashMap<Integer, String> someMap;

  public Class1() {}

  public Class1(Integer id, ArrayList<Integer> someList, HashMap<Integer, String> someMap) {
    this.id = id;
    this.someList = someList;
    this.someMap = someMap;
  }

  @Override
  public String toString() {
    String ret;

    ret = "Class1";
    ret  = "{id=";
    if (id == null) { ret  = "null"; } else { ret  = id.toString(); }
    ret  = ", someList=";
    if (someList == null) { ret  = "null"; } else { ret  = someList.toString(); }
    ret  = ", someMap=";
    if (someMap == null) { ret  = "null"; } else { ret  = someMap.toString(); }
    ret  = "}";

    return ret;
  }

  @Override
  public void toData(PdxWriter out) throws PdxFieldAlreadyExistsException, PdxSerializationException {
    out.writeInt("id", id);
    out.writeObject("someList", someList);
    out.writeObject("someMap", someMap);
  }

  @Override
  public void fromData(PdxReader in) throws PdxFieldTypeMismatchException, PdxSerializationException {
    id = in.readInt("id");
    someList = (ArrayList<Integer>)in.readObject("someList");
    someMap = (HashMap<Integer, String>)in.readObject("someMap");
  }
}
  

репозиторий

 @Repository
public interface GeodeRepository extends CrudRepository<Class1, Integer> {
}
  

класс конфигурации geode

 .....
@EnableEntityDefinedRegions(basePackageClasses = Class1.class, clientRegionShortcut = ClientRegionShortcut.CACHING_PROXY)
@EnableGemfireRepositories(basePackageClasses = GeodeRepository.class)
@Configuration
class GeodeConfiguration {
}
  

Главная

 ....
@SpringBootApplication
class SpringbootCmdTest {
  public static void main(String[] args) {
    SpringApplication.run(SpringbootCmdTest.class, args);
  }
}
  

MyRunner class

 ....
@Component
public class MyRunner implements CommandLineRunner {
  @Autowired
  private GeodeRepository repository;

  @Override
  public void run(String... args) throws Exception {

    //repository.deleteAll(); // doesn't work for partitioned regions as of 2020-11-02 https://jira.spring.io/browse/DATAGEODE-265
    repository.deleteAll(repository.findAll());
    ArrayList<Integer> l = new ArrayList<>();
    HashMap<Integer, String> m = new HashMap<>();
    Class1 obj;
    Optional<Class1> o;

    l.clear(); l.add(1); l.add(2); l.add(3); l.add(4);
    m.clear(); m.put(1, "a"); m.put(2, "b"); m.put(3, "c"); m.put(4, "d");
    obj = new Class1(1, l, m);
    repository.save(obj);

    l.clear(); l.add(2); l.add(3); l.add(4);
    m.clear(); m.put(2, "b"); m.put(3, "c"); m.put(4, "d");
    obj = new Class1(2, l, m);
    repository.save(obj);

    l.clear(); l.add(3); l.add(4);
    m.clear(); m.put(3, "c"); m.put(4, "d");
    obj = new Class1(3, l, m);
    repository.save(obj);

    m.clear(); m.put(4, "d");
    obj = new Class1(4, null, m);
    repository.save(obj);

    obj = new Class1(5, null, null);
    repository.save(obj);

    System.out.println("n-- findAll().foreach works -------------------------------");

    repository.findAll().forEach((item) ->System.out.println(item.toString()));

    System.out.println("n-- optional directly to string. issue with the array and map. displays the last entry --");

    System.out.println(repository.findById(1).toString());
    System.out.println(repository.findById(2).toString());
    System.out.println(repository.findById(3).toString());
    System.out.println(repository.findById(4).toString());
    System.out.println(repository.findById(5).toString());

    System.out.println("n-- first convert the optional to object. issue with the array and map. displays the last entry --");

    o = repository.findById(1);
    o.ifPresent(ob -> System.out.println(ob.toString()));
    o = repository.findById(2);
    o.ifPresent(ob -> System.out.println(ob.toString()));
    o = repository.findById(3);
    o.ifPresent(ob -> System.out.println(ob.toString()));
    o = repository.findById(4);
    o.ifPresent(ob -> System.out.println(ob.toString()));
    o = repository.findById(5);
    o.ifPresent(ob -> System.out.println(ob.toString()));

    System.out.println("n-- findAllById().foreach does not work either -------------------------------");

    ArrayList<Integer> il = new ArrayList<>();

    il.add(1); il.add(2); il.add(3); il.add(4); il.add(5);
    repository.findAllById(il).forEach((item) ->System.out.println(item.toString()));

    System.out.println("n---------------------------------");
  }
}
  

application.properties

 spring.profiles.active = dev

logging.level.org.springframework.boot = INFO
logging.level.org.springframework.transaction = TRACE
logging.level.owa = DEBUG
logging.file.name = Test.log

spring.main.banner-mode=off
spring.profiles.include=common

spring.data.gemfire.pool.locators = localhost[2001]
  

pom.xml

 <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.4.RELEASE</version>
    <relativePath/><!-- lookup parent from repository -->
  </parent>
  <groupId>org.Test</groupId>
  <artifactId>SpringbootCmdTest</artifactId>
  <version>20.10.0</version>
  <name>SpringbootCmdTest</name>
  <description>Springboot Cmd Line For Testing</description>

  <properties>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.geode</groupId>
      <artifactId>spring-geode-starter</artifactId>
      <version>1.3.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.apache.geode</groupId>
      <artifactId>geode-core</artifactId>
      <version>1.13.0</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <exclusions>
        <exclusion>
          <groupId>org.junit.vintage</groupId>
          <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
      <plugin>
        <groupId>org.codehaus.gmavenplus</groupId>
        <artifactId>gmavenplus-plugin</artifactId>
        <version>1.8.1</version>
        <executions>
          <execution>
            <goals>
              <goal>addSources</goal>
              <goal>addTestSources</goal>
              <goal>generateStubs</goal>
              <goal>compile</goal>
              <goal>generateTestStubs</goal>
              <goal>compileTests</goal>
              <goal>removeStubs</goal>
              <goal>removeTestStubs</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

</project>
  

запрос gfsh

 gfsh>query --query="select * from /TestRegion"
Result : true
Limit  : 100
Rows   : 5

id | someList  | someMap
-- | --------- | ---------------------------------
1  | [1,2,3,4] | {"1":"a","2":"b","3":"c","4":"d"}
2  | [2,3,4]   | {"4":"d","2":"b","3":"c"}
3  | [3,4]     | {"4":"d","3":"c"}
4  | null      | {"4":"d"}
5  | null      | null
  

вывод

 -- findAll().foreach works -------------------------------
Class1{id=1, someList=[1, 2, 3, 4], someMap={1=a, 2=b, 3=c, 4=d}}
Class1{id=2, someList=[2, 3, 4], someMap={4=d, 2=b, 3=c}}
Class1{id=3, someList=[3, 4], someMap={4=d, 3=c}}
Class1{id=4, someList=null, someMap={4=d}}
Class1{id=5, someList=null, someMap=null}

-- optional directly to string. issue with the array and map. displays the last entry --
Optional[Class1{id=1, someList=[3, 4], someMap={4=d}}]
Optional[Class1{id=2, someList=[3, 4], someMap={4=d}}]
Optional[Class1{id=3, someList=[3, 4], someMap={4=d}}]
Optional[Class1{id=4, someList=null, someMap={4=d}}]
Optional[Class1{id=5, someList=null, someMap=null}]

-- first convert the optional to object. issue with the array and map. displays the last entry --
Class1{id=1, someList=[3, 4], someMap={4=d}}
Class1{id=2, someList=[3, 4], someMap={4=d}}
Class1{id=3, someList=[3, 4], someMap={4=d}}
Class1{id=4, someList=null, someMap={4=d}}
Class1{id=5, someList=null, someMap=null}

-- findAllById().foreach does not work either -------------------------------
Class1{id=1, someList=[3, 4], someMap={4=d}}
Class1{id=2, someList=[3, 4], someMap={4=d}}
Class1{id=3, someList=[3, 4], someMap={4=d}}
Class1{id=4, someList=null, someMap={4=d}}
Class1{id=5, someList=null, someMap=null}
  

Комментарии:

1. В дополнение к комментарию Патрика ниже и его ответу на DATAGEODE-388, я также хотел поделиться дополнительной информацией, которую я предоставил в комментарии к DATAGEODE-388. Смотрите здесь: jira.spring.io/browse /…

2. Также обратите внимание: issues.apache.org/jira/browse/GEODE-8733 .

Ответ №1:

Примечание: Это поведение не является эксклюзивным для Spring и может быть воспроизведено только с помощью Apache Geode .

Поскольку вы clientRegionShortcut установили значение ClientRegionShortcut.CACHING_PROXY , ваши данные хранятся как на сервере, так и на клиенте в локальном регионе. При доступе к данным по идентификатору сначала проверяется локальный регион, и если запись найдена, она возвращается, если она не найдена, она будет запрашивать ее на сервере.

Причина, по которой вы видите только самые последние значения вашего списка и карты, заключается в том, что локальный регион поддерживается m и l по ссылке, поэтому, когда вы повторно используете их и обновляете их содержимое, значения, сохраненные в локальном регионе, также отражают изменения, даже без их сохранения. Вы видите правильные значения для findAll() и вашего запроса, потому что они делегируются непосредственно серверу (который не содержит значения по ссылке, потому что это отдельная машина / процесс), а не локальному региону.

Есть несколько способов получить ожидаемое поведение:

Вариант 1. Измените ClientRegionShortcut.CACHING_PROXY на ClientRegionShortcut.PROXY , чтобы значения не сохранялись локально, а вместо этого каждый раз извлекались с сервера.

Вариант 2. Вы можете создавать новый ArrayList и HashMap каждый раз, когда захотите добавить запись, вместо повторного использования одних и тех же объектов. Например, замена l.clear() и m.clear() на l = new ArrayList<>() и m = new HashMap<>() .