It's rather, a question than a problem. I have two entities with a String field called Type. Both have over twenty kinds of type: "Bills", "Transport", "Awards" etc. I want to put all them to the charts, and this is creating little problem because it totally creates over 60((!) 60 because I want to take data by date. For example day, month etc.)instances in ViewModel constructor and over 60 observers in fragment. So my question is:
There is some problem to make it more sexy for code (orderliness), performance of app, or I should not care about it?
One of entity (they are almost same):
#Entity(tableName = "expense_table")
public class Expense {
#PrimaryKey(autoGenerate = true)
private int expenseId;
private String note;
private Double value;
private String type;
private Long dateLong = System.currentTimeMillis();
private String date = new SimpleDateFormat("MM/yyyy").format(new Date(dateLong));
private static Calendar cal = Calendar.getInstance();
private int month = cal.get(Calendar.MONTH) + 1;
private int day = cal.get(Calendar.DAY_OF_MONTH);
private int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
private String weekDay = new DateFormatSymbols().getWeekdays()[dayOfWeek];
Query in Dao:
#Query("SELECT SUM(value) FROM expense_table WHERE type = :type")
MutableLiveData<List<Double>> getTotalType(String type);
One of Repository(As you can see there is many instances now, co code it will more complicated):
public class ExpenseRepository {
private ExpenseDao expenseDao;
private LiveData<List<Expense>> allExpensesDay;
private LiveData<List<Expense>> allExpensesMonth;
private LiveData<List<Expense>> allExpenses;
private LiveData<Double> totalValue;
private LiveData<Double> totalValueDay;
private LiveData<Double> totalValueMonth;
private MutableLiveData<List<Double>> totalType;
private String type = "Bills";
public ExpenseRepository(Application application) {
ExpenseIncomeDatabase database = ExpenseIncomeDatabase.getInstance(application);
expenseDao = database.expenseDao();
allExpenses = expenseDao.getAllExpenses();
allExpensesDay = expenseDao.getExpensesDay();
allExpensesMonth = expenseDao.getExpensesMonth();
totalValueDay = expenseDao.getTotalValueDay();
totalValueMonth = expenseDao.getTotalValueMonth();
totalValue = expenseDao.getTotalValue();
totalType = expenseDao.getTotalType(type);
One of Repository:
private LiveData<Double> totalExpenseValue;
private LiveData<Double> totalIncomeValue;
private MutableLiveData<List<Double>> totalType;
public TotalStatsViewModel(#NonNull Application application) {
super(application);
ExpenseRepository expenseRepository = new ExpenseRepository(application);
ExpenseRepository expenseChartsRepository = new ExpenseRepository(application);
IncomeRepository incomeRepository = new IncomeRepository(application);
totalExpenseValue = expenseChartsRepository.getTotalValue();
totalIncomeValue = incomeRepository.getTotalValue();
totalType = expenseRepository.getTotalType();
}
public LiveData<Double> getTotalExpenseValue() {
return totalExpenseValue;
}
public LiveData<Double> getTotalIncomeValue() {
return totalIncomeValue;
}
public LiveData<List<Double>> getTotalType() {
return totalType;
}
As you can see there is a lot of code now. In future it will be much more. Maybe it stupid question, but I just care about orderliness and performance.
Related
I've created database with Room with MVVM, and I'm facing a problem I hope you could help me solve this.
I have a database containing 3 entities Player, Group and Standings, where Standings is the relationship between Player and Group.
The things is that I want to present standings, but Standings only contains the IDs of Group and Player, and I want it to also show the name of the player which is in Player, and I'm using LiveData, adapters and ViewModels, so when I return the list of LiveData<List<Standings>> to observe, it doesn't contain the name of the player.
Does someone know how I can pass the name as well?
The only solution that I could think of is to create new class that has a Standing and the name(String) as the instances and then return it to observe.
But it doesn't feel natural so I thought I could find here a better, more elegant solution.
groupStandingsViewModel = ViewModelProviders.of(this, new GroupStandingsViewModelFactory(this.getApplication(), 0)).get(GroupStandingsViewModel.class);
groupStandingsViewModel.getAllStandings().observe(this, new Observer<List<Standings>>() {
#Override
public void onChanged(List<Standings> standings) {
adapter.setStanding(standings);
}
});
I'm expecting to be able to have both the standings and the names as given in the onChanged function of the observe.
The only solution that I could think of is to create new class that
has a Standing and the name(String) as the instances and then return
it to observe.
But it doesn't feel natural so I thought I could find here a better,
more elegant solution.
It may sound unnatural but you will need some new code, so what else? (P.S. that's rhetorical).
I'd suggest a new class is the way to but something like:-
public class PlayerGroupStanding {
#Embedded
Standings standing;
String playerName;
long playerId;
String groupName;
long groupId;
public PlayerGroupStanding() {
}
public Standings getStanding() {
return standing;
}
public void setStanding(Standings standing) {
this.standing = standing;
}
public long getPlayerId() {
return playerId;
}
public void setPlayerId(long playerId) {
this.playerId = playerId;
}
public String getPlayerName() {
return playerName;
}
public void setPlayerName(String playerName) {
this.playerName = playerName;
}
public long getGroupId() {
return groupId;
}
public void setGroupId(long groupId) {
this.groupId = groupId;
}
public String getGroupName() {
return groupName;
}
public void setGroupname(String groupname) {
this.groupName = groupname;
}
}
This could be used in conjunction with a Dao Query along the lines of :-
#Query("SELECT * FROM standings JOIN player ON mapToPlayer = playerId JOIN `group` ON mapToGroup = groupId")
List<PlayerGroupStanding> getAllStandingsWithPlayerAndGroupDetails();
Note the above makes many assumptions as to names, although the given names should be self-explantory.
Note that the names of the variables e.g. playerName should match the column name as returned from the query.
Additional
Re comment
what object does the SELECT returns in a query. I understand that if I use a SELECT * then the object will be of the class that is in the FROM. but when I returns columns, what will be the object that I need to mention in the LiveData>? Where can if find information about it? Thank you very much in advance:D
The SELECT actually returns a Cursor, it's the annotation that then writes the code that extracts the columns as per the definition of the method mapping the columns according to the class's member names to return the object, if additional columns exist they are ignored. The FROM clause doesn't determine the resultant object returned the method after the #Query does according to the type returned from that method.
The actual code can be found after building (Ctrl + F9) in the generated code of the project e.g.
So for the example above then the respective method that is generated in the Dao's code (i.e. Dao suffixd with _impl) is :-
#Override
public List<PlayerGroupStandings> getAllPlayerGroupStandings() {
final String _sql = "SELECT * FROM standings JOIN player ON mapToPlayer = playerId JOIN `group` ON mapToGroup = groupId";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
__db.assertNotSuspendingTransaction();
final Cursor _cursor = DBUtil.query(__db, _statement, true, null);
try {
final int _cursorIndexOfMapToPlayer = CursorUtil.getColumnIndexOrThrow(_cursor, "mapToPlayer");
final int _cursorIndexOfMapToGroup = CursorUtil.getColumnIndexOrThrow(_cursor, "mapToGroup");
final LongSparseArray<ArrayList<Player>> _collectionPlayers = new LongSparseArray<ArrayList<Player>>();
final LongSparseArray<ArrayList<Group>> _collectionGroup = new LongSparseArray<ArrayList<Group>>();
while (_cursor.moveToNext()) {
if (!_cursor.isNull(_cursorIndexOfMapToPlayer)) {
final long _tmpKey = _cursor.getLong(_cursorIndexOfMapToPlayer);
ArrayList<Player> _tmpPlayersCollection = _collectionPlayers.get(_tmpKey);
if (_tmpPlayersCollection == null) {
_tmpPlayersCollection = new ArrayList<Player>();
_collectionPlayers.put(_tmpKey, _tmpPlayersCollection);
}
}
if (!_cursor.isNull(_cursorIndexOfMapToGroup)) {
final long _tmpKey_1 = _cursor.getLong(_cursorIndexOfMapToGroup);
ArrayList<Group> _tmpGroupCollection = _collectionGroup.get(_tmpKey_1);
if (_tmpGroupCollection == null) {
_tmpGroupCollection = new ArrayList<Group>();
_collectionGroup.put(_tmpKey_1, _tmpGroupCollection);
}
}
}
_cursor.moveToPosition(-1);
__fetchRelationshipplayerAsarmAndroidroommigrationsPlayer(_collectionPlayers);
__fetchRelationshipgroupAsarmAndroidroommigrationsGroup(_collectionGroup);
final List<PlayerGroupStandings> _result = new ArrayList<PlayerGroupStandings>(_cursor.getCount());
while(_cursor.moveToNext()) {
final PlayerGroupStandings _item;
final Standings _tmpStandings;
if (! (_cursor.isNull(_cursorIndexOfMapToPlayer) && _cursor.isNull(_cursorIndexOfMapToGroup))) {
_tmpStandings = new Standings();
final Long _tmpMapToPlayer;
_tmpMapToPlayer = _cursor.getLong(_cursorIndexOfMapToPlayer);
_tmpStandings.setMapToPlayer(_tmpMapToPlayer);
final Long _tmpMapToGroup;
_tmpMapToGroup = _cursor.getLong(_cursorIndexOfMapToGroup);
_tmpStandings.setMapToGroup(_tmpMapToGroup);
} else {
_tmpStandings = null;
}
ArrayList<Player> _tmpPlayersCollection_1 = null;
if (!_cursor.isNull(_cursorIndexOfMapToPlayer)) {
final long _tmpKey_2 = _cursor.getLong(_cursorIndexOfMapToPlayer);
_tmpPlayersCollection_1 = _collectionPlayers.get(_tmpKey_2);
}
if (_tmpPlayersCollection_1 == null) {
_tmpPlayersCollection_1 = new ArrayList<Player>();
}
ArrayList<Group> _tmpGroupCollection_1 = null;
if (!_cursor.isNull(_cursorIndexOfMapToGroup)) {
final long _tmpKey_3 = _cursor.getLong(_cursorIndexOfMapToGroup);
_tmpGroupCollection_1 = _collectionGroup.get(_tmpKey_3);
}
if (_tmpGroupCollection_1 == null) {
_tmpGroupCollection_1 = new ArrayList<Group>();
}
_item = new PlayerGroupStandings();
_item.standings = _tmpStandings;
_item.players = _tmpPlayersCollection_1;
_item.group = _tmpGroupCollection_1;
_result.add(_item);
}
return _result;
} finally {
_cursor.close();
_statement.release();
}
}
How can I associate two tables so that I can create a database that sort of matches the image attached.
I understand that the experiment ID, Date, Name has to be part of the parent table, with the Trial #, Variable, and Result be the child table associated to the parent table. However, I'm not very sure how to implement this in Room Database persistence using Entity and Dao. Can someone show me how?
I want the results to be that if the user selects the experiment in a recyclerview, it will be lead to another activity showing inputs of Variable and Result to add trials at the top while displaying the results of just that experiment below using a Recyclerview.
I believe the following Working Example is one of the ways that you can achieve what you want.
The Code
The Experiment Entity (table) Experiment.java
#Entity
public class Experiment {
#PrimaryKey(autoGenerate = true)
private long experimentId;
private String experimentName;
private String experimentDate;
public Experiment() {
}
#Ignore
public Experiment(String name, String date) {
this.experimentName = name;
this.experimentDate = date;
}
public long getExperimentId() {
return experimentId;
}
public void setExperimentId(long experimentId) {
this.experimentId = experimentId;
}
public String getExperimentName() {
return experimentName;
}
public void setExperimentName(String experimentName) {
this.experimentName = experimentName;
}
public String getExperimentDate() {
return experimentDate;
}
public void setExperimentDate(String experimentDate) {
this.experimentDate = experimentDate;
}
}
Nothing special except perhaps the #Ignore'd constructor (for convenience)
The Trial Entity Trial.java
#Entity
public class Trial {
#PrimaryKey(autoGenerate = true)
private long trialId;
#ForeignKey(entity = Experiment.class, parentColumns = {BaseColumns._ID},childColumns = "parentExperiment", onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE)
private long parentExperiment;
private String trialVariable;
private String trialResult;
public Trial() {
}
#Ignore
public Trial(long parentExperimentId, String variable, String result) {
this.parentExperiment = parentExperimentId;
this.trialVariable = variable;
this.trialResult = result;
}
public long getTrialId() {
return trialId;
}
public void setTrialId(long trialId) {
this.trialId = trialId;
}
public long getParentExperiment() {
return parentExperiment;
}
public void setParentExperiment(long parentExperiment) {
this.parentExperiment = parentExperiment;
}
public String getTrialVariable() {
return trialVariable;
}
public void setTrialVariable(String trialVariable) {
this.trialVariable = trialVariable;
}
public String getTrialResult() {
return trialResult;
}
public void setTrialResult(String trialResult) {
this.trialResult = trialResult;
}
}
Nothing special except perhaps the #Ignore'd constructor (for convenience)
Dao.java (combined for convenience)
public interface Dao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
long[] insertExperiments(Experiment... experiments);
#Insert(onConflict = OnConflictStrategy.IGNORE)
long insertExperiment(Experiment experiment);
#Insert(onConflict = OnConflictStrategy.IGNORE)
long[] insertTrials(Trial... trials);
#Insert(onConflict = OnConflictStrategy.IGNORE)
long insertTrial(Trial trial);
#Update(onConflict = OnConflictStrategy.IGNORE)
int updateExperiments(Experiment... experiments);
#Update(onConflict = OnConflictStrategy.IGNORE)
int updateExperiment(Experiment experiment);
#Update(onConflict = OnConflictStrategy.IGNORE)
int updateTrials(Trial... trials);
#Update(onConflict = OnConflictStrategy.IGNORE)
int updateTrial(Trial trial);
#Delete
int deleteExperiments(Experiment... experiments);
#Delete
int deleteExperiment(Experiment experiment);
#Delete
int deleteTrials(Trial... trials);
#Delete
int deleteTrial(Trial trial);
#Query("SELECT * FROM Experiment")
List<Experiment> getAllexperiments();
#Query("SELECT * FROM Experiment WHERE experimentDate BETWEEN :startdate AND :enddate")
List<Experiment> getExperimentsInDateRange(String startdate, String enddate);
#Query("SELECT * FROM Trial")
List<Trial> getAllTrials();
#Query("SELECT * FROM Experiment JOIN Trial ON parentExperiment = experimentId")
List<TrialWithExperiment> getExperimentsWithTrials();
public class TrialWithExperiment {
private long experimentId;
private String experimentName;
private String experimentDate;
private long trialId;
private String trialVariable;
private String trialResult;
public long getExperimentId() {
return experimentId;
}
public void setExperimentId(long experimentId) {
this.experimentId = experimentId;
}
public String getExperimentName() {
return experimentName;
}
public void setExperimentName(String experimentName) {
this.experimentName = experimentName;
}
public String getExperimentDate() {
return experimentDate;
}
public void setExperimentDate(String experimentDate) {
this.experimentDate = experimentDate;
}
public void setTrialId(long trialId) {
this.trialId = trialId;
}
public long getTrialId() {
return trialId;
}
public String getTrialVariable() {
return trialVariable;
}
public void setTrialVariable(String trialVariable) {
this.trialVariable = trialVariable;
}
public String getTrialResult() {
return trialResult;
}
public void setTrialResult(String trialResult) {
this.trialResult = trialResult;
}
}
}
Note the TrialWithExperiment class, this defines a combination of a Trial and it's owning Experiment.
Note how column names are distinct e.g. no just id but experimentId and trialId differentiates them.
Note the last #Query getExperimentsWithTrials() this will return a list of Trials with their experiments.
The #Database ExperimentDatabase.java
#Database(entities = {Experiment.class, Trial.class}, version = 1,exportSchema = false)
public abstract class ExperimentDatabase extends RoomDatabase {
public abstract Dao getDao();
}
Finally an activity that utilises the above :-
public class MainActivity extends AppCompatActivity {
ExperimentDatabase mDB;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDB = Room.databaseBuilder(this,ExperimentDatabase.class,"experimentdb").allowMainThreadQueries().build();
mDB.getDao().insertExperiments(new Experiment[]{
new Experiment("Experiment 1","2019-01-01"),
new Experiment("Experiment 2", "2019-02-01")
});
List<Experiment> experiments = mDB.getDao().getExperimentsInDateRange("2019-01-01","2019-12-31");
for (Experiment e: experiments) {
Log.d("EXPERIMENTS", "Experiment is " + e.getExperimentName() + " on " + e.getExperimentDate());
}
experiments = mDB.getDao().getAllexperiments();
for (Experiment e: experiments) {
Log.d("EXPERIMENTS", "Experiment is " + e.getExperimentName() + " on " + e.getExperimentDate());
}
for (Experiment e: experiments) {
mDB.getDao().insertTrial(
new Trial(
e.getExperimentId(),
"Variable for " + e.getExperimentName(),
"Result for Experiment on " + e.getExperimentDate()
)
);
}
List<Trial> trials = mDB.getDao().getAllTrials();
for (Trial t: trials ) {
Log.d("TRIAL ",
"Trial is for ExperimentID " + String.valueOf(t.getParentExperiment()) +
"\n\tVariable = " + t.getTrialVariable() +
"Result = " + t.getTrialResult()
);
}
List<Dao.TrialWithExperiment> trialsWithExperiments = mDB.getDao().getExperimentsWithTrials();
for (Dao.TrialWithExperiment te:trialsWithExperiments) {
Log.d(
"TRIALWITHEXPERIMENT",
"Experiment Name = " + te.getExperimentName() +
"\n\tExperiment Date = " + te.getExperimentDate() +
"\n\t\tTrial Variable = " + te.getTrialVariable() +
"\n\t\tTrial Result = " + te.getTrialResult()
);
}
}
}
This :-
Adds 2 experiments (every run)
Retrieves Experiments in a date range (this assumes an SQLite recognised date format) and outputs them to the log.
Retrieves all Experiments and outputs them to the log.
Uses the List of experiments to add a Trial to each experiment.
Retrieves all Trials and outputs them to the log.
Retrieves all Trials along with the parent Experiment and outputs them to the log.
The Result
05-28 10:19:42.770 5750-5750/? D/EXPERIMENTS: Experiment is Experiment 1 on 2019-01-01
05-28 10:19:42.770 5750-5750/? D/EXPERIMENTS: Experiment is Experiment 2 on 2019-02-01
05-28 10:19:42.776 5750-5750/? D/EXPERIMENTS: Experiment is Experiment 1 on 2019-01-01
05-28 10:19:42.776 5750-5750/? D/EXPERIMENTS: Experiment is Experiment 2 on 2019-02-01
05-28 10:19:42.786 5750-5750/? D/TRIAL: Trial is for ExperimentID 1
Variable = Variable for Experiment 1Result = Result for Experiment on 2019-01-01
05-28 10:19:42.786 5750-5750/? D/TRIAL: Trial is for ExperimentID 2
Variable = Variable for Experiment 2Result = Result for Experiment on 2019-02-01
05-28 10:19:42.787 5750-5750/? D/TRIALWITHEXPERIMENT: Experiment Name = Experiment 1
Experiment Date = 2019-01-01
Trial Variable = Variable for Experiment 1
Trial Result = Result for Experiment on 2019-01-01
05-28 10:19:42.787 5750-5750/? D/TRIALWITHEXPERIMENT: Experiment Name = Experiment 2
Experiment Date = 2019-02-01
Trial Variable = Variable for Experiment 2
Trial Result = Result for Experiment on 2019-02-01
Alternative/Additional
Another approach could be to utilise #Relation to create an object per Experiment that includes a list of the related/associated Trial(s) in that object.
Expanding upon the above then the following could be added to Dao.java
#Query("SELECT * FROM Experiment")
List<ExperimentWithTrials> getExperimentsAndTrials();
class ExperimentWithTrials {
private long experimentId;
private String experimentName;
private String experimentDate;
#Relation(parentColumn = "experimentId", entityColumn = "parentExperiment")
List<Trial> trials;
public long getExperimentId() {
return experimentId;
}
public void setExperimentId(long experimentId) {
this.experimentId = experimentId;
}
public String getExperimentName() {
return experimentName;
}
public void setExperimentName(String experimentName) {
this.experimentName = experimentName;
}
public String getExperimentDate() {
return experimentDate;
}
public void setExperimentDate(String experimentDate) {
this.experimentDate = experimentDate;
}
public List<Trial> getTrials() {
return trials;
}
public void setTrials(List<Trial> trials) {
this.trials = trials;
}
}
and then the following could be added to MainActivity.java
List<Dao.ExperimentWithTrials> experimentsWithTrials = mDB.getDao().getExperimentsAndTrials();
for (Dao.ExperimentWithTrials et: experimentsWithTrials ) {
Log.d(
"EXPERIMENTANDTRIALS",
"Experiment Name = " + et.getExperimentName() +
"\n\tExperiment Date = " + et.getExperimentDate()
);
for (Trial t: et.getTrials()) {
Log.d(
"TRIALFOREXPERIMENT",
"\t\tVariable = " + t.getTrialVariable() +
"\n\t\tResult = " + t.getTrialResult()
);
}
}
}
Note how the Trials are obtained via the looping through the list of Trials embedded with the ExperimentWithTrials object, as opposed to the list of combined Experiment and Trial data from the previous.
This probably to purer OO way.
However, SQLite wise this appears to be cumbersome/inefficient as it appears to run multiple queries. One to get the Experiments and then another to get the underlying Trials for each experiment.
Resultant additional output
05-28 13:05:35.052 6524-6524/? D/EXPERIMENTANDTRIALS: Experiment Name = Experiment 1
Experiment Date = 2019-01-01
05-28 13:05:35.052 6524-6524/? D/TRIALFOREXPERIMENT: Variable = Variable for Experiment 1
Result = Result for Experiment on 2019-01-01
05-28 13:05:35.052 6524-6524/? D/EXPERIMENTANDTRIALS: Experiment Name = Experiment 2
Experiment Date = 2019-02-01
05-28 13:05:35.052 6524-6524/? D/TRIALFOREXPERIMENT: Variable = Variable for Experiment 2
Result = Result for Experiment on 2019-02-01
Note that for convenience all of the above has been run on the main thread (i.e .allowMainThreadQueries() has been used). If you follow recommendations the all database access would be via another thread, in which case the #Transaction annotation is advised for queries.
public class ChallengeDB extends RealmObject {
#PrimaryKey
private int challengeId;
private String targetType;
private RealmList<ChallengeTargetDB> ChallengeTargetDB;
private boolean isTargetPerParticipant;
}
public class ChallengeTargetDB extends RealmObject {
#PrimaryKey
private int targetId;
private String targetName;
private String description;
private long targetValue;
private int targetStep;
private boolean isPassed;
}
I have these 2 tables linked as following and I need to get max "targetValue" value of a specific challengeId.
The query for which I have written is as follows
RealmResults<ChallengeDB> challengeDBs = realm.where(ChallengeDB.class)
.equalTo(WorkoutCashConstants.COLUMN_CHALLENGE_ID, challengeDB.getChallengeId()).findAll();
long max = challengeDBs.max("ChallengeTargetDB.targetValue").longValue();
I get up ending exception as follows
java.lang.IllegalArgumentException: Aggregates on child object fields are not supported: ChallengeTargetDB.targetValue
With Realm 3.5.0+
public class ChallengeTargetDB extends RealmObject {
#PrimaryKey
private int targetId;
private String targetName;
private String description;
private long targetValue;
private int targetStep;
private boolean isPassed;
#LinkingObjects("ChallengeTargetDb")
private final RealmResults<ChallengeDb> targetOfChallenge = null;
}
Then
RealmResults<ChallengeTargetDB> challengeDBs = realm.where(ChallengeTargetDB.class)
.equalTo("targetOfChallenge." + WorkoutCashConstants.COLUMN_CHALLENGE_ID, challengeDB.getChallengeId()).findAll();
long max = challengeDBs.max("targetValue").longValue();
As the exception says, aggregates on linked object's field are not supported. You need to compute it by yourself.
Try this:
Number max = null;
for (ChallengeDB item : challengeDBs) {
if (max == null) {
max = item.ChallengeTargetDB.max();
} else {
Number tmp = item.ChallengeTargetDB.max();
max = max.longValue() > tmp.longValue() ? max : tmp;
}
}
In an android project i have a class with these fields:
public class TransactionHistoryDetail1 implements Parcelable, DatabaseEnabled{
private long id;
private static final String TABLE_NAME = "TransactionHistoryDetail";
private static final String EMPTY_STRING = "";
#XmlElement(name = "TxUd", required = true)
private String TxUd;
#XmlElement(name = "TxLclDtTm", required = true)
private Date TxLclDtTm;
#XmlElement(name = "CcyCd")
private CurrencyCode CcyCd;
#XmlElement(name = "Amt")
private BigDecimal Amt;
#XmlElement(name = "PmttpCd", required = true)
private PaymentTypeCode PmttpCd;
#XmlElement(name = "OprtnCd", required = true)
private OperationCode OprtnCd;
#XmlElement(name = "AppLabltpCd", required = true)
private AppLabelTypeCode AppLabltpCd;
#XmlElement(name = "PdSrl")
private String PdSrl;
#XmlElement(name = "PdBrndDsc")
private String PdBrndDsc;
#XmlElement(name = "UsrEml")
private String UsrEml;
#XmlElement(name = "CstmrAdr")
private String CstmrAdr;
#XmlElement(name = "TxDtlDsc")
private String TxDtlDsc;
#XmlElement(name = "TxRltdInd")
private boolean TxRltdInd;
#XmlElement(name = "TxSttsCd", required = true)
private TransactionStatusCode TxSttsCd;
#XmlElement(name = "UpdtDt", required = true)
private Date UpdtDt;
...
}
Im trying to Write and Read objects of this class as Parcelables but im not sure how to write and read the enums.
My writeToParcel method looks like this:
#Override
public void writeToParcel(Parcel dest, int flags) {
SimpleDateFormat sdf = new SimpleDateFormat(JsonBuilder.DateFormat);
dest.writeLong(id);
dest.writeDouble(Amt.doubleValue());
dest.writeString(PdSrl);
dest.writeString(PdBrndDsc);
dest.writeString(TxUd);
dest.writeString(PdSrl);
dest.writeString(UsrEml);
dest.writeString(CstmrAdr);
dest.writeString(TxDtlDsc);
dest.writeString((CcyCd == null) ? "" : CcyCd.name());
dest.writeString((PmttpCd == null) ? "" : PmttpCd.name());
dest.writeString((OprtnCd == null) ? "" : OprtnCd.name());
dest.writeString((AppLabltpCd == null) ? "" : AppLabltpCd.name());
dest.writeString(sdf.format(TxLclDtTm));
dest.writeString(sdf.format(UpdtDt));
dest.writeByte((byte) (TxRltdInd ? 1 : 0));
}
and my Constructor with Parcel looks like this
private TransactionHistoryDetail1(Parcel in) {
SimpleDateFormat sdf = new SimpleDateFormat(JsonBuilder.DateFormat);
try {
TxLclDtTm = sdf.parse(in.readString());
UpdtDt = sdf.parse(in.readString());
} catch (ParseException e) {
e.printStackTrace();
}
id = in.readLong();
TxUd = in.readString();
PdSrl = in.readString();
PdBrndDsc = in.readString();
UsrEml = in.readString();
CstmrAdr = in.readString();
TxDtlDsc = in.readString();
TxRltdInd = in.readByte() != 0;
Amt = new BigDecimal(in.readDouble());
CcyCd = CurrencyCode.valueOf(in.readString());
PmttpCd = PaymentTypeCode.valueOf(in.readString());
OprtnCd = OperationCode.valueOf(in.readString());
AppLabltpCd = AppLabelTypeCode.valueOf(in.readString());
TxSttsCd = TransactionStatusCode.fromValue(in.readString());
}
The writeToParcel i belive its working well, but the constructor is crashing at the "CcyCd" line.
My CurrencyCode class is a enum, (so are PaymentTypeCode,OperationCode and AppLabelTypeCode) that looks like this:
#XmlType(name = "CurrencyCode")
#XmlEnum
public enum CurrencyCode {
EUR;
public String value() {
return name();
}
public static CurrencyCode fromValue(String v) {
return valueOf(v);
}
}
Is there another way to deal with enums in Parcelables?
The exception i get is this:
java.lang.RuntimeException: Unable to start activity ComponentInfo{package/package.activities.ChildActivity}: java.lang.IllegalArgumentException: No enum constant package.data.apiClasses.CurrencyCode.��EUR����CASH������CLS����APP����2017-04-10T09:07:52.525Z������2017-04-10T09:07:52.528Z����������CHILD_12345������?
First of all you should read from the Parcel in the exact order that you wrote to it. Since Parcel just write and read data in order instead of actually serializing data you have to keep read and write in order else you gonna read wrong values and getting error..
It is the best if you treat Enum as int, write them to parcel like this:
dest.writeInt(this.CcyCd == null ? -1 : this.CurrencyCode.ordinal());
and read them like this:
int tmpCurrencyCode = in.readInt();
this.CcyCd = tmpCurrencyCode == -1 ? null : CurrencyCode.values()[tmpMState];
P.S: This code check for null values too ^^
I had problem parsing Nullable Enum. If this is your case, look up here
https://stackoverflow.com/a/58337932/5980046
I have a problem with Realm that ill my brain :(
The problem is when I try to add a new object into RealmList. The object is inserted but it is not linked with the relationship.
Now, my database has multiple relationships:
User 1-->M Trip 1-->M Vehicle 1-->M
And then:
Vehicle 1-->M VehicleInfo
VehicleInfo 1-->M Temperature
The problem reside when I try to insert a new object into Temperature class.
My classes:
Vehicle:
#RealmClass
public class Vehicle extends RealmObject
{
private String state; //R: Reservado | P: Listo para embarcar | E: Embarcado | P1: Pdte. confirmar medidas | P2: Tª no válida | R1: No embarca
private String locata = "";
private String customer = "";
private String customerCode = "";
private String originPort = ""; //Origin port
private String destinyPort = ""; //Destiny port
private int fp; //Method pay (0 = CASH | 1 = CREDIT)
//Relationship
private Trip trip; //Inverse
private RealmList<VehicleInfo> vehicleInfo = new RealmList<>(); //One-to-many
private RealmList<Observation> observations = new RealmList<>();; //One-to-many
.....
}
VehicleInfo:
#RealmClass
public class VehicleInfo extends RealmObject {
private String sv; //Vehicle type
private String licensePlate = ""; //License
private String seal = ""; //Seal
private String temperature = ""; //Temperature control
private String iv = ""; //Ida/Vuelta
private String commodityCode = "";
private int tara = 0; //TARA
private int packages = 0; //Bultos
private int weight = 0;
private double length = 0.0; //Meters
private boolean flagFT; //Flag Technical data
private boolean flagDua;
private boolean flagManifest;
private boolean flagTransport;
private boolean flagDangerCommodity;
//Relationship
private RealmList<Temperature> temperatures = new RealmList<>(); //One-to-many
....
}
My code to add new Temperature:
Temperature temp = new Temperature();
temp.setDate(appCommon.getCurrentTimeOrDate("DATE"));
temp.setTime(appCommon.getCurrentTimeOrDate("TIME"));
temp.setValue(Double.parseDouble(etTemp.getText().toString()));
VehicleInfoPersistence.updateVehicleInfoTemperature(realm, vehicle.getLocata(), selectedUnitPosition, temp);
updateRecycler(tempRecyclerAdapter, temp);
Method to find and persist in Realm:
public static VehicleInfo findVehicleInfoFromLocata(Realm realm, String locata, int position) {
RealmQuery<Vehicle> query = realm.where(Vehicle.class).equalTo("locata", locata);
Vehicle realmVehicle = query.findFirst();
return realmVehicle.getVehicleInfo().get(position);
}
public static void updateVehicleInfoTemperature(Realm realm, String locata, int position, Temperature temperature) {
Vehicle vehicle = VehiclePersistence.findVehicleFromLocata(realm, locata);
realm.beginTransaction();
Temperature realmTemp = realm.copyToRealm(temperature);
vehicle.getVehicleInfo().get(position).getTemperatures().add(realmTemp);
realm.commitTransaction();
}
How I said, object is created in database but it is not linked with the vehicle-->vehicleInfo-->Temperature.
What's wrong in my code??
Thanks in advance :)
I resolved my problem :)
By some motive, realm fails when I try to add new object into sub-array of a RealmClass.
To solve this issue I've created an intermediate object and then I've added the object to this intermediate object.
public static void updateVehicleInfoTemperature(Realm realm, String locata, int position, Temperature temperature) {
Vehicle vehicle = VehiclePersistence.findVehicleFromLocata(realm, locata);
realm.beginTransaction();
VehicleInfo vInfo = vehicle.getVehicleInfo().get(position);
VehicleInfo realmVehicleInfo = realm.copyToRealm(vInfo);
Temperature realmTemp = realm.copyToRealm(temperature);
realmVehicleInfo.getTemperatures().add(realmTemp);
realm.commitTransaction();
}
I hope to help someone :)