D.Zet
D.Zet

Reputation: 779

JAVA DynamoDB: not supported; requires @DynamoDBTyped or @DynamoDBTypeConverted

I've created CRUD methods but i have some problems with that.

This is my add method code:

public Product addProduct(Product content) {
    Product item = new Product();

    item.setName(content.getName());
    item.setCalories(content.getCalories());
    item.setFat(content.getFat());
    item.setCarbo(content.getCarbo());
    item.setProtein(content.getProtein());
    item.setProductKinds(content.getProductKinds());
    item.setAuthor(content.getAuthor());
    item.setMedia(content.getMedia());
    item.setApproved(content.getApproved());


    databaseController.saveTest(item);
    logger.log("Item created");


    return item;
}

This is my editProduct method:

public Product editProduct(Product product) {
    Product databaseProduct = databaseController.get(Product.class, product.getId());
    databaseProduct.setAllProducts(product);
    databaseController.save(databaseProduct);
    return databaseProduct;
}

In model class I think that I have made everything right :

package pl.javamill.model.kitchen;

import com.amazonaws.services.dynamodbv2.datamodeling.*;
import pl.javamill.model.Request;
import pl.javamill.model.common.Author;
import pl.javamill.model.common.AuthorConverter;
import pl.javamill.model.common.Media;
import pl.javamill.model.common.MediaConverter;

import java.util.List;

@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;
}

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

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

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

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

@DynamoDBTypeConverted(converter = MediaConverter.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 void setAllProducts(Product product) {
    if (!getName().equals(product.getName())) {
        setName(product.getName());
    }
    if (!getCalories().equals(product.getCalories())) {
        setCalories(product.getCalories());
    }
    if (!getFat().equals(product.getFat())) {
        setFat(product.getFat());
    }
    if (!getCarbo().equals(product.getCarbo())) {
        setCarbo(product.getCarbo());
    }
    if (!getProtein().equals(product.getProtein())) {
        setProtein(product.getProtein());
    }
    if (!getProductKinds().equals(product.getProductKinds())) {
        setProductKinds(product.getProductKinds());
    }
    if (!getAuthor().equals(product.getAuthor())) {
        setAuthor(product.getAuthor());
    }
    if (!getMedia().equals(product.getMedia())) {
        setMedia(product.getMedia());
    }
    if (!getApproved().equals(product.getApproved())) {
        setApproved(product.getApproved());
    }

}


@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Product product = (Product) o;

    if (name != null ? !name.equals(product.name) : product.name != null) return false;
    if (calories != null ? !calories.equals(product.calories) : product.calories != null) return false;
    if (fat != null ? !fat.equals(product.fat) : product.fat != null) return false;
    if (carbo != null ? !carbo.equals(product.carbo) : product.carbo != null) return false;
    if (protein != null ? !protein.equals(product.protein) : product.protein != null) return false;
    if (productKinds != null ? !productKinds.equals(product.productKinds) : product.productKinds != null)
        return false;
    if (author != null ? !author.equals(product.author) : product.author != null) return false;
    if (media != null ? !media.equals(product.media) : product.media != null) return false;
    return approved != null ? approved.equals(product.approved) : product.approved == null;
}

@Override
public int hashCode() {
    int result = id != null ? id.hashCode() : 0;
    result = 31 * result + (name != null ? name.hashCode() : 0);
    result = 31 * result + (calories != null ? calories.hashCode() : 0);
    result = 31 * result + (fat != null ? fat.hashCode() : 0);
    result = 31 * result + (carbo != null ? carbo.hashCode() : 0);
    result = 31 * result + (protein != null ? protein.hashCode() : 0);
    result = 31 * result + (productKinds != null ? productKinds.hashCode() : 0);
    result = 31 * result + (author != null ? author.hashCode() : 0);
    result = 31 * result + (media != null ? media.hashCode() : 0);
    result = 31 * result + (approved != null ? approved.hashCode() : 0);
    return result;
}
}

ProductKindConventor:

public class ProductKindConverter implements DynamoDBTypeConverter<String, List<ProductKind>> {

@Override
public String convert(List<ProductKind> objects) {
    //Jackson object mapper
    ObjectMapper objectMapper = new ObjectMapper();
    try {
        String objectsString = objectMapper.writeValueAsString(objects);
        return objectsString;
    } catch (JsonProcessingException e) {
        //do something
    }
    return null;
}

@Override
public List<ProductKind> unconvert(String objectsString) {
    ObjectMapper objectMapper = new ObjectMapper();
    try {
        List<ProductKind> objects = objectMapper.readValue(objectsString, new TypeReference<List<ProductKind>>(){});
        return objects;
    } catch (JsonParseException e) {
        //do something
    } catch (JsonMappingException e) {
        //do something
    } catch (IOException e) {
        //do something
    }
    return null;
}
}

Save method in dbcontroller:

