This is an automated email from the ASF dual-hosted git repository.
mpochatkin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new b94a39d6365 IGNITE-27753 IgniteCatalog DSL class inheritance mapping
(#7536)
b94a39d6365 is described below
commit b94a39d63657e585b1083874d4670a789b40ec73
Author: Vadim Kolodin <[email protected]>
AuthorDate: Thu Feb 19 15:58:01 2026 +0400
IGNITE-27753 IgniteCatalog DSL class inheritance mapping (#7536)
---
.../apache/ignite/table/mapper/MapperBuilder.java | 17 +-
.../ignite/internal/catalog/ItCatalogDslTest.java | 19 ++
.../ignite/internal/catalog/PojoExtended.java | 77 +++++
.../catalog/sql/CreateFromAnnotationsImpl.java | 14 +-
.../catalog/sql/CreateFromAnnotationsTest.java | 74 +++++
.../internal/schema/marshaller/MarshallerTest.java | 18 +
.../internal/schema/marshaller/Inheritance.java | 368 +++++++++++++++++++++
.../ignite/internal/marshaller/FieldAccessor.java | 19 +-
8 files changed, 602 insertions(+), 4 deletions(-)
diff --git
a/modules/api/src/main/java/org/apache/ignite/table/mapper/MapperBuilder.java
b/modules/api/src/main/java/org/apache/ignite/table/mapper/MapperBuilder.java
index b8aa1028a82..21038eb9000 100644
---
a/modules/api/src/main/java/org/apache/ignite/table/mapper/MapperBuilder.java
+++
b/modules/api/src/main/java/org/apache/ignite/table/mapper/MapperBuilder.java
@@ -22,9 +22,11 @@ import static
org.apache.ignite.lang.util.IgniteNameUtils.parseIdentifier;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.AbstractMap.SimpleEntry;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.apache.ignite.catalog.annotations.Column;
@@ -283,7 +285,7 @@ public final class MapperBuilder<T> {
}
if (automapFlag) {
- Arrays.stream(targetType.getDeclaredFields())
+ getAllFields(targetType).stream()
.filter(fld -> !Modifier.isStatic(fld.getModifiers()) &&
!Modifier.isTransient(fld.getModifiers()))
.map(MapperBuilder::getColumnToFieldMapping)
.filter(entry -> !fields.contains(entry.getValue()))
@@ -300,4 +302,15 @@ public final class MapperBuilder<T> {
var columnName = column != null && !column.value().isEmpty() ?
column.value() : fldName;
return new SimpleEntry<>(parseIdentifier(columnName), fldName);
}
+
+ /**
+ * Gets all fields of the given class and its parents (if any).
+ */
+ private static List<Field> getAllFields(Class<?> clazz) {
+ var result = new ArrayList<Field>();
+ for (Class<?> current = clazz; current != Object.class; current =
current.getSuperclass()) {
+ Collections.addAll(result, current.getDeclaredFields());
+ }
+ return result;
+ }
}
diff --git
a/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/ItCatalogDslTest.java
b/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/ItCatalogDslTest.java
index 2aae592eaf9..c05288112e0 100644
---
a/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/ItCatalogDslTest.java
+++
b/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/ItCatalogDslTest.java
@@ -71,6 +71,8 @@ class ItCatalogDslTest extends ClusterPerClassIntegrationTest
{
static final String POJO_RECORD_TABLE_NAME = "pojo_record_test";
+ static final String POJO_RECORD_EXTENDED_TABLE_NAME =
"pojo_record_extended_test";
+
static final String EXPLICIT_QUOTES_TABLE_NAME =
"explicit_quotes_test_table";
static final String ZONE_NAME = "ZONE_TEST";
@@ -790,6 +792,23 @@ class ItCatalogDslTest extends
ClusterPerClassIntegrationTest {
}
}
+ @Test
+ void inheritance() throws Exception {
+ CompletableFuture<Table> tableFuture =
catalog().createTableAsync(PojoExtended.class);
+ assertThat(tableFuture, will(not(nullValue())));
+
+ sql("insert into "
+ + POJO_RECORD_EXTENDED_TABLE_NAME
+ + " (id, id_str, f_name, l_name, str, f_name_extended) values
(1, '1', 'f', 'l', 's', 'e')");
+ List<List<Object>> rows = sql("select id, id_str, f_name, l_name, str,
f_name_extended from "
+ + POJO_RECORD_EXTENDED_TABLE_NAME);
+
+ assertThat(rows, contains(List.of(1, "1", "f", "l", "s", "e")));
+
+ PojoExtended pojo = new PojoExtended(1, "1", "f", "l", "s", "e");
+ assertThat(tableFuture.get().recordView(PojoExtended.class).get(null,
pojo), is(pojo));
+ }
+
private static IgniteCatalog catalog() {
return CLUSTER.node(0).catalog();
}
diff --git
a/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/PojoExtended.java
b/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/PojoExtended.java
new file mode 100644
index 00000000000..18a1c000137
--- /dev/null
+++
b/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/PojoExtended.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.catalog;
+
+import static
org.apache.ignite.internal.TestDefaultProfilesNames.DEFAULT_AIPERSIST_PROFILE_NAME;
+
+import java.util.Objects;
+import org.apache.ignite.catalog.SortOrder;
+import org.apache.ignite.catalog.annotations.Column;
+import org.apache.ignite.catalog.annotations.ColumnRef;
+import org.apache.ignite.catalog.annotations.Index;
+import org.apache.ignite.catalog.annotations.Table;
+import org.apache.ignite.catalog.annotations.Zone;
+
+/**
+ * A POJO class representing the whole record with inheritance.
+ */
+@Table(
+ value = ItCatalogDslTest.POJO_RECORD_EXTENDED_TABLE_NAME,
+ zone = @Zone(value = ItCatalogDslTest.ZONE_NAME, storageProfiles =
DEFAULT_AIPERSIST_PROFILE_NAME),
+ colocateBy = @ColumnRef("id"),
+ indexes = @Index(value = "ix_pojo", columns = {
+ @ColumnRef("f_name"),
+ @ColumnRef(value = "l_name", sort = SortOrder.DESC),
+ @ColumnRef(value = "f_name_extended")
+ })
+)
+class PojoExtended extends Pojo {
+ @Column("f_name_extended")
+ String firstNameExtended;
+
+ PojoExtended() {}
+
+ PojoExtended(Integer id, String idStr, String firstName, String lastName,
String str, String firstNameExtended) {
+ super(id, idStr, firstName, lastName, str);
+ this.firstNameExtended = firstNameExtended;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ PojoExtended pojo = (PojoExtended) o;
+ return Objects.equals(id, pojo.id)
+ && Objects.equals(idStr, pojo.idStr)
+ && Objects.equals(firstName, pojo.firstName)
+ && Objects.equals(lastName, pojo.lastName)
+ && Objects.equals(str, pojo.str)
+ && Objects.equals(firstNameExtended, pojo.firstNameExtended);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, idStr, firstName, lastName, str,
firstNameExtended);
+ }
+}
diff --git
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsImpl.java
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsImpl.java
index df167cc7b38..524d4f17f52 100644
---
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsImpl.java
+++
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsImpl.java
@@ -25,6 +25,7 @@ import static
org.apache.ignite.table.mapper.Mapper.nativelySupported;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import org.apache.ignite.catalog.ColumnSorted;
import org.apache.ignite.catalog.ColumnType;
@@ -201,7 +202,7 @@ class CreateFromAnnotationsImpl extends
AbstractCatalogQuery<TableZoneId> {
}
private static void processColumnsInPojo(CreateTableImpl createTable,
Class<?> clazz, List<ColumnSorted> idColumns) {
- for (Field f : clazz.getDeclaredFields()) {
+ for (Field f : getAllFields(clazz)) {
if (Modifier.isStatic(f.getModifiers()) ||
Modifier.isTransient(f.getModifiers())) {
continue;
}
@@ -230,4 +231,15 @@ class CreateFromAnnotationsImpl extends
AbstractCatalogQuery<TableZoneId> {
}
}
+ /**
+ * Gets all fields of the given class and its parents (if any).
+ */
+ private static List<Field> getAllFields(Class<?> clazz) {
+ var result = new ArrayList<Field>();
+ for (Class<?> current = clazz; current != Object.class; current =
current.getSuperclass()) {
+ Collections.addAll(result, current.getDeclaredFields());
+ }
+ return result;
+ }
+
}
diff --git
a/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsTest.java
b/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsTest.java
index 5e6a577d9de..c9e93a9790e 100644
---
a/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsTest.java
+++
b/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsTest.java
@@ -219,6 +219,61 @@ class CreateFromAnnotationsTest {
);
}
+ @Test
+ void inheritance() {
+ // Record class
+ CreateFromAnnotationsImpl fromAnnotations =
createTable().processRecordClass(PojoValueExtended.class);
+ String sqlFromAnnotations = fromAnnotations.toString();
+
+ assertThat(
+ sqlFromAnnotations,
+ is("CREATE TABLE IF NOT EXISTS PUBLIC.POJO_VALUE_EXTENDED_TEST
("
+ + "F_NAME_EXTENDED VARCHAR, F_NAME VARCHAR, L_NAME
VARCHAR, STR VARCHAR);"
+ + System.lineSeparator()
+ + "CREATE INDEX IF NOT EXISTS IX_POJO ON
PUBLIC.POJO_VALUE_EXTENDED_TEST (F_NAME, L_NAME DESC, F_NAME_EXTENDED);")
+ );
+
+ TableDefinition definition =
TableDefinition.builder("pojo_value_extended_test")
+ .ifNotExists()
+ .record(PojoValueExtended.class)
+ .index("ix_pojo", IndexType.DEFAULT, column("f_name"),
column("l_name").desc(), column("f_name_extended"))
+ .build();
+ CreateFromDefinitionImpl fromDefinition = new
CreateFromDefinitionImpl(null).from(definition);
+ String sqlFromDefinition = fromDefinition.toString();
+
+ assertThat(
+ sqlFromAnnotations,
+ is(sqlFromDefinition)
+ );
+
+ // Key Value class
+ fromAnnotations =
createTable().processKeyValueClasses(PojoKeyExtended.class,
PojoValueExtended.class);
+ sqlFromAnnotations = fromAnnotations.toString();
+
+ assertThat(
+ sqlFromAnnotations,
+ is("CREATE TABLE IF NOT EXISTS PUBLIC.POJO_VALUE_EXTENDED_TEST
("
+ + "ID_STR_EXTENDED VARCHAR(20), ID INT, ID_STR
VARCHAR(20), F_NAME_EXTENDED VARCHAR, F_NAME VARCHAR, "
+ + "L_NAME VARCHAR, STR VARCHAR, PRIMARY KEY
(ID_STR_EXTENDED, ID, ID_STR));"
+ + System.lineSeparator()
+ + "CREATE INDEX IF NOT EXISTS IX_POJO ON
PUBLIC.POJO_VALUE_EXTENDED_TEST (F_NAME, L_NAME DESC, F_NAME_EXTENDED);")
+ );
+
+ definition = TableDefinition.builder("pojo_value_extended_test")
+ .ifNotExists()
+ .key(PojoKeyExtended.class)
+ .value(PojoValueExtended.class)
+ .index("ix_pojo", IndexType.DEFAULT, column("f_name"),
column("l_name").desc(), column("f_name_extended"))
+ .build();
+ fromDefinition = new CreateFromDefinitionImpl(null).from(definition);
+ sqlFromDefinition = fromDefinition.toString();
+
+ assertThat(
+ sqlFromAnnotations,
+ is(sqlFromDefinition)
+ );
+ }
+
@SuppressWarnings("unused")
private static class PojoKey {
@Id
@@ -402,6 +457,25 @@ class CreateFromAnnotationsTest {
String str;
}
+ static class PojoKeyExtended extends PojoKey {
+ @Id
+ @Column(value = "id_str_extended", length = 20)
+ String idStrExtended;
+ }
+
+ @Table(
+ value = "pojo_value_extended_test",
+ indexes = @Index(value = "ix_pojo", columns = {
+ @ColumnRef("f_name"),
+ @ColumnRef(value = "l_name", sort = SortOrder.DESC),
+ @ColumnRef("f_name_extended"),
+ })
+ )
+ static class PojoValueExtended extends PojoValue {
+ @Column("f_name_extended")
+ String firstNameExtended;
+ }
+
private static CreateFromAnnotationsImpl createTable() {
return new CreateFromAnnotationsImpl(null);
}
diff --git
a/modules/java-records-tests/src/test/java/org/apache/ignite/internal/schema/marshaller/MarshallerTest.java
b/modules/java-records-tests/src/test/java/org/apache/ignite/internal/schema/marshaller/MarshallerTest.java
index 6fee1caa1d8..3402948d238 100644
---
a/modules/java-records-tests/src/test/java/org/apache/ignite/internal/schema/marshaller/MarshallerTest.java
+++
b/modules/java-records-tests/src/test/java/org/apache/ignite/internal/schema/marshaller/MarshallerTest.java
@@ -20,6 +20,8 @@ package org.apache.ignite.internal.schema.marshaller;
import static
org.apache.ignite.internal.schema.marshaller.AssertMarshaller.assertMarshaller;
import static
org.apache.ignite.internal.schema.marshaller.AssertMarshaller.assertMarshallerThrows;
+import org.apache.ignite.internal.schema.marshaller.Inheritance.Kv;
+import org.apache.ignite.internal.schema.marshaller.Inheritance.Kv.ChildV;
import org.apache.ignite.internal.schema.marshaller.Records.ComponentsEmpty;
import org.apache.ignite.internal.schema.marshaller.Records.ComponentsExact;
import org.apache.ignite.internal.schema.marshaller.Records.ComponentsNarrow;
@@ -171,4 +173,20 @@ class MarshallerTest extends BaseIgniteAbstractTest {
new C()
);
}
+
+ @Test
+ void inheritance() {
+ // recordView
+ assertMarshaller(new Inheritance.AbstractParent.Child(1, "a", "b"));
+ assertMarshaller(new Inheritance.RegularParent.Child(1, "a", "b"));
+ assertMarshaller(new Inheritance.MultipleParent.Child(1, "a", "b"));
+ assertMarshaller(new Inheritance.ParentWithPrivateField.Child(1, "a",
"b"));
+
+ // kvView
+ assertMarshaller(new Kv.Key(1), new ChildV("a", "b"));
+
+ String msgSubstring = "Unsupported class. Only top-level or nested
static classes are supported";
+ assertMarshallerThrows(IllegalArgumentException.class, msgSubstring,
new Inheritance.LocalClass().newChild(1, "a", "b"));
+ }
+
}
diff --git
a/modules/java-records-tests/src/testFixtures/java/org/apache/ignite/internal/schema/marshaller/Inheritance.java
b/modules/java-records-tests/src/testFixtures/java/org/apache/ignite/internal/schema/marshaller/Inheritance.java
new file mode 100644
index 00000000000..72369d3616e
--- /dev/null
+++
b/modules/java-records-tests/src/testFixtures/java/org/apache/ignite/internal/schema/marshaller/Inheritance.java
@@ -0,0 +1,368 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema.marshaller;
+
+import java.util.Objects;
+import org.apache.ignite.catalog.annotations.Column;
+
+/**
+ * Declaration variants fixture. Each nested class contains various cases.
+ */
+class Inheritance {
+
+ Inheritance() {}
+
+ static class AbstractParent {
+ abstract static class Parent {
+ @Column("key")
+ Integer key;
+
+ @Column("val")
+ String val;
+
+ Parent() {}
+
+ Parent(Integer key, String val) {
+ this.key = key;
+ this.val = val;
+ }
+ }
+
+ static class Child extends Parent {
+ @Column("val2")
+ String val2;
+
+ Child() {}
+
+ Child(Integer key, String val, String val2) {
+ super(key, val);
+ this.val2 = val2;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Child that = (Child) o;
+ return Objects.equals(key, that.key)
+ && Objects.equals(this.val, that.val)
+ && Objects.equals(this.val2, that.val2);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(key, val, val2);
+ }
+ }
+ }
+
+ static class RegularParent {
+ static class Parent {
+ @Column("key")
+ Integer key;
+
+ @Column("val")
+ String val;
+
+ Parent() {}
+
+ Parent(Integer key, String val) {
+ this.key = key;
+ this.val = val;
+ }
+ }
+
+ static class Child extends Parent {
+ @Column("val2")
+ String val2;
+
+ Child() {}
+
+ Child(Integer key, String val, String val2) {
+ super(key, val);
+ this.val2 = val2;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Child that = (Child) o;
+ return Objects.equals(key, that.key)
+ && Objects.equals(this.val, that.val)
+ && Objects.equals(this.val2, that.val2);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(key, val, val2);
+ }
+ }
+ }
+
+ static class MultipleParent {
+ static class Parent1 {
+ @Column("key")
+ Integer key;
+
+ Parent1() {}
+
+ Parent1(Integer key) {
+ this.key = key;
+ }
+ }
+
+ static class Parent2 extends Parent1 {
+ @Column("val")
+ String val;
+
+ Parent2() {}
+
+ Parent2(Integer key, String val) {
+ super(key);
+ this.val = val;
+ }
+ }
+
+ static class Child extends Parent2 {
+ @Column("val2")
+ String val2;
+
+ Child() {}
+
+ Child(Integer key, String val, String val2) {
+ super(key, val);
+ this.val2 = val2;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Child that = (Child) o;
+ return Objects.equals(key, that.key)
+ && Objects.equals(this.val, that.val)
+ && Objects.equals(this.val2, that.val2);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(key, val, val2);
+ }
+ }
+
+ }
+
+ static class ParentWithPrivateField {
+ static class Parent {
+ @Column("key")
+ private Integer key;
+
+ @Column("val")
+ private String val;
+
+ Parent() {
+ }
+
+ Parent(Integer key, String val) {
+ this.key = key;
+ this.val = val;
+ }
+
+ Integer getKey() {
+ return key;
+ }
+
+ String getVal() {
+ return val;
+ }
+ }
+
+ static class Child extends Parent {
+ @Column("val2")
+ String val2;
+
+ Child() {
+ }
+
+ Child(Integer key, String val, String val2) {
+ super(key, val);
+ this.val2 = val2;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Child that = (Child) o;
+ return Objects.equals(getKey(), that.getKey())
+ && Objects.equals(this.getVal(), that.getVal())
+ && Objects.equals(this.val2, that.val2);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getKey(), getVal(), val2);
+ }
+ }
+ }
+
+ static class LocalClass {
+ Child newChild(Integer key, String val, String val2) {
+ return new Child(key, val, val2);
+ }
+
+ class Parent {
+ @Column("key")
+ Integer key;
+
+ @Column("val")
+ String val;
+
+ Parent() {
+ }
+
+ Parent(Integer key, String val) {
+ this.key = key;
+ this.val = val;
+ }
+ }
+
+ class Child extends Parent {
+ @Column("val2")
+ String val2;
+
+ Child() {
+ }
+
+ Child(Integer key, String val, String val2) {
+ super(key, val);
+ this.val2 = val2;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Child that = (Child) o;
+ return Objects.equals(key, that.key)
+ && Objects.equals(this.val, that.val)
+ && Objects.equals(this.val2, that.val2);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(key, val, val2);
+ }
+ }
+ }
+
+ static class Kv {
+ static class Key {
+ @Column("key")
+ Integer key;
+
+ Key() {}
+
+ Key(Integer key) {
+ this.key = key;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Key that = (Key) o;
+ return Objects.equals(key, that.key);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(key);
+ }
+ }
+
+ static class ParentV {
+ @Column("val")
+ String val;
+
+ ParentV() {}
+
+ ParentV(String val) {
+ this.val = val;
+ }
+ }
+
+ static class ChildV extends ParentV {
+ @Column("val2")
+ String val2;
+
+ ChildV() {}
+
+ ChildV(String val, String val2) {
+ super(val);
+ this.val2 = val2;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ChildV that = (ChildV) o;
+ return Objects.equals(this.val, that.val)
+ && Objects.equals(this.val2, that.val2);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(val, val2);
+ }
+ }
+ }
+}
diff --git
a/modules/marshaller-common/src/main/java/org/apache/ignite/internal/marshaller/FieldAccessor.java
b/modules/marshaller-common/src/main/java/org/apache/ignite/internal/marshaller/FieldAccessor.java
index 8075ff8106e..f74b0360c29 100644
---
a/modules/marshaller-common/src/main/java/org/apache/ignite/internal/marshaller/FieldAccessor.java
+++
b/modules/marshaller-common/src/main/java/org/apache/ignite/internal/marshaller/FieldAccessor.java
@@ -86,7 +86,7 @@ abstract class FieldAccessor {
@Nullable TypeConverter<?, ?> typeConverter
) {
try {
- Field field = type.getDeclaredField(fldName);
+ Field field = getField(type, fldName);
if (typeConverter == null) {
validateColumnType(col, field.getType());
@@ -135,6 +135,23 @@ abstract class FieldAccessor {
}
}
+ /**
+ * Gets an accessible field by name of the given class and its parents (if
any).
+ */
+ private static Field getField(Class<?> clazz, String fieldName) throws
NoSuchFieldException {
+ var current = clazz;
+ while (current != Object.class) {
+ try {
+ return current.getDeclaredField(fieldName);
+ } catch (NoSuchFieldException ignored) {
+ // ignore
+ }
+
+ current = current.getSuperclass();
+ }
+ throw new NoSuchFieldException("Field '" + fieldName + "' not found in
class hierarchy " + clazz);
+ }
+
/**
* Create accessor for the field.
*