I am writing a Xamarin Android application using SQLite and am not sure how to add an object to a table where the object has a list.
Here is my model class:
public class TestObject
{
[PrimaryKey]
public int Id { get; set; }
public string name { get; set; }
public DateTime lastUpdate { get; set; }
public List<TestItem> items { get; set; }
}
Here is my code to add an object to a table:
public void InsertObjectToDatabase<T>(string databasePath, T objType)
{
var db = new SQLiteConnection (databasePath);
db.CreateTable(typeof (T));
db.InsertOrReplace (objType);
}
Here is my code to add a TestObject to a table:
TestObject testObject = new TestObject ();
testObject.Id = 1;
testObject.name = "Test Object 1";
testObject.lastUpdate = DateTime.Now;
sQLiteService.InsertObjectToDatabase<TestObject> (filename, testObject);
This is the error that I am getting:
System.NotSupportedException: Don't know about System.Collections.Generic.List`1[LearningSQLite.TestItem]
Is it possible add a list to a SQLite table?
Thanks in advance
Create another table and refer to the parent table with a foreign key. Once you insert the row in parent, insert all the items from the list in the child. Read more here http://www.sqlite.org/foreignkeys.html
If you are using SQLite.Net-PCL you can use the IBlobSerializer interface to store complex types to a BLOB (byte array). Here is a unit test class that provides more info:
https://github.com/oysteinkrog/SQLite.Net-PCL/blob/master/tests/BlobSerializationTest.cs
For the serializer you can either implement your own or use something like JSON serializers to store the data as JSON.
I am using the BLOB interface to use SQLite as key-value pair caching mechanism:
https://github.com/XForms/Xamarin-Forms-Labs/blob/master/src/Xamarin.Forms.Labs/Plugins/Caching/Xamarin.Forms.Labs.Caching.SQLiteNet/SQLiteSimpleCache.cs
Related
I'm using Room library (MVVM Pattern), and one of the Dao functions returns this error message:
android.database.sqlite.SQLiteException: near "?": syntax error (code 1): , while compiling: UPDATE parcel_table SET possibleDeliveryPersonsList = ?,? WHERE id = ?
This is the Dao code:
#Dao
public interface ParcelDao {
#Insert
void insert(Parcel parcel);
#Delete
void delete(Parcel parcel);
#Query("UPDATE parcel_table SET shippingDate=:shippingDate WHERE id = :id")
void updateShippingDate(String shippingDate, int id);
#Query("UPDATE parcel_table SET parcelStatus=:status WHERE id = :id")
void updatePackageStatus(Enums.ParcelStatus status, int id);
#Query("UPDATE parcel_table SET deliveryPersonName=:deliveryPersonName WHERE id = :id")
void updateDeliveryPersonName(String deliveryPersonName, int id);
#Query("UPDATE parcel_table SET possibleDeliveryPersonsList = :possibleList WHERE id = :tid")
void updatePossibleDeliveryPersonsList(List<String> possibleList, int tid);
#Query("DELETE FROM parcel_table")
void deleteAllParcels();
#Query("SELECT * from parcel_table")
LiveData<List<Parcel>> getParcels();
}
And this is part of the Parcel class:
#Entity(tableName = "parcel_table")
public class Parcel {
private Enums.ParcelType parcelType;
private boolean isFragile;
private Enums.ParcelWeight parcelWeight;
private LatLng warehouseLocation;
private String recipientName;
private LatLng recipientAddress;
private String recipientEmail;
private String recipientPhone;
private String dateReceived;
private String shippingDate;
private Enums.ParcelStatus parcelStatus;
private String deliveryPersonName;
private String fireBasePushId;
private List<String> possibleDeliveryPersonsList;
#PrimaryKey(autoGenerate = true)
#NonNull
private int id;
//and more...
}
The List<String> Type Converter:
#TypeConverter
public String listToString(List<String> list) {
String joined = TextUtils.join(", ", list);
return joined;
}
#TypeConverter
public List<String> stringToList(String string) {
List<String> myList = new ArrayList<String>(Arrays.asList(string.split(",")));
return myList;
}
I have no idea what to do, because the SQLite code is supposedly automatically generated by the Dao and I have no effect on it ...
Two more workarounds in addition to Bob Snyder' answer (but they need to be thoroughfully tested):
To "imitate" TypeConverter (from List to String) by yourself (it's a tricky thing, I've not tried it in practice!):
In DAO change the type of possibleList to String:
#Query("UPDATE parcel_table SET possibleDeliveryPersonsList = :possibleList WHERE id = :tid")
void updatePossibleDeliveryPersonsList(String possibleList, int tid);
add auxiliary method for conversion (you can place it at DAO as well):
void updatePossibleDeliveryPersonsList(List<String> possibleList, int tid) {
String listToString = TextUtils.join(", ", possibleList);
// copied from your converter, it could be put in some common function to follow DRY
updatePossibleDeliveryPersonsList(listToString, tid);
}
and call it from Repository/ViewModel:
db.ParcelDao().updatePossibleDeliveryPersonsList(possibleList, tid);
To replace your multiple updateXXX methods in DAO with single update (you have a lot of fields in your table, may be it would be better to try some universal way to update any combinations of them?):
#Update
void update(Parcel parcel);
Add to your DAO method for searching parcel by id:
#Query("SELECT * from parcel_table where id = :id")
Parcel getParcel(int id);
And in your Repository/ViewModel at first get Parcel, then change it (status, name whatever) and then update database:
Parcel parcel = db.ParcelDao().getParcel(id); // let's say it can't be null
parcel.shippingDate = yourShippingDate; // or change here any of your other fields, including list
db.ParcelDao().update(parcel);
The documentation for the Query annotation explains a feature of Room argument binding:
As an extension over SQLite bind arguments, Room supports binding a
list of parameters to the query. At runtime, Room will build the
correct query to have matching number of bind arguments depending on
the number of items in the method parameter.
This feature was likely intended to be used in the where-clause (as shown in the documentation example), but appears to be applied everywhere in the query statement.
In your case, the desired behavior is to have Room apply your type converter, but instead Room is ignoring the type converter and generating the special list binding.
I think you are going to have to work around this limitation of the current Room implementation. You might want to write a Room bug-report to get confirmation that the explanation provided here is correct.
One option for workaround that will use the type converters is to define this class:
public class DeliveryPersonsUpdate {
public int id;
public List<String> deliveryPersons;
public DeliveryPersonsUpdate(int id, List<String> deliveryPersons) {
this.id = id;
this.deliveryPersons = deliveryPersons;
}
}
then add this method to your Dao:
#Update(entity = Parcel.class)
void update(DeliveryPersonsUpdate update);
Example invocation:
db.ParcelDao().update(new DeliveryPersonsUpdate(id, personsList);
I am using GreenDao for Android application, with some specification, for example, I have a Contact Model with some information like name, avatar, phone number, etc...
Right now the need is to change from only one phone number to a multiphone number.
Instead of creating two tables (table for numbers, and table for contacts), I really need just one information is the number so in my backend the contact numbers is stocked on a DC2type, (a json array saved as a string).
Do we have a possibility to do that using GreenDao?
i search for a solution or a DC2type implementation , etc ... and nothing is found
so i decide to created by my self , and this is what i did :
using the #Convert annotation presented of GreenDao 3 :
#Property(nameInDb = "phoneNumbers")
#Convert(converter = PhoneNumbersConverter.class, columnType = String.class)
private List<String> phoneNumbers;
static class PhoneNumbersConverter implements PropertyConverter<List<String>, String> {
#Override
public List<String> convertToEntityProperty(String databaseValue) {
List<String> listOfStrings = new Gson().fromJson(databaseValue,List.class);
return listOfStrings;
}
#Override
public String convertToDatabaseValue(List<String> entityProperty) {
String json = new Gson().toJson(entityProperty);
return json;
}
}
short story long , i create a json to array parser
thanks to myself to helped me :D
I want to select multiple column from SqLite through Room Library in Android SDK environment.
Below is the query for selecting it.
#Query("SELECT ID,message,timestamp FROM Chat_Message WHERE groupID =:groupID
ORDER BY timestamp DESC LIMIT 1")
public List get_last_msg_ID_timestamp (String
groupID);
My Last_Msg_Detail class which is define under main class is as follows:-
public class Last_Msg_Detail {
public Integer ID;
public String message;
public Long timestamp;
}
For accessing this three variable have created below method :-
Last_Msg_Detail last_record_t = new Last_Msg_Detail();
public Last_Msg_Detail get_last_msgand_time_stamp(String groupID){
List<Last_Msg_Detail> last_record =
chat_messageDao.get_last_msg_ID_timestamp(groupID);
last_record_t = last_record.get(0);
return last_record_t;
}
On Rebuilding Project, getting follow error
1. error: Cannot figure out how to save this field into database. You can
consider adding a type converter for it.
2. error: Not sure how to convert a Cursor to this method's return type
Kindly advise how to resolve.
Thanks in advance for your help.
your method return custom object and the object has a lot of fields.
so, when you try to return specific columns, you try to return a new object. so the error occurs.
to solve the problem, create a new object for the selected columns. it has to has these fields.
ID,message,timestamp
and use the object in your method
#Query("SELECT ID,message,timestamp FROM Chat_Message WHERE groupID =:groupID ORDER BY timestamp DESC LIMIT 1")
public List<NEW_OBJECT> get_last_msg_ID_timestamp (String groupID);
With a custom Marshaller I try to map a DynamoDB query to an object
class ownObject {
private int myInteger;
#DynamoDBMarshalling(marshallerClass = MasrshallAsInteger.class)
#DynamoDBAttribute
public int getMyInteger {
return myInteger;
}
public void setMyInteger(int newint) {
myInteger = newint;
}
}
Since the value myInteger in the db has both types String and Number, the SDK throws the Exception: "Expected S in value {N:123,}" if I use the marshaller and "Expected N in value {S:123,}" on an other object if I don't .
Is there any way to force the DynamoDB to use a custom marshaller and parse the value of the Key as String? Or is there any other way to parse a undetermined type of data but using PaginatedQueryList?
I recommend you use the Document SDK to paginate, parse your items into Item objects, paginate, and then convert those Items to your domain.
Table table = new AmazonDynamoDBClient(new DefaultCredentialsProviderChain()).getTable("ownObject");
for (Item item : table.scan()) {
//convert item to your domain object here
}
I'm trying to map an object to database with greenDao. But when it comes to arrays, I don't know how to do it. After receiving JSON from network and deserializing it with GSON, I have objects defined by this class:
public class Car {
Long carId;
String name;
ArrayList<String> listOfLinks;
}
In case of a a different architecture, like this:
public class Car {
Long carId;
String name;
ArrayList<Link> listOfLinks;
}
public class Link {
Long carId;
String link;
}
----
Entity cars = schema.addEntity("Car");
cars.addLongProperty("carId").primaryKey();
cars.addStringProperty("name");
Entity links = schema.addEntity("Link");
links.addStringProperty("name");
links.addIdProperty().primaryKey().notNull().autoincrement();
Property linkProperty = links.addLongProperty("carId").getProperty();
ToMany carToLinks = cars.addToMany(link, linkProperty);
It would is easy. Define some relations, define properties, add foreign key and your done. With arrays I have no clue what to do. Ideas?
That approach is not common when using relational databases.
This is commonly done using to-many relations : instead of using a list of String, you can create a Link entity and then use a list of Link.
Relation toMany is useful when you have a list of your not primitive object, that you can declare like entity that have its own id etc etc etc, and make list of entities (with toMeny). By doing that greenDao makes another table in the base for you new entity with the foreign key of the base entity that contains list. When you have list of primitive type the only way to do is to make converter that converts List into one of the primitive types that greenDao works naturally. You have to do something like this `
import org.greenrobot.greendao.converter.PropertyConverter;
import java.util.Arrays;
import java.util.List;
/**
*DOLE BREEE SQLITE BREEEEEE!!!**
*i choosed to convert List into one string
*that is going to be saved in database, and vice versa
*/
public class GreenConverter implements PropertyConverter, String> {
#Override
public List convertToEntityProperty(String databaseValue) {
if (databaseValue == null) {
return null;
}
else {
List<String> lista = Arrays.asList(databaseValue.split(","));
return lista;
}
}
#Override
public String convertToDatabaseValue(List<String> entityProperty) {
if(entityProperty==null){
return null;
}
else{
StringBuilder sb= new StringBuilder();
for(String link:entityProperty){
sb.append(link);
sb.append(",");
}
return sb.toString();
}
}
}
now above all the properties that are List you have to put
#Convert(converter=yourconverterclass.class, columnType = String.class)
#Entity
public class ShipEntry {
#Id(autoincrement = true)
private long ship_id;
private String name;
private String model;
private String manufacturer;
private String starship_class;
#Convert(converter = GreenConverter.class, columnType = String.class)
private List<String> pilots;
#Convert(converter = GreenConverter.class, columnType = String.class)
private List<String> films ;
}
you can create Converter as a inner class of entitiy, and in that case it has to be declared as staticthat is the only way i have found, but the bad side is that you can not use property that you are converting into query. There might me some typo, but i hope this helps to solve your problem
I also have the same issue, and there no answer (not in official docs, not in google). Please explain how to map List to Entity?
public class Car {
Long carId;
String name;
ArrayList<String> listOfLinks;
}
Can I do something like this?
#Entity(active = true, nameInDb = "CARS")
public class Car {
#Id
private Long id;
#NotNull
#Unique
private String remoteId;
#ToMany(joinProperties = {
#JoinProperty(name = "remoteId", referencedName = "carRemoteId")
})
private List<Links> listOfLinks;
}
#Entity(active = true, nameInDb = "LISTOFLINKS")
public class Links{
#Id
private Long id;
#NotNull
#Unique
private String remoteId;
#SerializedName("listOfLinks")
#Expose
private String listOfLinks;//is it possible?????
private String carRemoteId;
}
Since JPA 2.0, you can use an element collection to persist a Collection of value types. You just need to annotate the attribute with #ElementCollection and the persistence provider will persist the elements of the Collection in an additional database table.
#Entity
public class Author {
#ElementCollection
private List<String> phoneNumbers = new ArrayList<String>();
}
The element collection might seem easier to use than an entity with a one-to-many association. But it has one major drawback: The elements of the collection have no id and Hibernate can’t address them individually.
When you add a new Object to the List or remove an existing one, Hibernate deletes all elements and inserts a new record for each item in the List.
Let’s take a quick look at an example. The following code snippet selects an Author entity and adds a second phoneNumber to the element collection.
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Author a = em.find(Author.class, 1L);
a.getPhoneNumbers().add("42424242");
em.getTransaction().commit();
em.close();
an element collection is an easy but not the most efficient option to store a list of value types in the database. You should, therefore, only use it for very small collections so that Hibernate doesn’t perform too many SQL statements. In all other cases, a one-to-many association is the better approach.