Room TypeConverter not working - java

I have an issue with Room not recognizing my converter. Error:
Cannot figure out how to save this field into database. You can consider adding a type converter for it.
I need to store some maps and sets in my database. What am I doing wrong? Does room not like interfaces or generics?
code: (sorry for all the fields and class name, they are a mixture of English and Czech to not have same names as some java classes):
Converter (only part)
public class MyConverter {
/**
* makes a string like 1;2;3;5;4;8;1;6;8;4 from a collection of integers.
*/
#TypeConverter
public static #NonNull String toString(#NonNull Collection<Integer> c) {
StringBuilder sb = new StringBuilder();
for (Integer item : c) {
sb.append(item.toString() + ";");
}
sb.delete(sb.length()-1,sb.length()-1);
return sb.toString();
}
/**
* makes a Set<Integer> from string like 1;2;3;4;5;6
* #throws NumberFormatException on incorrect input
*/
#TypeConverter
public static#NonNull Set<Integer> toIntegerSet(#NonNull String s) {
Set<Integer> set = new LinkedHashSet<>();
String[] split = s.split(";");
try {
for (String item : split) {
set.add(Integer.parseInt(item));
}
}catch (NumberFormatException e){
throw new NumberFormatException("Could not make set of integers (like 1;2;3;8;7) from \"" + s +"\"");
}
return set;
}
}
Database:
#Database(entities = {SQLUkol.class,SQLPredmet.class,SQLList.class},version = 1)
#TypeConverters({MyConverter.class})
public abstract class AppDatabase extends RoomDatabase {
public abstract MojeDAO mojeDao();
}
One of the entities (getters, setters and constructors not included):
#Entity(primaryKeys = {"id", "list_id"},
indices = {#Index("list_id")},
foreignKeys = #ForeignKey(entity = SQLList.class, parentColumns = "id",
childColumns = "list_id", onDelete = ForeignKey.CASCADE),
tableName = "ukols")
public class SQLUkol implements Comparable<SQLUkol> {
#ColumnInfo(name = "list_id")
private final int listID;
private final int id;
private String title;
#ColumnInfo(name = "title_changed")
private boolean titleChanged = false;
private String notes;
#ColumnInfo(name = "notes_changed")
private boolean notesChanged = false;
private boolean completed;
#ColumnInfo(name = "completed_changed")
private boolean completedChanged = false;
private LocalDate date;
#ColumnInfo(name = "date_changed")
private boolean dateChanged = false;
#Embedded
private final SQLData data;
}

Room does not like generics much. I had to do this:
#TypeConverter
public static String toString1(Map<String, String> m){
...
}
#TypeConverter
public static String toString2(Map<Integer, String> m){
...
}
#TypeConverter
public static String toString3(Set<Integer> s){
...
}
#TypeConverter
public static String toString4(List<Integer> l){
...
}
not just
#TypeConverter
public static String toString(Map m){
...
}
#TypeConverter
public static String toString(Collection<Integer> c){
...
}

Related

Android: how to insert data in manyToMany relationship in Room (Java)

