web-dev-qa-db-ja.com

AWS DynamoDB:属性エラーを変換解除できませんでした

私は問題を抱えていて、それを処理することができません。たとえばKitchenServiceでCRUDメソッドを作成しました。 addProductなどのメソッドがあり、これらは正常に機能します。しかし、Productクラスフィールドを使用しているRecipeクラスがあります。この場合、大きな問題が発生します。

私のaddRecipeメソッド:

public Recipe addRecipe (Recipe recipe){

    List<RecipeElement> recipeElements = recipe.getRecipeElements();
    for (RecipeElement recipeElement : recipeElements) {
        String id = recipeElement.getProduct().getId();
        Product product = databaseController.get(Product.class, id);
        recipeElement.setProduct(product);
    }

    databaseController.saveRecipe(recipe);
    logger.log("Recipe created");

    return recipe;

ビルドが成功したので、POSTMANでテストしたいと思います。これが、送信するJSONウィッチの外観です。

{"id":null,"name":"test3","labels":["GLUTEN_FREE"],"author":{"name":"Plejer Annołn","id":"testID2"},"media":{"name":"heheszki","url":"http://blabla.pl","mediaType":"IMAGE"},"recipeElements":[{"product":{"id":"ecacaf36-29a2-41c6-942e-be5a715ed094"},"weight":"100"}],"approved":false}

そして、「メッセージ」:「内部サーバーエラー」が表示されるので、ログをチェックしています。そこで見つけたのは次のとおりです。

Product[Media]; could not unconvert attribute: com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException

Caused by: com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException: could not invoke public void pl.javamill.model.kitchen.Product.setMedia(pl.javamill.model.common.Media) on class pl.kitchen.Product with value {name=heheszki, url=http://blabla.pl, mediaType=IMAGE} of type class Java.util.LinkedHashMap

レシピクラスは次のようになります。

@DynamoDBTable(tableName = "recipe")
public class Recipe extends Request {
/**
 * Id of kitchen content
 */
private String id;
/**
 * Name of recipe
 */
private String name;

/**
 * Labels of product for example gluten fee product
 */
private List<KitchenLabel> labels;

/**
 * Author of content.
 */
private Author author;

/**
 * Address of content image.
 */
private Media media;

private Boolean approved;

private List<RecipeElement> recipeElements;

@DynamoDBHashKey(attributeName = "id")
@DynamoDBAutoGeneratedKey
public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

@DynamoDBAttribute(attributeName = "Name")
public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

@DynamoDBTypeConverted(converter = EnumConverter.class)
@DynamoDBAttribute(attributeName = "Labels")
public List<KitchenLabel> getLabels() {
    return labels;
}

public void setLabels(List<KitchenLabel> labels) {
    this.labels = labels;
}

@DynamoDBTypeConverted(converter = ObjectConverter.class)
@DynamoDBAttribute(attributeName = "Author")
public Author getAuthor() {
    return author;
}

public void setAuthor(Author author) {
    this.author = author;
}

@DynamoDBTypeConverted(converter = ObjectConverter.class)
@DynamoDBAttribute(attributeName = "Media")
public Media getMedia() {
    return media;
}

public void setMedia(Media media) {
    this.media = media;
}

@DynamoDBAttribute(attributeName = "Approved")
public Boolean getApproved() {
    return approved;
}

public void setApproved(Boolean approved) {
    this.approved = approved;
}

@DynamoDBTypeConverted(converter = ObjectConverter.class)
@DynamoDBAttribute(attributeName = "RecipeElements")
public List<RecipeElement> getRecipeElements() {
    return recipeElements;
}

public void setRecipeElements(List<RecipeElement> recipeElements) {
    this.recipeElements = recipeElements;
}

RecipeElementクラス:

public class RecipeElement {


private Product product;
private Integer weight;

@DynamoDBTypeConverted(converter = ObjectConverter.class)
@DynamoDBHashKey(attributeName = "product")
public Product getProduct() {
    return product;
}

public void setProduct(Product product) {
        this.product = product;
    }

    public Integer getWeight() {
        return weight;
    }

    public void setWeight(Integer weight) {
        this.weight = weight;
    }
}

および製品クラス:

@DynamoDBTable(tableName = "product")
public class Product extends Request {
    /**
     * Id of kitchen content
     */
    private String id;

    /**
     * Name of product
     */
    private String name;

    /**
     * Calories in 100g
     */
    private Integer calories;

    /**
     * Fat in 100g
     */
    private Double fat;

    /**
     * Total carbo in 100g
     */
    private Double carbo;

    /**
     * Total Protein in 100g
     */
    private Double protein;

    /**
     * Labels of product for example gluten fee product
     */
    private List<ProductKind> productKinds;

    /**
     * Author of content.
     */
    private Author author;

    /**
     * Address of content image.
     */
    private Media media;

    private Boolean approved;

    @DynamoDBHashKey(attributeName = "id")
    @DynamoDBAutoGeneratedKey
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @DynamoDBAttribute(attributeName = "Name")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @DynamoDBAttribute(attributeName = "Calories")
    public Integer getCalories() {
        return calories;
    }

    public void setCalories(Integer calories) {
        this.calories = calories;
    }

    @DynamoDBAttribute(attributeName = "Fat")
    public Double getFat() {
        return fat;
    }

    public void setFat(Double fat) {
        this.fat = fat;
    }

    @DynamoDBAttribute(attributeName = "Carbo")
    public Double getCarbo() {
        return carbo;
    }

    public void setCarbo(Double carbo) {
        this.carbo = carbo;
    }

    @DynamoDBAttribute(attributeName = "Protein")
    public Double getProtein() {
        return protein;
    }

    public void setProtein(Double protein) {
        this.protein = protein;
    }

    @DynamoDBTypeConverted(converter = EnumConverter.class)
    @DynamoDBAttribute(attributeName = "ProductKinds")
    public List<ProductKind> getProductKinds() {
        return productKinds;
    }

    public void setProductKinds(List<ProductKind> productKinds) {
        this.productKinds = productKinds;
    }

    @DynamoDBTypeConverted(converter = ObjectConverter.class)
    @DynamoDBAttribute(attributeName = "Author")
    public Author getAuthor() {
        return author;
    }

    public void setAuthor(Author author) {
        this.author = author;
    }

    @DynamoDBTypeConverted(converter = ObjectConverter.class)
    @DynamoDBAttribute(attributeName = "Media")
    public Media getMedia() {
        return media;
    }

    public void setMedia(Media media) {
        this.media = media;
    }

    @DynamoDBAttribute(attributeName = "Approved")
    public Boolean getApproved() {
        return approved;
    }

    public void setApproved(Boolean approved) {
        this.approved = approved;
    }

そしてこれは私のコンバータークラスです:

public class ObjectConverter<T extends Object> 
implements DynamoDBTypeConverter<String, T> {

    ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public String convert(T object) {
        try {
            return objectMapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        throw new IllegalArgumentException("Unable to parse JSON");
    }

    @Override
    public T unconvert(String object) {
        try {
            T unconvertedObject = objectMapper.readValue(object, 
                new TypeReference<T>() {
            });
            return unconvertedObject;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

誰かがこの問題で私を助けることができますか?

5
D.Zet

短い答え

パブリックの空のコンストラクターをMediaクラスに追加します。

長い答え

コードでは、databaseController.get(Product.class, id)を実行しているときに、 DynamoDBMapperTableModel のアンダースコアメソッドpublic T unconvert(final Map<String,AttributeValue> object)を呼び出しています。

_@Override
public T unconvert(final Map<String,AttributeValue> object) {
    final T result = StandardBeanProperties.DeclaringReflect.<T>newInstance(targetType);
    if (!object.isEmpty()) {
        for (final DynamoDBMapperFieldModel<T,Object> field : fields()) {
            try {
                final AttributeValue value = object.get(field.name());
                if (value != null) {
                    field.unconvertAndSet(result, value);
                }
            } catch (final RuntimeException e) {
                throw new DynamoDBMappingException(
                    targetType.getSimpleName() + "[" + field.name() + "]; could not unconvert attribute", e
                );
            }
        }
    }
    return result;
}
_

このメソッドの最初の行では、リフレクションを使用してテーブルモデルの新しいインスタンスが作成され(この場合はProductの新しいインスタンス)、新しいインスタンスのフィールドが目的のクラスに変換され、それに応じて設定します。

StandardBeanProperties.DeclaringReflect.<T>newInstance(targetType)targetType.newInstance()を呼び出し、そのタイプは_Class<T>_です。

Class.newInstance()ドキュメント に記述されているように、クラスにnullaryコンストラクターがない場合、InstantiationExceptionがスローされます。この例外がスローされるシナリオは他にもありますが、私の経験では、おそらくpublicemptyコンストラクターが実装されていないことが原因です。

5
Tom Ben Gal

私にとっては、セッター方式のタイプミスでした。

@DynamoDBAttribute(attributeName = "closed_date_time")
public Date getClosedDateTime() {
    return closedDateTime;
}

public void setClosedDate(Date closedDateTime) {
    this.closedDateTime = closedDateTime;
}

setClosedDatesetClosedDateTimeに置き換えたところ、機能しました。

0
user3364622