private static <T> EntityFieldsInfo parseFields(Class<T> clazz, Constructor<T> constructor) {
        List<Class<?>> classes = new ArrayList<>(8);
        Class<?> currentClass = clazz;
        do {
            classes.add(currentClass);
            currentClass = currentClass.getSuperclass();
        } while (currentClass != null && currentClass != Object.class);
        // Reverse the classes so that the child can override
        // the parent's fields and methods when parsing.
        classes = classes.reversed();

        String idField = null;
        Map<String, EntityField<?>> nameToField = null;
        List<Index> entityIndexes = null;
        List<Field> fields = parseFields(classes);
        Map<String, MethodHandle> propertyNameToSetter = parseSetterMethods(classes);
        boolean hasParameters = constructor.getParameterCount() > 0;
        Parameter[] parameters = hasParameters
                ? constructor.getParameters()
                : null;
        boolean isUserDefinedAttributesFieldRequired = Customizable.class.isAssignableFrom(clazz);
        boolean isUserDefinedAttributesFieldFound = false;
        for (Field field : fields) {
            int modifiers = field.getModifiers();
            if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) {
                continue;
            }
            // Classes
            Class<?> fieldClass = field.getType();
            Class keyClass = null;
            Class elementClass = null;
            if (Iterable.class.isAssignableFrom(fieldClass)) {
                elementClass = ClassUtil.getIterableElementClass(field);
            } else if (Map.class.isAssignableFrom(fieldClass)) {
                Pair<Class<?>, Class<?>> keyAndElement =
                        ClassUtil.getMapKeyClassAndElementClass(field);
                keyClass = keyAndElement.first();
                elementClass = keyAndElement.second();
            }

            // ID, field name and type
            String fieldName = parseFieldName(field);
            EntityFieldType fieldType = EntityFieldType.NORMAL;
            boolean isIdField = field.isAnnotationPresent(Id.class);
            if (isIdField) {
                if (idField == null) {
                    idField = fieldName;
                    fieldType = EntityFieldType.ID;
                } else {
                    throw new IllegalArgumentException(
                            "The entity class ("
                                    + clazz.getName()
                                    + ") must not have multiple fields marked with @"
                                    + Id.class.getSimpleName());
                }
            }
            if (isUserDefinedAttributesFieldRequired
                    && Customizable.USER_DEFINED_ATTRIBUTES_FIELD_NAME.equals(field.getName())) {
                if (isIdField) {
                    throw new IllegalArgumentException(
                            "The entity class ("
                                    + clazz.getName()
                                    + ") must not have a field named \""
                                    + Customizable.USER_DEFINED_ATTRIBUTES_FIELD_NAME
                                    + "\" that is also marked with @"
                                    + Id.class.getSimpleName());
                }
                if (keyClass == null) {
                    throw new IllegalArgumentException(
                            "The entity class ("
                                    + clazz.getName()
                                    + ") must have a field named \""
                                    + Customizable.USER_DEFINED_ATTRIBUTES_FIELD_NAME
                                    + "\" that is a Map");
                }
                fieldType = EntityFieldType.USER_DEFINED_ATTRIBUTES;
                isUserDefinedAttributesFieldFound = true;
            }
            // Indexes
            List<Index> indexes = parseIndexes(field);
            if (!indexes.isEmpty()) {
                if (entityIndexes == null) {
                    entityIndexes = new ArrayList<>(8);
                }
                entityIndexes.addAll(indexes);
            }
            // Getter and Setter
            MethodHandle setter = propertyNameToSetter.get(fieldName);
            VarAccessor<T, ?> varAccessor = setter == null
                    ? VarAccessorFactory.get(field)
                    : VarAccessorFactory.get(field, setter);
            // Constructor
            int constructorParamIndex = hasParameters
                    ? parseConstructorParamIndex(constructor, parameters, field)
                    : EntityField.UNSET_CTOR_PARAM_INDEX;
            if (nameToField == null) {
                nameToField = CollectionUtil.newMapWithExpectedSize(fields.size());
            }
            nameToField.put(fieldName,
                    new EntityField<>(
                            fieldClass,
                            keyClass,
                            elementClass,
                            fieldName,
                            fieldType,
                            constructorParamIndex,
                            varAccessor,
                            field.isAnnotationPresent(EnumNumber.class)));
        }
        if (isUserDefinedAttributesFieldRequired && !isUserDefinedAttributesFieldFound) {
            throw new IllegalArgumentException(
                    "The entity class ("
                            + clazz.getName()
                            + ") must have a field named \""
                            + Customizable.USER_DEFINED_ATTRIBUTES_FIELD_NAME
                            + "\" that is a Map");
        }
        return new EntityFieldsInfo(
                idField,
                nameToField == null
                        ? Collections.emptyMap()
                        : Map.copyOf(nameToField),
                entityIndexes == null
                        ? Collections.emptyList()
                        : entityIndexes);
    }
@implNote The method doesn't the support shard keys that contains a hashed key supported in
          4.4
turms-server-common/src/main/java/im/turms/server/common/storage/mongo/entity/MongoEntityFactory.java