Android Architecture Components: Using Enums

23,193

Solution 1

I can use enum values at Room with TypeConverters. There are some parts to change at your code:

1) You must declare your Entity's fields public or they must have public getters/setters. Or you'll get below error:

yourField is not public in YourEntity; cannot be accessed from outside package

2) You don't need the @Embedded annotation for your status field. It is for nested objects. More from docs.

3) You didn't use the @TypeConverters annotation at the correct place. In your case it can be set above the status field. More from docs.

4) You must define a constructor for your Entity or you'll get below error:

Entities and Pojos must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type).

You can define an empty constructor to skip this error.

5) Use int instead of Integer in your TypeConverter.

Sum; below works as expected:

@Entity(tableName = "tasks")
public class Task extends SyncEntity {

    @PrimaryKey(autoGenerate = true)
    public String taskId;

    public String title;

    /** Status of the given task.
     * Enumerated Values: 0 (Active), 1 (Inactive), 2 (Completed)
     */
    @TypeConverters(StatusConverter.class)
    public Status status;

    @TypeConverters(DateConverter.class)
    public Date startDate;

    // empty constructor 
    public Task() {
    }

    public enum Status {
        ACTIVE(0),
        INACTIVE(1),
        COMPLETED(2);

        private int code;

        Status(int code) {
            this.code = code;
        }

        public int getCode() {
            return code;
        }
    }
}

Solution 2

I encountered a similar problem. Devrim's answer to the subject was very helpful. However my entity have nullable one.

In this case, the insert operation returns an error. This is caused by the primitive "code" member in the enum type.

I have developed the following implemantation to resolve this error.

First I fixed the enum type as follows.

package com.example.models.entities.enums;


import androidx.room.TypeConverter;

public enum Status {
    ACTIVE(0),
    INACTIVE(1),
    COMPLETED(2);

    private final Integer code;

    Status(Integer value) {
        this.code = value;
    }
    public Integer getCode() {
        return code;
    }

    @TypeConverter
    public static Status getStatus(Integer numeral){
        for(Status ds : values()){
            if(ds.code == numeral){
                return ds;
            }
        }
        return null;
    }

    @TypeConverter
    public static Integer getStatusInt(Status status){

        if(status != null)
            return status.code;

        return  null;
    }

}

Then I updated the entity as follows.

package com.example.models.entities;


import com.example.models.TypeConverters.DateConverter;
import com.example.models.entities.enums.Status;

import java.util.Date;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import androidx.room.TypeConverters;

@Entity(tableName = "tasks")
@TypeConverters({Status.class, DateConverter.class})

public class Task extends SyncEntity {

    @PrimaryKey(autoGenerate = true)
    String taskId;

    @NonNull
    String title;

    /** Status of the given task.
     * Enumerated Values: 0 (Active), 1 (Inactive), 2 (Completed)
     */
    @Nullable
    Status status;

    @NonNull
    Date startDate;


    public String getTaskId() {
        return taskId;
    }

    public void setTaskId(String taskId) {
        this.taskId = taskId;
    }

    @NonNull
    public String getTitle() {
        return title;
    }

    public void setTitle(@NonNull String title) {
        this.title = title;
    }

    @Nullable
    public Status getStatus() {
        return status;
    }

    public void setStatus(@Nullable Status status) {
        this.status = status;
    }

    @NonNull
    public Date getStartDate() {
        return startDate;
    }

    public void setStartDate(@NonNull Date startDate) {
        this.startDate = startDate;
    }
}

So, I could manage nullable enum property in Room database.

I hope I've been able to help with a similar situation.

Happy coding.

Share:
23,193
Bohsen
Author by

Bohsen

Updated on September 17, 2020

Comments

  • Bohsen
    Bohsen almost 4 years

    Is it possible to use an Enum type as an embedded field in an Entity class with the new Android Architecture Components and Room Persistence Library?

    My Entity (with embedded Enum):

    @Entity(tableName = "tasks")
    public class Task extends SyncEntity {
    
        @PrimaryKey(autoGenerate = true)
        String taskId;
    
        String title;
    
        /** Status of the given task.
         * Enumerated Values: 0 (Active), 1 (Inactive), 2 (Completed)
         */
        @Embedded
        Status status;
    
        @TypeConverters(DateConverter.class)
        Date startDate;
    
        @TypeConverters(StatusConverter.class)
        public enum Status {
            ACTIVE(0),
            INACTIVE(1),
            COMPLETED(2);
    
            private int code;
    
            Status(int code) {
                this.code = code;
            }
    
            public int getCode() {
                return code;
            }
        }
    }
    

    My TypeConverter:

    public class StatusConverter {
    
        @TypeConverter
        public static Task.Status toStatus(int status) {
            if (status == ACTIVE.getCode()) {
                return ACTIVE;
            } else if (status == INACTIVE.getCode()) {
                return INACTIVE;
            } else if (status == COMPLETED.getCode()) {
                return COMPLETED;
            } else {
                throw new IllegalArgumentException("Could not recognize status");
            }
        }
    
        @TypeConverter
        public static Integer toInteger(Task.Status status) {
            return status.getCode();
        }
    }
    

    When I compile this I get an error saying Error:(52, 12) error: Entities and Pojos must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type).

    Update 1 My SyncEntity class:

    /**
     * Base class for all Room entities that are synchronized.
     */
    @Entity
    public class SyncEntity {
    
        @ColumnInfo(name = "created_at")
        Long createdAt;
    
        @ColumnInfo(name = "updated_at")
        Long updatedAt;
    }
    
  • Ahmad Kayyali
    Ahmad Kayyali over 6 years
    Thanks @Devrim for it was "Use int instead of Integer in your TypeConverter"
  • Brill Pappin
    Brill Pappin over 6 years
    If you must use integer values for your enums, this is the correct way to do it. Never use the ordinal.
  • Lumii
    Lumii about 6 years
    What's the reason not using ordinal?
  • AdamHurwitz
    AdamHurwitz almost 6 years
    I'd prefer to call the Converters on the Database level if they will be commonly used.
  • kassim
    kassim over 5 years
    The reason you shouldn't use the ordinal is that you may choose to re-arrange the order of your enums (e.g. if you add a new one) - so when its pulled out of the DB you may map it to the wrong value. You're probably better off using a String. @CheokYanCheng