Search code examples
javaandroidabstract-classpojoandroid-room

Room Abstract Pojo


I'm creating for fun an android application that tracks the spendings. I'm using Room to persist the user's data and I have POJOs that show the daily/weekly/monthly summaries.

These classes are quite similar, thus I would like to have one abstract POJO that contains the fields and extensions of it that reformat to the correct format. Something like:

public abstract class PeriodInformation {

PeriodInformation(@NonNull Calendar mCalendar, Integer mPeriodSpendingCount, Float mPeriodSpendingSum) {
    this.mCalendar = mCalendar;
    this.mPeriodSpendingCount = mPeriodSpendingCount;
    this.mPeriodSpendingSum = mPeriodSpendingSum;
}

@ColumnInfo(name = "DateTime")
private final Calendar mCalendar;
@ColumnInfo(name = "SpendingCount")
private Integer mPeriodSpendingCount;
@ColumnInfo(name = "SpendingSum")
private Float mPeriodSpendingSum;

// Some other code, e.g., getters, equal override,...
}

Here the extension:

public class WeekInformation extends PeriodInformation{

public WeekInformation(@NonNull Calendar mCalendar, Integer mPeriodSpendingCount, Float mMonthSpendingSum) {
    super(mCalendar, mPeriodSpendingCount, mMonthSpendingSum);
}

@Override
public String getPeriodRepresentation() {
    //return representation;
}

}

However, I get following error message for the WeekInformation Class:

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).

So it seems that this is not possible in Room, thus I would be happy to get some suggestion how to not have to copy the same code too often.

thank you.

EDIT: I use following DAO code to aggregate to the POJO, the column calendarDate has following format "yyyy-MM-dd'T'HH:mm:ss.SSSXXX":

@Query("SELECT date(datetime(calendarDate)) AS 'DateTime', count(uID) AS 'SpendingCount', sum(value)  AS 'SpendingSum' from spending GROUP BY date(datetime(calendarDate))")
LiveData<List<DayInformation>> loadDayInformation();

Solution

  • I was able to make this work for me, using the Embedded annotation, allowing direct access to the fields of the embedded data type.

    public class DayInformation {
    
        @Embedded
        public PeriodInformation periodInformation;
    
        @Override
        public String toString() {
            return "DayInformation{" +
               "periodInformation=" + periodInformation +
               '}';
        }
    }
    

    and

    public class PeriodInformation {
    
        PeriodInformation(Calendar timestamp,
                          int periodSpendingCount,
                          float periodSpendingSum) {
            this.timestamp = timestamp;
            this.periodSpendingCount = periodSpendingCount;
            this.periodSpendingSum = periodSpendingSum;
        }
    
        @ColumnInfo(name = "DateTime")
        public final Calendar timestamp;
        @ColumnInfo(name = "SpendingCount")
        public Integer periodSpendingCount;
        @ColumnInfo(name = "SpendingSum")
        public Float periodSpendingSum;
    
        @Override
        public String toString() {
            final DateFormat dateInstance = SimpleDateFormat.getDateInstance();
            String date = timestamp == null ? "null" : dateInstance.format(timestamp.getTime());
            return "PeriodInformation{" +
                   "timestamp='" + date + '\'' +
                   ", periodSpendingCount=" + periodSpendingCount +
                   ", periodSpendingSum=" + periodSpendingSum +
                   '}';
        }
    }
    

    plus

    @Entity
    public class Spending {
        @PrimaryKey(autoGenerate = true)
        public int uid;
    
        @ColumnInfo(name = "calendarDate")
        public Calendar timestamp;
    
        @ColumnInfo(name = "value")
        public float value;
    
        public Spending(@NonNull Calendar timestamp, float value) {
            this.timestamp = timestamp;
            this.value = value;
        }
    
        @Override
        public String toString() {
            final DateFormat dateInstance = SimpleDateFormat.getDateInstance();
            String date = timestamp == null ? "null" : dateInstance.format(timestamp.getTime());
    
    
            return "Spending{" +
                   "uid=" + uid +
                   ", timestamp='" + date + '\'' +
                   ", value=" + value +
                   '}';
        }
    }
    

    and a DAO

    @Dao
    public interface SpendingDao {
    
        @Insert
        void insertAll(Spending... spendings);
    
        @Query("SELECT * FROM spending")
        LiveData<List<Spending>> findAll();
    
        @Query("SELECT calendarDate AS 'DateTime', count(uID) AS 'SpendingCount', sum(value)  AS 'SpendingSum' from spending GROUP BY date(datetime(calendarDate))")
        LiveData<List<DayInformation>> loadDayInformation();
    }
    

    gives the following output

    aggregated data is 
    DayInformation{periodInformation=PeriodInformation{timestamp='Jun 26, 2018', periodSpendingCount=8, periodSpendingSum=184.0}}
    spending data is Spending{uid=1, timestamp='Jun 26, 2018', value=23.0}
    spending data is Spending{uid=2, timestamp='Jun 26, 2018', value=23.0}
    spending data is Spending{uid=3, timestamp='Jun 26, 2018', value=23.0}
    spending data is Spending{uid=4, timestamp='Jun 26, 2018', value=23.0}
    spending data is Spending{uid=5, timestamp='Jun 26, 2018', value=23.0}
    spending data is Spending{uid=6, timestamp='Jun 26, 2018', value=23.0}
    spending data is Spending{uid=7, timestamp='Jun 26, 2018', value=23.0}
    spending data is Spending{uid=8, timestamp='Jun 26, 2018', value=23.0}