How do I query an object that has another object as a property? I need to get the values of the property as well. Here's my model:
public class Department {
public int DeptId;
public string DeptName;
}
public class Employee {
public int Id;
public string Name;
public int DeptId;
public Department Department;
}
I'm coming from a c# background, and I could do this with c# using Entity Framework. Now it seems like this model works but when I included a sqllite functionality for the objects I'm not sure how to query it.
Here's my first try, but I'm not sure if this is the best way
public List<Employee> getAllEmployeesWithDepartments(){
List<Employee> employees = new ArrayList<>();
SQLiteDatabase db = this.getReadableDatabase();
Cursor res = db.rawQuery("SELECT * FROM Employee e LEFT JOIN Department d on e.DeptId = d.Id" , null);
if(res.moveToFirst()){
do{
Employee emp = new Employee();
emp.Id = res.getInt(res.getColumnIndex("Id"));
emp.Name = res.getString(res.getColumnIndex("Name"));
Department dep = new Department();
dep.Id = res.getInt(res.getColumnIndex("Id"));
dep.Name = res.getString(res.getColumnIndex("Name"));
emp.Department = dep;
employees.add(emp);
}while (res.moveToNext());
}
return employees;
}
You're confused. SQLite doesn't have objects. Its a table based database (like almost all databases). You can map objects into tables, but you have to create those mappings yourself. Since SQLite doesn't have objects, it definitely doesn't have subobjects. You generally make something like it by joining another table with a foreign key constraint on the main table, but it really is defined by your schema. There is no generic answer for you.
Related
I'm trying write a query using ORMLite. I need this query check a id of customer in other entity. How could I do it ?
Entities
#DatabaseTable(tableName = "customer")
public class Customer {
#DatabaseField(generatedId = true)
private Integer id;
#DatabaseField
private String name;
#DatabaseField
private Sale sale;
//gets sets
}
#DatabaseTable(tableName = "sale")
public class Sale{
#DatabaseField(generatedId = true)
private Integer id;
#DatabaseField
private Customer customer;
#DatabaseField
private Integer status;
//gets sets
}
Query
Customer customer = new Customer();
customer.setId(1);
customer.setName("Fernando Paiva");
QueryBuilder<Sale, Integer> qb = saleDAO.queryBuilder();
Where where = qb.where();
where.eq("sale.customer.id", customer.getId());
where.and();
where.eq("sale.status", 1);
PreparedQuery<Sale> pq = qb.prepare();
List<Sale> list = saleDAO.query(pq);
Log.i("SALE LIST->", list.size() + "");
You need to use JOIN
Here your example using Join:
First of all, you need a QueryBuilder to each Dao.
You can apply your filter to each QueryBuilder separately
Last but not least, you join the main QueryBuilder (Sales) with the Customer's QueryBuilder and
perform the query.
Here the code
Dao<Sale, Integer> saleDao = DaoManager.createDao(getConnectionSource(), Sale.class);
Dao<Customer, Integer> customerDao = DaoManager.createDao(getConnectionSource(), Customer.class);
QueryBuilder<Sale, Integer> saleQa= saleDao.queryBuilder();
saleQa.where().eq("status", 1);
QueryBuilder<Customer, Integer> customerQa = customerDao.queryBuilder();
customerQa.where().idEq(customer.getId());
sales = saleQa.join(customerQa).query();
Are you trying to use OrmLite to check if the customer id is the same as the sale id and get all of the matching result? If so the below code will do that
qb.where().eq("id", customer.id);
List<Sale> results = saleDAO.query(qb.prepare());
Update:
After rereading your question I realized what you're trying to do
qb.where().in(Sale.customer, id);
See this question for further details.
Ormlite Foreign Entity Searching
Since ORMLite is already mapping an Android Cursor to a POJO.
I'm wondering if I could somehow leverage that instead of writing a copy constructor and manually map a Cursor to my POJO?
I could for example, use a content provider and map that result into the same POJO's backed by ORMLite's Dao.
Perform operations on my dataset and then batch insert that data into my database using ORMLite.
The advantage to this would be that I don't have to write all the CRUD ofc.
EDIT:
this is a fabricated example but the concept is here. What would be the best way to do something like this?
#DatabaseTable(tableName = "my_dict")
public class MyDictionary{
#DatabaseField(columnName = COLUMN_ID, generatedId = true)
Integer _ID;
#DatabaseField(columnName = "word")
String word;
#DatabaseField(columnName = "app_id")
String app_id;
#DatabaseField(columnName = "frequency")
Integer frequency;
#DatabaseField(columnName = "locale")
String locale;
public MyDictionary(){}
}
public void someCoolDatasetOperation(){
Dao<MyDictionary, Long> dao = databaseHelper.getDao(MyDictionary.class);
List<MyDictionary> inDb = dao.queryForAll();
Cursor dictCursor = getContentResolver().query(WEB_SERVICE_URI,
null, // The columns to return for each row
null, // Selection criteria
null, // Selection criteria
null); // The sort order for the returned rows
//iffy part don't know the correct/best way to do this?????
AndroidDatabaseResults fabricatedResult = new AndroidDatabaseResults(dictCursor, null);
List<MyDictionary> fromContentProvider = dao.mapSelectStarRow(dictCursor);
//Do Something with the lists
}
I don't know a whole lot about ORMLite and I'm not positive the two will get along, but I'm working on library that can generate a POJO wrapper for cursor using an annotated class like you have there.
So if I were to try and use this library with your above example it might look like this:
#Entity
public class MyDictionary{
#Attribute(sqliteName = COLUMN_ID, primaryKey = true, autoIncrement = true)
Integer _ID;
#Attribute(sqliteName = "word")
String word;
#Attribute(sqliteName = "app_id")
String app_id;
#Attribute(sqliteName = "frequency")
Integer frequency;
#Attribute(sqliteName = "locale")
String locale;
}
public void someCoolDatasetOperation(){
Dao<MyDictionary, Long> dao = databaseHelper.getDao(MyDictionary.class);
List<MyDictionary> inDb = dao.queryForAll();
Cursor dictCursor = getContentResolver().query(WEB_SERVICE_URI,
null, // The columns to return for each row
null, // Selection criteria
null, // Selection criteria
null); // The sort order for the returned rows
List<MyDictionary> fromContentProvider = new ArrayList<MyDictionary();
while(dictCursor.moveToNext()) {
//MyDictionaryEntity is a generated class that contains getters and
//setters for all the attributes in MyDictionary
fromContentProvider.add(new MyDictionaryEntity(dictCursor));
}
}
I'm still actively developing it and haven't got around to too much documentation yet, but if you wanted to check it out:
https://github.com/adecker89/Glowplug
I have a SQLite table (on Android) that has numerous fields, but certain fields are repeated/denormalized. I would like to select a distinct set of this data and use them as actual objects.
Example
books table
title summary author
Little Johnny A funny kid Johnny Himself
Big Johnny A funny adult Johnny Himself
I would like to extract one author from this list ("Johnny Himself") and would expect I should be able to do this with ORMLite instead of manually with Java.
I would like to select a distinct set of this data and use them as actual objects.
ORMLite supports a distinct() method on the QueryBuilder that should do what you want. So your code would look something like:
List<Book> results = booksDao.queryBuilder()
.distinct().selectColumns("author").query();
In this case, the resulting Book objects would only have the author field set and not the id field or anything else. If you just wanted the author names instead of objects then you could do:
GenericRawResults<String[]> rawResults =
booksDao.queryRaw("SELECT DISTINCT author FROM books");
for (String[] resultColumns : rawResults) {
String author = resultColumns[0];
...
}
This is my application code
public class DbHelper<T> {
private Class<T> c;
private DatabaseHelper db;
public DbHelper(Class<T> c) {
this.c = c;
db = DatabaseHelper.getInstance();
}
This is a good idea
public List<T> queryForBuilderDistinct(int offset, int limit, String ColumnsName,
String orderName, boolean isAsc) {
try {
Dao<T, Integer> dao = db.getDao(c);
QueryBuilder<T, Integer> queryBuilder = dao.queryBuilder();
if (offset != 0) {
queryBuilder.offset((long) offset);
}
if (limit != 0) {
queryBuilder.limit((long) limit);
}
if (orderName != null) {
queryBuilder.orderBy(orderName, isAsc);
}
queryBuilder.distinct().selectColumns(ColumnsName);
return dao.query(queryBuilder.prepare());
} catch (SQLException e) {
LogUtil.e(TAG, "queryForBuilderDistinct", e);
}
return new ArrayList<T>();
}
I use ormlite and I have a db with a field:
public static final String NAME = "name";
#DatabaseField (canBeNull = false, dataType = DataType.SERIALIZABLE, columnName = NAME)
private String[] name = new String[2];
And I would like to get all elements that name[0] and name[1] are "car". I try to add a where clausule like:
NAMEDB nameDB = null;
Dao<NAMEDB, Integer> daoName = this.getHelper().getDao(NAMEDB.class);
QueryBuilder<NAMEDB, Integer> queryName = daoName.queryBuilder();
Where<NAMEDB, Integer> where = queryName.where();
where.in(nameDb.NAME, "car");
But it doesn't work because it's an array string.
I have other fields:
public static final String MARK = "mark";
#DatabaseField (canBeNull = false, foreign = true, index = true, columnName = MARK)
private String mark = null;
And I can do this:
whereArticulo.in(nameDB.MARK, "aaa");
How can I solve my problem? Thanks.
It seems to me that a third option to store a string array (String[] someStringArray[]) in the database using Ormlite would be to define a data persister class that converts the string array to a single delimited string upon storage into the database and back again to a string array after taking it out of the database.
E.g., persister class would convert ["John Doe", "Joe Smith"] to "John Doe | Joe Smith" for database storage (using whatever delimiter character makes sense for your data) and converts back the other way when taking the data out of the database.
Any thoughts on this approach versus using Serializable or a foreign collection? Anyone tried this?
I just wrote my first persister class and it was pretty easy. I haven't been able to identify through web search or StackOverflow search that anyone has tried this.
Thanks.
As ronbo4610 suggested, it is a good idea to use a custom data persister in this case, to store the array as a string in the database separated by some kind of delimiter. You can then search the string in your WHERE clause just as you would any other string. (For example, using the LIKE operator)
I have implemented such a data persister. In order to use it, you must add the following annotation above your String[] object in your persisted class:
#DatabaseField(persisterClass = ArrayPersister.class)
In addition, you must create a new class called "ArrayPersister" with the following code:
import com.j256.ormlite.field.FieldType;
import com.j256.ormlite.field.SqlType;
import com.j256.ormlite.field.types.StringType;
import org.apache.commons.lang3.StringUtils;
public class ArrayPersister extends StringType {
private static final String delimiter = ",";
private static final ArrayPersister singleTon = new ArrayPersister();
private ArrayPersister() {
super(SqlType.STRING, new Class<?>[]{ String[].class });
}
public static ArrayPersister getSingleton() {
return singleTon;
}
#Override
public Object javaToSqlArg(FieldType fieldType, Object javaObject) {
String[] array = (String[]) javaObject;
if (array == null) {
return null;
}
else {
return StringUtils.join(array, delimiter);
}
}
#Override
public Object sqlArgToJava(FieldType fieldType, Object sqlArg, int columnPos) {
String string = (String)sqlArg;
if (string == null) {
return null;
}
else {
return string.split(delimiter);
}
}
}
Unfortunately ORMLite does not support querying fields that are the type SERIALIZABLE. It is storing the array as a serialized byte[] so you cannot query against the values with an IN query like:
where.in(nameDb.NAME, "car");
ORMLite does support foreign collections but you have to set it up yourself with another class holding the names. See the documentation with sample code:
http://ormlite.com/docs/foreign-collection
I have a Client bean ,
#DatabaseField(columnName = "client_id",generatedId = true,useGetSet = true)
private Integer clientId;
#DatabaseField(columnName = "client_nom",useGetSet = true)
private String clientNom;
#DatabaseField(columnName = "city_id",foreign = true,useGetSet = true)
private City city;
and a City bean ,
#DatabaseField(columnName = "city_id",generatedId = true,useGetSet = true)
private Integer cityId;
#DatabaseField(columnName = "city_name",useGetSet = true)
private String cityName;
#ForeignCollectionField
private ForeignCollection<Client> clientList;
Those beans are just an example but let's say , I want to delete all the clients having as foreign city cityId when deleting a city.
How is that possible please ?
ORMLite does not support cascading deletes #Majid. That is currently outside of what it considers to be "lite". If you delete the city then you need to delete the clients by hand.
One way to ensure this would be to have a CityDao class that overrides the delete() method and issues the delete through the ClientDao at the same time. Something like:
public class CityDao extends BaseDaoImpl<City, Integer> {
private ClientDao clientDao;
public CityDao(ConnectionSource cs, ClientDao clientDao) {
super(cs, City.class);
this.clientDao = clientDao;
}
...
#Override
public int delete(City city) {
// first delete the clients that match the city's id
DeleteBuilder db = clientDao.deleteBuilder();
db.where().eq("city_id", city.getId());
clientDao.delete(db.prepare());
// then call the super to delete the city
return super.delete(city);
}
...
}
To implement cascading while using ORMLite on Android you need to enable foreign key restraints as described here:
(API level > 16)
#Override
public void onOpen(SQLiteDatabase db){
super.onOpen(db);
if (!db.isReadOnly()){
db.setForeignKeyConstraintsEnabled(true);
}
}
For API level < 16 please read:
Foreign key constraints in Android using SQLite? on Delete cascade
Then use columnDefinition annotation to define cascading deletes. Ex:
#DatabaseField(foreign = true,
columnDefinition = "integer references my_table(id) on delete cascade")
private MyTable table;
This is assuming the table/object name is "my_table", as described here: Creating foreign key constraints in ORMLite under SQLite