public void saveTest(Product product){
    AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
            .withRegion(regions).build();
    mapper = new DynamoDBMapper(client);
    mapper.save(product);

}

Get product from DB method:

public Product getTest(String id) {
    AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
            .withRegion(regions).build();
    mapper = new DynamoDBMapper(client);
    Product retrivedProduct = mapper.load(Product.class, id);
    return retrivedProduct;
}

Unit test passed, everything seems to be ok, but when I was using POSTMAN to test, i got some errors. I send product as json and it seems like that:

{"id":null,"name":"yoloornotyolo","calories":1000,"fat":400.0,"carbo":20.0,"protein":40.0,"productKinds":["MEAT"],"author":{"name":"Plejer Annołn","id":"testID2"},"media":{"name":"heheszki","url":"http://blabla.pl","mediaType":"IMAGE"},"approved":false}

Then I got ""message": "Internal server error"", so I check log file and that what I can see:

not supported; requires @DynamoDBTyped or @DynamoDBTypeConverted: com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException

I dont know what is wrong. Can someone explain me what I suppose to do?

Upvotes: 13

Views: 36250

Answers (5)

Ali Ather
Ali Ather

Reputation: 1

@DynamoDBTypeConvertedJson This is pretty helpful if you want to dump list of objects in dynamo

   @DynamoDBTypeConvertedEnum
   @DynamoDBTypeConverted(converter = ProductKindConverter.class)
   @DynamoDBAttribute(attributeName = "ProductKinds")
   public List<ProductKind> getProductKinds() {
       return productKinds;
   }
Instead of this

You can do something like this

   @DynamoDBTypeConvertedJson
   @DynamoDBAttribute(attributeName = "ProductKinds")
   public List<ProductKind> getProductKinds() {
       return productKinds;
   }

Upvotes: 0

anaysarkar7
anaysarkar7

Reputation: 73

you will run into this error if the mapper class is

missing a No-Arg constructor

or has

invalid Setters/Getters

that do not comply with the camel case convention.

(Solved) Error: could not unconvert attribute dynamodb

Upvotes: 0

newbieProgrammer
newbieProgrammer

Reputation: 336

I also had this error and it is a bit misleading, all we need to do is use @DynamoDBDocument on the class file of the referenced classes.

For your case, just put this annotation on ProductKind, like this:

@DynamoDBDocument
class ProductKind {
    ...
}

It should persist into dynamoDB as a map in the product table.

Upvotes: 2

Volodymyr Zubariev
Volodymyr Zubariev

Reputation: 443

It would be useful to have a stack trace for the exception. In the intermediate message it contains the data type and field information which can not be converted.

Usually you do not need to make any convertors, just be sure to have @DynamoDBDocument on all custom classes, and @DynamoDBTypeConvertedEnum on all custom enums used.

Upvotes: 8

F_SO_K
F_SO_K

Reputation: 14799

It would be useful to see your saveProduct method. You are using DynamoDBMapper annotations but it looks like you are trying to save an item object, not a Product object. Rather than

Item item = new Item()

Use

Product item = new Product();
item.setName...

One thing I would mention is that you can offload the handling of your generated id to DyanmoDBMapper using the DynamoDBAutoGeneratedKey annotation

In your model class do this:

@DynamoDBHashKey(attributeName = "Id")
@DynamoDBAutoGeneratedKey
public String getId() { return id; }
public void setId(String id) { this.id = id; } 

And then you dont need to handle id in your addProduct method

EDIT: Your saveProduct method should be along the lines of

DynamoDBMapper mapper = new DynamoDBMapper(dynamoDBClient);
mapper.save(item)

I doubt you need to be passing a tablename as that is an annotation on your Product object. You shouldn't be using putItem.

EDIT: Your saveProduct method should take a Product object, not an item object. Dont use putItem because this doesn't know about your model class. Use DynamoDBMapper save function. You dont need to specify the table name as that's an annotation on your Product class (the table is called 'product'). You should use AmazonDynamoDB for interfacing to DynamoDB. The method below return is void, so you will need to update the addProduct method.

public void saveProduct(Product item) {

logger.log("Starting to save an item");

//You need a new method to return an AmazonDynamoDB interface 
AmazonDynamoDB dynamoDB = getAmazonDynamoDB();
DynamoDBMapper mapper = new DynamoDBMapper(dynamoDB);
mapper.save(item)

logger.log("Item stored");

return saved;
}

EDIT: You build an AmazonDynamoDB interface like this

   // Set to your chosen region
   AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
   .withRegion(Regions.US_WEST_2)
   .build(); 

EDIT: DynamoDBMapper only supports certain data types.

You have three complex data types there; productKind, author and media. If you want to store these in your database, you will need to write a Convertor Class for each one. The convertor class typically define how you convert your object to a string and back again.

EDIT: Your set, ProductKind, will need a convertor that looks like this. Just change 'MyObject' to ProductKinds.

Upvotes: 8

Related Questions