Guys I'm trying to create a database with a manyToMany relationship, I'm able to create the 2 tables of elements but I'm not able to populate the joining table. I don't know how should I insert datas.
This is Card.class:
#Entity
public class Card {
#PrimaryKey(autoGenerate = true)
private Long idCard;
#ColumnInfo(name = "title")
private String title;
#ColumnInfo(name = "taboo_word_1")
private String tabooWord1;
#ColumnInfo(name = "taboo_word_2")
private String tabooWord2;
#ColumnInfo(name = "taboo_word_3")
private String tabooWord3;
#ColumnInfo(name = "taboo_word_4")
private String tabooWord4;
#ColumnInfo(name = "taboo_word_5")
private String tabooWord5;
public Long getIdCard() {
return idCard;
}
public void setIdCard(Long idCard) {
this.idCard = idCard;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getTabooWord1() {
return tabooWord1;
}
public void setTabooWord1(String tabooWord1) {
this.tabooWord1 = tabooWord1;
}
public String getTabooWord2() {
return tabooWord2;
}
public void setTabooWord2(String tabooWord2) {
this.tabooWord2 = tabooWord2;
}
public String getTabooWord3() {
return tabooWord3;
}
public void setTabooWord3(String tabooWord3) {
this.tabooWord3 = tabooWord3;
}
public String getTabooWord4() {
return tabooWord4;
}
public void setTabooWord4(String tabooWord4) {
this.tabooWord4 = tabooWord4;
}
public String getTabooWord5() {
return tabooWord5;
}
public void setTabooWord5(String tabooWord5) {
this.tabooWord5 = tabooWord5;
}
}
Tag:
#Entity
public class Tag {
#PrimaryKey(autoGenerate = true)
private long idTag;
#ColumnInfo(name = "tag")
private String tag;
public Tag(String tag) {
this.tag = tag;
}
public long getIdTag() {
return idTag;
}
public void setIdTag(long idTag) {
this.idTag = idTag;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
#Override
public String toString() {
return getTag();
}
}
This is DatabaseTaboom.class:
#Database(entities = {Card.class, Tag.class, CardTagCrossRef.class},
version = 1)
public abstract class DatabaseTaboom extends RoomDatabase {
public static final String DATABASE_NAME = "db_taboom-1";
public abstract CardDAO cardDao();
public static DatabaseTaboom db;
// Singleton pattern
public static DatabaseTaboom getDatabase(Context applicationContext) {
if (db == null) {
db = Room.databaseBuilder(applicationContext, DatabaseTaboom.class, DATABASE_NAME)
//.allowMainThreadQueries()
.build();
}
return db;
}
}
This is CardDAO.class:
#Dao
public interface CardDAO {
#Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertCard(Card card);
#Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertTag(Tag tag);
#Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertCardWithTags(CardTagCrossRef cardTagCrossRef);
// If called on an item not present in the DB it won't do anything
#Update
public void updateCard(Card card);
#Delete
public void deleteCard(Card card);
// With a query method you can also perform complex inserts/updates/deletes
// Transaction needed for relational classes
#Transaction
#Query("SELECT * FROM Card")
LiveData<List<CardWithTags>> getAllCards();
}
This is CardTagCrossRef.class:
#Entity(primaryKeys = {"idCard", "idTag"})
public class CardTagCrossRef {
public long idCard;
public long idTag;
}
CardWithTags:
public class CardWithTags {
#Embedded private Card card;
#Relation(
parentColumn = "idCard",
entityColumn = "idTag",
associateBy = #Junction(CardTagCrossRef.class)
)
private List<Tag> tagList;
public CardWithTags() {
}
public CardWithTags(Card card, List<Tag> tagList) {
this.card = card;
this.tagList = tagList;
}
public Card getCard() {
return card;
}
public void setCard(Card card) {
this.card = card;
}
public List<Tag> getTagList() {
return tagList;
}
public void setTagList(List<Tag> tagList) {
this.tagList = tagList;
}
#Override
public String toString() {
String s = getCard().toString();
s += ", TAG[";
for (Tag t: getTagList()) {
s += t + "";
}
s+="]";
return s;
}
}
And this is the method that I wrote to insert a card:
public void insertCard(CardWithTags card) {
Log.d(TAG, ">>insertCard(): " + card);
executor.execute(() -> {
cardDAO.insertCard(card.getCard());
for (Tag t: card.getTagList()) {
cardDAO.insertTag(t);
CardTagCrossRef cardTagCrossRef = new CardTagCrossRef();
cardTagCrossRef.idCard = card.getCard().getIdCard();
cardTagCrossRef.idTag = t.getIdTag();
Log.d(TAG, "CardCrossRef:" + cardTagCrossRef.idCard + cardTagCrossRef.idTag);
cardDAO.insertCardWithTags(cardTagCrossRef);
}
// Check if tags already exists
cardListIsUpdatedWithDb = false;
});
}
First you should amend the Dao's so that they return the id of the inserted row enabling you to ascertain the actual id of the inserted rows. So :-
#Insert(onConflict = OnConflictStrategy.REPLACE)
public long insertCard(Card card);
#Insert(onConflict = OnConflictStrategy.REPLACE)
public long insertTag(Tag tag);
#Insert(onConflict = OnConflictStrategy.REPLACE)
public long insertCardWithTags(CardTagCrossRef cardTagCrossRef);
This allows you to retrieve the respective id when you insert either a Card or a Tag (note that for a CardTagCrossRef insertion this will be the rowid, a normally hidden row).
So you could then have use long cardId = cardDAO.insertCard(card.getCard()); and not then need to attempt to use cardTagCrossRef.idCard = card.getCard().getIdCard(); where the card DOES NOT have the id of the inserted card (part of the issue you are facing).
And likewise for the Tag.
So you could use :-
long cardId = cardDAO.insertCard(card.getCard());
for (Tag t: card.getTagList()) {
long tagId = cardDAO.insertTag(t);
CardTagCrossRef cardTagCrossRef = new CardTagCrossRef();
cardTagCrossRef.idCard = cardId;
cardTagCrossRef.idTag = tagId;
Log.d(TAG, "CardCrossRef:" + cardTagCrossRef.idCard + cardTagCrossRef.idTag);
cardDAO.insertCardWithTags(cardTagCrossRef);
}
However, with a few changes I believe that that can make things far more flexible and have an insert that effectively does what you want within the Dao's.
SO perhaps consider the following that culminates in a working DEMO
Card
#Entity
public class Card {
#PrimaryKey/*(autoGenerate = true) SUGGESTED suppression of autogenerate as will still autogenerate but more efficiently */
private Long idCard;
#ColumnInfo(name = "title")
private String title;
#ColumnInfo(name = "taboo_word_1")
private String tabooWord1;
#ColumnInfo(name = "taboo_word_2")
private String tabooWord2;
#ColumnInfo(name = "taboo_word_3")
private String tabooWord3;
#ColumnInfo(name = "taboo_word_4")
private String tabooWord4;
#ColumnInfo(name = "taboo_word_5")
private String tabooWord5;
/* Constructors added */
public Card(){}
#Ignore
public Card(Long idCard,String title, String tabooWord1, String tabooWord2, String tabooWord3, String tabooWord4, String tabooWord5) {
this.idCard = idCard;
this.title = title;
this.tabooWord1 = tabooWord1;
this.tabooWord2 = tabooWord2;
this.tabooWord3 = tabooWord3;
this.tabooWord4 = tabooWord4;
this.tabooWord5 = tabooWord5;
}
public Long getIdCard() {
return idCard;
}
public void setIdCard(Long idCard) {
this.idCard = idCard;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getTabooWord1() {
return tabooWord1;
}
public void setTabooWord1(String tabooWord1) {
this.tabooWord1 = tabooWord1;
}
public String getTabooWord2() {
return tabooWord2;
}
public void setTabooWord2(String tabooWord2) {
this.tabooWord2 = tabooWord2;
}
public String getTabooWord3() {
return tabooWord3;
}
public void setTabooWord3(String tabooWord3) {
this.tabooWord3 = tabooWord3;
}
public String getTabooWord4() {
return tabooWord4;
}
public void setTabooWord4(String tabooWord4) {
this.tabooWord4 = tabooWord4;
}
public String getTabooWord5() {
return tabooWord5;
}
public void setTabooWord5(String tabooWord5) {
this.tabooWord5 = tabooWord5;
}
}
2 changes an more flexible constructor and not using autogenerate = true (but that does automatically generate id's BUT without the overheads of the SQLite AUTOINCREMENT which is what autogenerate = true adds).
Tag (similar changes)
#Entity
public class Tag {
#PrimaryKey/*(autoGenerate = true) SUGGESTED suppression of autogenerate*/
private Long idTag;
#ColumnInfo(name = "tag")
private String tag;
public Tag(){}
#Ignore
public Tag(Long idTag, String tag) {
this.idTag = idTag;
this.tag = tag;
}
#Ignore
public Tag(String tag) {
this.tag = tag;
}
public Long getIdTag() {
return idTag;
}
public void setIdTag(Long idTag) {
this.idTag = idTag;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
#Override
public String toString() {
return getTag();
}
}
CardTagCrossRef (added ForeignKey constraints to enforce/manage referential integrity)
#Entity(
primaryKeys = {"idCard", "idTag"}
/* SUGGESTED */
, foreignKeys = {
#ForeignKey(
entity = Card.class,
parentColumns = "idCard",
childColumns = "idCard",
/* SUGGESTED with ForeignKey */
onDelete = CASCADE,
onUpdate = CASCADE
),
#ForeignKey(
entity = Tag.class,
parentColumns = "idTag",
childColumns = "idTag",
/* SUGGESTED with ForeignKey */
onDelete = CASCADE,
onUpdate = CASCADE
)
}
)
public class CardTagCrossRef {
public long idCard;
#ColumnInfo(index = true) /* SUGGESTED */
public long idTag;
public CardTagCrossRef(){}
#Ignore
public CardTagCrossRef(long idCard, long idTag) {
this.idCard = idCard;
this.idTag = idTag;
}
}
CardWithTags
identical other than #Ignore annotation on the CardWithTags(Card card, List<Tag> tagList) constructor to supress warnings about multiple good consctructors.
i.e.
....
#Ignore /*<<<<< SUGGESTED */
public CardWithTags(Card card, List<Tag> tagList) {
this.card = card;
this.tagList = tagList;
}
....
CardDAO (new INSERT + return values)
#Dao
abstract class CardDAO {
/* public interface CardDAO { CHANGED TO abstract class to allow functions with bodies */
#Insert(onConflict = OnConflictStrategy.REPLACE)
abstract long insertCard(Card card); /* Returns long (inserted row id) */
#Insert(onConflict = OnConflictStrategy.REPLACE)
abstract long insertTag(Tag tag); /* Returns long (inserted row id) */
#Insert(onConflict = OnConflictStrategy.REPLACE)
abstract long insertCardWithTags(CardTagCrossRef cardTagCrossRef); /* Returns long (inserted row id) */
/* NEW INSERT */
#Query("")
#Transaction
long[] insert(Card card, List<Tag> tags) {
long[] rv = new long[tags.size() + 1];
int ix = 0;
rv[ix++] = insertCard(card);
if (rv[ix-1] > -1) {
for (Tag t : tags) {
rv[ix++] = insertTag(t);
if (rv[ix-1] > -1) {
insertCardWithTags(new CardTagCrossRef(rv[0],rv[ix-1]));
}
}
}
return rv;
}
// If called on an item not present in the DB it won't do anything
#Update
abstract int updateCard(Card card); /* returns number of updated rows */
#Delete
abstract int deleteCard(Card card); /* returns number of deleted rows */
// With a query method you can also perform complex inserts/updates/deletes
// Transaction needed for relational classes
#Transaction
#Query("SELECT * FROM Card")
/* abstract LiveData<List<CardWithTags>> getAllCards(); COMMENTED OUT to allow demo to run on main thread */
abstract List<CardWithTags> getAllCards(); /* same but not with LiveData */
}
DatabaseTaboom (allow main thread + exportSchema = false to suppress warning)
#Database(entities = {Card.class, Tag.class, CardTagCrossRef.class},
version = 1/* SUGGESTED */ , exportSchema = false)
public abstract class DatabaseTaboom extends RoomDatabase {
public static final String DATABASE_NAME = "db_taboom-1";
abstract CardDAO cardDao();
public static DatabaseTaboom db;
// Singleton pattern
public static DatabaseTaboom getDatabase(Context applicationContext) {
if (db == null) {
db = Room.databaseBuilder(applicationContext, DatabaseTaboom.class, DATABASE_NAME)
.allowMainThreadQueries() /* uncommented for testing */
.build();
}
return db;
}
}
Finally the DEMO MainActivity with some examples of inserting Cards, Tags and CardTagCrossRef's followed by extracting them all outputting the results to the log.
public class MainActivity extends AppCompatActivity {
DatabaseTaboom db;
CardDAO dao;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = DatabaseTaboom.getDatabase(this);
dao = db.cardDao();
/* Simple but long winded */
long c1id = dao.insertCard(new Card(null,"Card1","tw1","tw2","tw3","tw4","tw5"));
long t1id = dao.insertTag(new Tag(null,"TAG1"));
CardTagCrossRef ctcr1 = new CardTagCrossRef();
ctcr1.idCard = c1id;
ctcr1.idTag = t1id ;
dao.insertCardWithTags(ctcr1);
/* Using additional constructor for CardTagCrossRef */
long t2id = dao.insertTag(new Tag("TAG2"));
dao.insertCardWithTags(new CardTagCrossRef(c1id,t2id));
/* More dynamic BUT don't know the actual inserted id's of the Card and Tag */
dao.insertCardWithTags(
new CardTagCrossRef(dao.insertCard(new Card(100l,"Card2","c2tw1","c2tw2","c2tw3","c2tw4","c2tw5")),dao.insertTag(new Tag(null,"TAG3"))));
CardWithTags cwt = new CardWithTags(
new Card(null,"CARD3","c3tw1","c3tw2","c3tw3","c3tw4","c3tw5"),
Arrays.asList(
new Tag(null,"TAG4"), new Tag("TAG5"), new Tag("TAG6")
)
);
/* Amended insert function */
insertCard(cwt,dao);
/* Using new insert funciotn */
dao.insert(
new Card(1000l,"CARD4","c4tw1","c4tw2","c4tw3","c4tw4","c4tw5"),
Arrays.asList(
new Tag(null,"TAG7"), new Tag(500l,"TAG8"),new Tag(null,"TAG9")
)
);
/* Extract the results and output to the log */
for(CardWithTags cwtlist: dao.getAllCards()) {
Log.d("CWTINFO","Card is " + cwtlist.getCard().getTitle() + " TabooWord1 is " + cwtlist.getCard().getTabooWord1() + " it has " + cwtlist.getTagList().size() + " tags. They are:-");
for(Tag t: cwtlist.getTagList()) {
Log.d("CWTINFO_TAG","\tTAG is " + t.getTag());
}
}
}
public void insertCard(CardWithTags card, CardDAO cardDAO) {
final String TAG = "INSERTCARDINFO";
Log.d(TAG, ">>insertCard(): " + card);
/*
executor.execute(() -> {
*/
long currentCardId = cardDAO.insertCard(card.getCard());
for (Tag t: card.getTagList()) {
long currentTagId = cardDAO.insertTag(t);
CardTagCrossRef cardTagCrossRef = new CardTagCrossRef();
cardDAO.insertCardWithTags(new CardTagCrossRef(currentCardId,currentTagId));
/*
cardTagCrossRef.idCard = card.getCard().getIdCard();
cardTagCrossRef.idTag = t.getIdTag();
*/
/*
OR with new Contsructor
CardTagCrossRef ctcr = new CardTagCrossRef(currentCardId,currentTagId);
*/
/* AND THEN cardDAO.insertCardWithTags(cardTagCrossRef); */
Log.d(TAG, "CardCrossRef:" + cardTagCrossRef.idCard + cardTagCrossRef.idTag);
}
/*
// Check if tags already exists
cardListIsUpdatedWithDb = false;
*/
/*})*/;
}
}
When run (after new install as only designed to run the once) the Log includes:-
2022-02-04 13:29:10.569D/INSERTCARDINFO: >>insertCard(): a.a.so70979022javaroom.Card#d751e5e, TAG[TAG4TAG5TAG6]
2022-02-04 13:29:10.573D/INSERTCARDINFO: CardCrossRef:00
2022-02-04 13:29:10.578I/chatty: uid=10194(a.a.so70979022javaroom) identical 1 line
2022-02-04 13:29:10.581D/INSERTCARDINFO: CardCrossRef:00
2022-02-04 13:29:10.600D/CWTINFO: Card is Card1 TabooWord1 is tw1 it has 2 tags. They are:-
2022-02-04 13:29:10.600D/CWTINFO_TAG: TAG is TAG1
2022-02-04 13:29:10.601D/CWTINFO_TAG: TAG is TAG2
2022-02-04 13:29:10.601D/CWTINFO: Card is Card2 TabooWord1 is c2tw1 it has 1 tags. They are:-
2022-02-04 13:29:10.601D/CWTINFO_TAG: TAG is TAG3
2022-02-04 13:29:10.601D/CWTINFO: Card is CARD3 TabooWord1 is c3tw1 it has 3 tags. They are:-
2022-02-04 13:29:10.601D/CWTINFO_TAG: TAG is TAG4
2022-02-04 13:29:10.601D/CWTINFO_TAG: TAG is TAG5
2022-02-04 13:29:10.601D/CWTINFO_TAG: TAG is TAG6
2022-02-04 13:29:10.601D/CWTINFO: Card is CARD4 TabooWord1 is c4tw1 it has 3 tags. They are:-
2022-02-04 13:29:10.601D/CWTINFO_TAG: TAG is TAG7
2022-02-04 13:29:10.601D/CWTINFO_TAG: TAG is TAG8
2022-02-04 13:29:10.601D/CWTINFO_TAG: TAG is TAG9
Via App Inspection then :-
and :-
and :-

