Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Network pojo class and database entity class, how to avoid duplication

Sorry if the doubt is too conceptual, I am a little inexperienced...

I get JSON data from a URL with retrofit and I also want to store this data in my SQLite database. To create this database I will use the new Room ORM mapping.

I extract the network POJO classes using Android Studio plugin GsonFormat and it generates the Recipes, Ingredients and Steps classes below. And generate some Room Entity classes (more below)

As you see, both sets of classes are almost identical, bringing a lot of repeated code. But I also do not find it good to mix the use of these two sets of classes. And Room forbids entity objects to reference each other, so If I want to make use of the first set of classes, it will be more complicated...

My doubt is how are these cases handled in general?

--- Pojo - Gson understandable:

public class Recipes {

    private int id;
    private String name;
    private int servings;
    private String image;
    private List<Ingredients> ingredients;
    private List<Steps> steps;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public int getServings() {
        return servings;
    }

    public void setServings(int servings) {
        this.servings = servings;
    }

    public String getImage() {
        return image;
    }

    public void setImage(String image) {
        this.image = image;
    }

    public List<Ingredients> getIngredients() {
        return ingredients;
    }

    public void setIngredients(List<Ingredients> ingredients) {
        this.ingredients = ingredients;
    }

    public List<Steps> getSteps() {
        return steps;
    }

    public void setSteps(List<Steps> steps) {
        this.steps = steps;
    }

    @Override
    public String toString() {
        return "Recipes{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", servings=" + servings +
                ", image='" + image + '\'' +
                ", ingredients=" + ingredients +
                ", steps=" + steps +
                '}';
    }
}

class Ingredients {
    private double quantity;
    private String measure;
    private String ingredient;

    public double getQuantity() {
        return quantity;
    }

    public void setQuantity(double quantity) {
        this.quantity = quantity;
    }

    public String getMeasure() {
        return measure;
    }

    public void setMeasure(String measure) {
        this.measure = measure;
    }

    public String getIngredient() {
        return ingredient;
    }

    public void setIngredient(String ingredient) {
        this.ingredient = ingredient;
    }

    @Override
    public String toString() {
        return "Ingredients{" +
                "quantity=" + quantity +
                ", measure='" + measure + '\'' +
                ", ingredient='" + ingredient + '\'' +
                '}';
    }
}

class Steps {

    private int id;
    private String shortDescription;
    private String description;
    private String videoURL;
    private String thumbnailURL;

    public int getId() {
        return id;
    }

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

    public String getShortDescription() {
        return shortDescription;
    }

    public void setShortDescription(String shortDescription) {
        this.shortDescription = shortDescription;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getVideoURL() {
        return videoURL;
    }

    public void setVideoURL(String videoURL) {
        this.videoURL = videoURL;
    }

    public String getThumbnailURL() {
        return thumbnailURL;
    }

    public void setThumbnailURL(String thumbnailURL) {
        this.thumbnailURL = thumbnailURL;
    }

    @Override
    public String toString() {
        return "Steps{" +
                "id=" + id +
                ", shortDescription='" + shortDescription + '\'' +
                ", description='" + description + '\'' +
                ", videoURL='" + videoURL + '\'' +
                ", thumbnailURL='" + thumbnailURL + '\'' +
                '}';
    }
}

--- Room Entity classes

@Entity
public class Recipe {

    @PrimaryKey
    private int id;

    private String name;
    private int servings;
    private String image;

}


@Entity(foreignKeys = @ForeignKey(entity = Recipe.class,
        parentColumns = "id",
        childColumns = "recipeId"))
public class Ingredient {

    @PrimaryKey
    private int id;
    private double quantity;
    private String measure;
    private String ingredient;


    private int recipeId;

}
like image 501
alexpfx Avatar asked May 24 '17 02:05

alexpfx


2 Answers

You can use one Object class which satisfies both Gson and Room mapping.

For example.

@Entity
public class Recipe {
  @PrimaryKey           // Room annotation
  @SerializedName("id") // Gson annotation
  private int id;

  @ColumnInfo(name = "name") // Room annotation
  @SerializedName("name")    // Gson annotation
  private String name;
  ....
}

In compilation process, Gson and Room will look for annotations to map with correct entities.
In general, they are just normal POJO objects with specific annotations to help other frameworks to use it correctly.

like image 117
Quang Nguyen Avatar answered Oct 20 '22 19:10

Quang Nguyen


There are 2 solutions I can think of, and you have to consider the tradeoff based on the objectives of your system's intended design :

1.By minimizing duplication, such as using one of the approach listed in this thread in combining 2 of the classes, the response object and entity object are now tightly coupled. This is fine if you're confident all of the fields in the response is appropriate to be consumed as Room entity all the time.

2.Instead of reducing duplication, we allow it to happen for the favor of having more extendability.

Normally in my projects, I personally prefer the second approach, simply because without direct dependency, I don't have to store unnecessary fields (sometimes I store some field as metadata instead of using Room), and plus I don't have to write migrations code everytime the response change.

like image 32
atjua Avatar answered Oct 20 '22 21:10

atjua