Generic method to take different class objects

I have 3 class(techspecs, packages, and features) objects where they all share the same fields. The fields are big and instead of repeating setting the fields of each field 3 times(which ends up looking like duplicates), I would like to pass the class objects into one method that uses the generic object to setting the object fields.
I tried passing the class object as a generic but then i dont have access to its members. This is what i tried
Packages packagesFeatures = new Packages();
TechSpecs techSpecsFeature = new TechSpecs();
packagesFeatures = addFeatures(Packages.class, packagesFeatures, vehFeatures);
techSpecsFeature = addFeatures(TechSpecs.class, techSpecsFeature, vehFeatures);
Then
private <T> T addFeatures(Class<T> clazz, T obj, VehicleFeature vehFeatures) {
T inst = null;
try {
inst = clazz.getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
if (inst instanceof Packages) {
obj = (T) new Packages();
}
if(inst instanceof TechSpecs){
obj = (T) new TechSpecs();
}
if(inst instanceof Features){
obj = (T) new Features();
}
//then somthing like:
//obj.setFeatureId(vehFeatures.getFeatureId());
// obj.setFeatureKey(vehFeatures.getFeatureKey());
// obj.setFeatureCode(vehFeatures.getFeatureCode());
return obj;
EDIT
Each of the 3 classes extend BaseFeatures
public abstract class BaseFeatures {
private String featureId;
private String featureKey;
private String featureCode;
private String subSectionId;
private String subSectionName;
private String featureIdName;
private Integer subSectionRank;
private Integer featureImgClassificationId;
private String featureImgClassification;
private boolean has3DAnimation;
private String sectionId;
private String searchKeys;
private String description;
private String featureName;
private double featureRank;
private String geoId;
private String ecc;
private String specSegments;
private String featureIconType;
private String featureIconText;
private double featureValue;
private boolean standardCertain;
private boolean built;
private List<String> featureKeyAnswers;
private boolean isNumeric;
private boolean adasFeature;
private List<String> icCodeAnswers;
private String featureKeyNoBrand;
private List<StyleInfo> styles;
private List<String> optionCodes;
private List<String> changeOptions;
//getters and setters.
Here is one of the classes.
public class TechSpecs extends BaseFeatures {
private String techSpecs;
public void setTechSpecs(String techSpecs) {
this.techSpecs = techSpecs;
}
public String getTechSpecs(){
return techSpecs;
}
}
All of these fields need to be set in the class object of all 3 classes
EDIT 2
VehicleFeature Class is a standalone class
#JsonInclude(JsonInclude.Include.NON_NULL)
public class VehicleFeature {
private String section;
private String subSection;
private String featureName;
private String subSectionId;
private String sectionName;
private String subSectionName;
If it were me, I would simplify your addFeatures(...) method to something like:
private <T> T addFeatures(Class<T> clazz, BaseFeatures theseFeatures) {
T obj = null;
try {
obj = clazz.getDeclaredConstructor(BaseFeatures.class).newInstance(theseFeatures);
} catch (ReflectiveOperationException roe) {
roe.printStackTrace();
}
return obj;
}
I'd add these two constructors to BaseFeatures:
public abstract class BaseFeatures{
protected String featureId;
protected String featureKey;
protected String featureCode;
/*...*/
protected BaseFeatures(String featureId, String featureKey, String featureCode){
this.featureId = featureId;
this.featureKey = featureKey;
this.featureCode = featureCode;
}
protected BaseFeatures(BaseFeatures features){
this.featureId = features.featureId ;
this.featureKey = features.featureKey;
this.featureCode = features.featureCode;
}
/*...*/
}
You can see how that implementation would actually work, here:
public class BigAssFields {
/* ... */
static public void main(String ... args){
BigAssFields bLike = new BigAssFields();
VehicleFeature vehFeatures = new VehicleFeature("what", "the actual", "Feature");
TechSpecs bigTechSpecs = bLike.addFeatures(TechSpecs.class, vehFeatures);
}
/* ... */
}

#TypeConverter from List<SomeObject> to String in Room Android gson.toJson(List<SomeObject>)

Error:
java.lang.IllegalArgumentException: class android.widget.SeekBar declares multiple JSON fields named mMinHeight
My Typeconverter
public class ObjectConverter {
private static Gson gson= new Gson();
#TypeConverter
public static List<DynamicItem> fromString(String s){
if (s == null) {
return Collections.emptyList();
}
Type listType = new TypeToken<List<DynamicItem>>() {}.getType();
return gson.fromJson(s, listType);
}
#TypeConverter
public static String fromObject(List<DynamicItem> someObjects) {
//Error in the following line
return gson.toJson(someObjects,new TypeToken<List<DynamicItem>>() {}.getType());
}
}
First Entity:
#Entity(foreignKeys = #ForeignKey(entity = AllTypes.class,
parentColumns = "rid",
childColumns = "busId",
onDelete = ForeignKey.CASCADE))
public class BusItem {
#PrimaryKey(autoGenerate = true)
#ColumnInfo
private int busId;
#ColumnInfo
private List<DynamicItem> proxyObj;
//Getters & Setters
}
Second Entity
#Entity
public class AllTypes implements Serializable {
#NonNull
#PrimaryKey(autoGenerate = true)
#ColumnInfo()
private int rid;
#ColumnInfo
private String type;
#ColumnInfo
private String timeStamp;
#Embedded
private BusItem busItem;
#Embedded
private Simple simple;
//Getters & Setters
}
Adding Data
#Override
protected Void doInBackground(Void... voids) {
Calendar calendar = Calendar.getInstance();
String time = new SimpleDateFormat("HH:mm:ss").format(calendar.getTime());
String date = new SimpleDateFormat("dd-MM-yyyy").format(calendar.getTime());
busItem.setProxyObj(busItemList);
allTypes.setType("BUS_N");
allTypes.setTimeStamp(date + time);
allTypes.setBusItem(busItem);
allTypes.setTag(stag);
//adding to database
DBClient.getInstance(getApplicationContext()).getNotesDB()
.notesDao()
.insertAllTypes(allTypes);
return null;
}
DynamicItem
public class DynamicItem {
CustomEditText customEditText;
ImageView imageUri;
AudioItem audioItem;
String header;
String htmlEditText;
//Getters and Setters
}
I am trying to save a list of dynamicItems in the database. The Relation is one-one relation between (AllTypes & BusItem) (AllTypes & Simple).
Image For Schema Understanding

How to populate a ChoiceBox with items from a database

I'm trying to populate a ChoiceBox with items(Countries) from a H2 database via Hibernate, but the ChoiceBox only gets populated with some strange items that don't make sense, instead of the actual countries names; something like this:
project.Forms.AddNew.DB.ItemsPOJO#5aa434
How can I get the actual countries names from the database instead of values like the above?
The classes look as follows:
The POJO class:
#Entity(name = "InitialDBItems")
public class InitialDBItemsPOJO implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int countriesListID;
private String countriesList;
public String getCountriesList() {
return countriesList;
}
public void setCountriesList(String countriesList) {
this.countriesList = countriesList;
}
public int getCountriesListID() {
return countriesListID;
}
public void setCountriesListID(int countriesListID) {
this.countriesListID = countriesListID;
}
}
The countries array that gets persisted into the database:
public class InitialDBItems {
static InitialDBItemsPOJO initialDBItemsPOJO = new InitialDBItemsPOJO();
public static void persistCountries() {
String[] countriesList = {
"Afghanistan",
"Albania",
"Algeria",
// More countries
};
for (String c : countriesList) {
initialDBItemsPOJO.setCountriesList(c);
new ManageItems().addItems(initialDBItemsPOJO);
System.out.println(c);
}
}
How I get the countries from the database:
public static ObservableList<InitialDBItemsPOJO> retrieveCountriesList() {
ObservableList<InitialDBItemsPOJO> data;
List countriesListListItems;
String countriesListListItemsQuery = "from InitialDBItems";
data = FXCollections.observableArrayList();
countriesListListItems = new ManageItems().listItems(countriesListListItemsQuery);
for (Iterator iterator = countriesListListItems.iterator(); iterator.hasNext();) {
InitialDBItemsPOJO countriesListListItemsIt = (InitialDBItemsPOJO) iterator.next();
data.add(countriesListListItemsIt);
}
return data;
}
}
Hope you can help. Thank you all in advance.
project.Forms.AddNew.DB.ItemsPOJO#5aa434 is the value of the default toString(); of your entity InitialDBItemsPOJO an easy work around is to override it
#Entity(name = "InitialDBItems")
public class InitialDBItemsPOJO implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int countriesListID;
private String countriesList;
public String getCountriesList() {
return countriesList;
}
public void setCountriesList(String countriesList) {
this.countriesList = countriesList;
}
public int getCountriesListID() {
return countriesListID;
}
public void setCountriesListID(int countriesListID) {
this.countriesListID = countriesListID;
}
public String toString(){
return countriesList;
}
}
or
public static ObservableList retrieveCountriesList() {
ObservableList<InitialDBItemsPOJO> data;
List countriesListListItems;
String countriesListListItemsQuery = "from InitialDBItems";
data = FXCollections.observableArrayList();
countriesListListItems = new ManageItems().listItems(countriesListListItemsQuery);
for (Iterator iterator = countriesListListItems.iterator(); iterator.hasNext();) {
InitialDBItemsPOJO countriesListListItemsIt = (InitialDBItemsPOJO) iterator.next();
data.add(countriesListListItemsIt.getCountriesList());
}
return data;
}

Java-Enums- How to create with default values and access them

i have a java enum which have to have some default values like
for the
String some default values
int some degault values
etc
i have created a enum like the following
package com.hexgen.tools;
public enum DefaultParamsValues {
STRING ("HEXGEN"),
INTEGER(2013),
DATE(new org.joda.time.LocalDate()),
BOOLEAN(true),
BIGINTEGER(BigInteger.valueOf(Long.MAX_VALUE)),
LONG("1898.48");
private String defaultString;
private int defaultInteger;
private LocalDate defaultDate;
private boolean defaultBoolean;
private long defaultLong;
private BigInteger defaultBigInteger;
public DefaultParamsValues(String strValue,int intValue,LocalDate date,boolean booleanValue,long longValue,BigInteger bigintVlaue){
this.defaultString = strValue;
this.defaultInteger = intValue;
this.defaultDate = date;
this.defaultBoolean = booleanValue;
this.defaultLong=longValue;
this.defaultBigInteger = bigintVlaue;
}
}
but it is giving so many issues, would some one help me to create a enum with basic values for primitive types?
EDIT: this is how i solved it:
package com.test.poc;
import java.math.BigInteger;
import org.joda.time.LocalDate;
public enum DefaultParamValues {
STRING("HEXGEN"),
INTEGER(123),
DATE(new LocalDate()),
BOOLEAN(true),
LONGVALUE(123123),
BIGINTEGER(BigInteger.valueOf(Long.MAX_VALUE));
private String defaultString;
private int defaultInteger;
private LocalDate defaultDate;
private boolean defaultBoolean;
private long defaultLong;
private BigInteger defaultBigInteger;
private DefaultParamValues(String strDefaultValue) {
defaultString = strDefaultValue;
}
private DefaultParamValues(int intDefaultValue) {
defaultInteger = intDefaultValue;
}
private DefaultParamValues(LocalDate dateDefaultValue) {
defaultDate = dateDefaultValue;
}
private DefaultParamValues(boolean booleanDefaultValue) {
defaultBoolean = booleanDefaultValue;
}
private DefaultParamValues(long longDefaultValue) {
defaultLong = longDefaultValue;
}
private DefaultParamValues(BigInteger bigIntegerDefaultValue) {
defaultBigInteger = bigIntegerDefaultValue;
}
public String getDefaultString() {
return defaultString;
}
public int getDefaultInt() {
return defaultInteger;
}
public LocalDate getDefaultDate() {
return defaultDate;
}
public boolean getDefaultBoolean() {
return defaultBoolean;
}
public long getDefaultLong() {
return defaultLong;
}
public BigInteger getDefaultBigInteger() {
return defaultBigInteger;
}
}
Thanks
First, an enum is probably not right for your purpose. You should use a final Class with constants.
But if you want it inefficient and cumbersome you could do it this way:
public enum GenericEnum {
STRING("HEXGEN"),
INTEGER(2013),
DATE(new Date()),
BOOLEAN(true),
BIGINTEGER(BigInteger.valueOf(Long.MAX_VALUE)),
LONG("1898.48");
private String defaultString;
private int defaultInteger;
private Date defaultDate;
private boolean defaultBoolean;
private long defaultLong;
private BigInteger defaultBigInteger;
GenericEnum(Object value) {
if(String.class.isAssignableFrom(value.getClass())) {
this.defaultString = (String) value;
} else if (Integer.class.isAssignableFrom(value.getClass())) {
this.defaultInteger = (Integer) value;
}
[...]
}
}
or overload the constructor:
private GenericEnum(String val) {
this.defaultString = val;
}
private GenericEnum(int val) {
this.defaultInteger = val;
}
[...]

Categories