I have two entity order and customer. Each order is associated with zero or single customer and each customer is associated with zero or many orders.
#Entity
public class Order {
#PrimaryKey(autoGenerate = true)
public long id;
public long customerId;
....
}
#Entity
public class Customer {
#PrimaryKey(autoGenerate = true)
public long id;
public String name;
}
I wanted to query the order table with the corresponding customer if it exists.
So, I have created the relation following the documentation.
public class OrderAndCustomer {
#Embedded public Order order;
#Relation(
parentColumn = "customerId",
entityColumn = "id"
)
public Customer customer;
}
I can query the list of orders and the corresponding customer using dao.
#Dao
public interface OrderDao {
#Transaction
#Query("select * from `order`")
List<OrderAndCustomer> getOrderAndCustomer();
}
But when I try to access the columns of child entity, I got compile error. For instance, I wanted to query the order where customer name is like ****.
Therefore, my updated query:
#Query("select * from `order` where customer.name like '****'")
Is it possible to access the attribute of child entity in where clause?
So, the question arises that how the relation works!? I have figure out that it first query the order entity and then query the customer entity. Let me show the correct way If I am wrong.
I have another solution to query multiple tables but I am unable to use the relation feature what room provides or I am missing something!
I can follow this answer to use join and mapping to object.
public class OrderAndCustomer extends Order {
public String customerName;
}
Query:
#Query("select `order`.*, customer.name as customerName from `order` left outer join customer on `order`.customerId = customer.id where customerName like '****'")
List<OrderAndCustomer> getOrderAndCustomer();
But, Still have questions.
How do I map all columns of the order and customer table? Do I need to map all columns of customer with as in the query or there is another simplified approach available? If both tables has more columns and I need all of the columns of the first and second table to be fetched, then my query will be long enough. I wonder if there is the simplest thing to map both tables without or minimal using of as with all columns of the second table.
Help me with the right approach and better available solution.
My Problem:
I'm struggling to eliminate the compiling error on the following Room #Query statement in a Room DAO. As you can see, the SQLite query statement is joining various fields from different tables. The missing fields identified by the error are a part of the Notes class constructor identified in the List type for the method. I think I need to change the List type identified. If I'm right, I need some guidance/suggestion on how I should resolve it. Do I need to create a new Class and DAO with just those specific fields queried? Or maybe just a class since there is not table specific to these fields only. The error is:
error: The columns returned by the query does not have the fields [commentID,questionID,quoteID,termID,topicID,deleted] in com.mistywillow.researchdb.database.entities.Notes even though they are annotated as non-null or primitive. Columns returned by the query: [NoteID,SourceID,SourceType,Title,Summary]
List getNotesOnTopic(String topic);
#Query("SELECT n.NoteID, s.SourceID, s.SourceType, s.Title, c.Summary FROM Comments as c " +
"LEFT JOIN Notes as n ON n.CommentID = c.CommentID " +
"LEFT JOIN Sources as s ON n.SourceID = s.SourceID " +
"LEFT JOIN Topics as t ON n.TopicID = t.TopicID WHERE t.Topic = :topic AND n.Deleted = 0")
List<Notes> getNotesOnTopic(String topic);
What I'm trying to do:
I'm attempting to convert and existing Java desktop app with an embedded an SQLite database. The above query does work fine in that app. I only want to pass field data from these tables.
What I've tried:
I've done some googling and visited some forums for the last few days (e.g. Android Forum, Developer.Android.com) but most of the Room #Query examples are single table full field queries (e.g. "Select * From table"). Nothing I found yet (there is probably something) quite addresses how and what to do if you are joining and querying only specific fields across tables.
I think I may have fixed my issue. I just created a new class called SourceTable and designated the queried fields in the constructor. The only catch was I, according to a follow up error, was that the parameters had to match the field names.
public class SourcesTable {
private int NoteID;
private int SourceID;
private String SourceType;
private String Title;
private String Summary;
public SourcesTable(int NoteID, int SourceID, String SourceType, String Title, String Summary){
this.NoteID = NoteID;
this.SourceID = SourceID;
this.SourceType = SourceType;
this.Title = Title;
this.Summary = Summary;
}
}
and then I update my list method:
List<SourcesTable> getNotesOnTopic(String topic);
I am trying to build sqlite view vwCOMPLETE_INFO from 2 tables (CUSTOMERS and CITIES)
but i have problem in merging the FIRST_NAME and LAST_NAME to build FULL_NAME Column in the new view
CREATE VIEW vwCOMPLETE_INFO AS SELECT (CUSTOMERS.FIRST_NAME || CUSTOMERS.LAST_NAME AS FULL_NAME), CUSTOMERS.AGE, CITIES.NAME FROM CITIES JOIN CUSTOMERS ON CUSTOMER.CITY_ID = CITY.ID;
what could fix that ?
The issue is that you are including AS FULL_NAME as part of the term that builds the column's data, rather than where it should be as after the term so that it names the column accordingly.
In short AS FULL_NAME should be the other-side of/outside/after the parenthesis's. i.e. ....CUSTOMERS.LAST_NAME) AS FULL_NAME..... rather than .....CUSTOMERS.LAST_NAME AS FULL_NAME).....
Try
CREATE VIEW vwCOMPLETE_INFO AS SELECT (CUSTOMERS.FIRST_NAME || CUSTOMERS.LAST_NAME) AS FULL_NAME, CUSTOMERS.AGE, CITIES.NAME FROM CITIES JOIN CUSTOMERS ON CUSTOMER.CITY_ID = CITY.ID;
I am trying to change my sqlite database with room library. I am little confuse with left join query.
I have implemented it with sqlite, but don't know how can I achieve same withh room?
Here is my table creation:
first table: Notification
db.execSQL("CREATE TABLE IF NOT EXISTS $TABLE_NAME ($COLUMN_ID INTEGER PRIMARY KEY, $ICON TEXT, $TITLE INTEGER," +
" $DATE INTEGER, $TYPE INTEGER,$URL TEXT, $MESSAGE INTEGER, FOREIGN KEY($TITLE) REFERENCES ${TableNotificationsTrans.getTableName(this)}(id)," +
"FOREIGN KEY($MESSAGE) REFERENCES ${TableNotificationsTrans.getTableName(this)}(id))")
second table: Notification_Trans
db.execSQL("CREATE TABLE IF NOT EXISTS $TABLE_NAME ($COLUMN_ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, $COLUMN_EN TEXT, $COLUMN_GU TEXT, $COLUMN_HI TEXT)")
What I am doing is I am storing notification in notification table but its name and description will be stored with specific language, in notification_trans.
Query to achieve
DatabaseHelper.database!!.rawQuery("SELECT A.$COLUMN_ID, A.$ICON, N.${language.toLowerCase()} $TITLE, A.$DATE, A.$TYPE, A.$URL, M.${language.toLowerCase()} $MESSAGE FROM $TABLE_NAME A LEFT JOIN NotificationsTrans N ON A.$TITLE = N.id LEFT JOIN NotificationsTrans M ON A.$MESSAGE = M.id ORDER BY $DATE DESC LIMIT $pageNum*10, 10", null)
Question
How can I achieve same with room?
Edit
My application is multi-language application, where I am getting notification title with specific language, like Hindi or Gujarati. I am storing notification details in notification table, while title in notification_trans.
NotificationTrans have column with id, english, hindi, gujarati.
When user asked for gujarati, I am retriving notification title from notificationTrans's column gujarati.
I am able do so, in sqlite.
But now I want it with Room
First You have to make the model classes for both, You may have already declared them , You just need to make a few changes if they already exists.
#Entity
public class Notification {
#PrimaryKey
int id;
String icon;
#ForeignKey(entity = Notification_Trans.class, parentColumns = "col_id", childColumns = "id")
String title;
int date;
int type;
String url;
int msg;
}
#Entity
public class Notification_Trans {
#PrimaryKey(autoGenerate = true)
int col_id;
String column_en;
String column_gu;
String column_hi;
This makes for your POJO, I couldn't understand your Foreign key constraints, so Pardon me for that, you can make changes as you see fit.
You can Declare your DAO as per this`
#Dao
public interface DAO {
#Query("SELECT note.id, note.title, note.description, category.name as categoryName " +
"FROM note " +
"LEFT JOIN category ON note.category_id = category.id")
List getCategoryNotes();
}
`
I haven't made changes in the query, which I found at Link here. As your query was a complex one, But, it'll give you an Idea about how to do that.,
After this, You just need to access your Dao interface from your Database class object, which will handle the creation & all other things room, like this one below`
#Database(entities = {Notification.class, NotificationTrans.class}, version = 3)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase instance;
public static AppDatabase getAppDatabase(Context context) {
if (instance == null) {
instance =
Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "database_name")
// allow queries on the main thread.
// Don't do this on a real app! See PersistenceBasicSample for an example.
//.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.build();
}
return instance;
}
public static void destroyInstance() {
instance = null;
}
public abstract Dao notificationDao();
It helps creating a separate class for Database, & keeping track of object from it.
& you can access your data with AppDatabase.getAppDatabase(context).notificationDao().yourQueryMethodName();
You may require to refer to this to understand the relations between room, & implement your requirement,
EDIT 1:
Here's how your DAO should look like ,`
#Insert
void insert(Notifications object);
//This will insert a single Item
#Insert
void insertAll(Notifications... objects);
While this can enter a list of Data,
You can call this methods with your Database object, likeAppDatabase.getAppDatabase(context).notificationDao().yourQueryMethodName() here instead of yourQueryMethod(), if you call insert() & pass the object you need to store in the database, It'll do it,
For E.g.db.parcelDao().insert(parcel);
this is how I insert Data in my ParcelDao, db is Database object, & parcel is the object of data need to be stored. One more thing, you can't call this method on main thread, so you may need to use Handler or AsyncTask for the purpose, Sorry I forgot to mention that.
Have a look at Room Training at Android Developers for implementation of basic functionality of room
#1 - Need to create a model class that matches with the result of the query
data class ClientAndCity(
#ColumnInfo(name="id") val id: Long,
#ColumnInfo(name="client_name") val clientName: String?,
#ColumnInfo(name="city_name") val cityName: String?
)
#2 - Inside your DAO create your query
#Query("SELECT clients.id, clients.name AS client_name, cities.name AS city_name FROM clients LEFT JOIN cities ON cities.id = clients.city_id WHERE clientes.id = :clientId")
fun getClientAndHisCity(clientId: Long): ClientAndCity?
#3 - Use you function
CoroutineScope(Dispatchers.IO).launch{
val result: ClientAndCity = clientDAO.getClientAndHisCity(clientId)
//do something with it
}
I'm trying to convert the following SQL statement into DBFlow method calls:
SELECT t.SSID, t.BSSID, t.Latitude, t.Longitude, t.Timestamp
FROM wlan_events t
INNER JOIN (SELECT BSSID, MAX(Timestamp) AS MaxTimestamp FROM wlan_events GROUP BY BSSID) groupedt
ON t.BSSID = groupedt.BSSID AND t.Timestamp = groupedt.MaxTimestamp
What I got so far:
SQLite.select(WifiEvent_Table.SSID, WifiEvent_Table.BSSID, WifiEvent_Table.latitude,
WifiEvent_Table.longitude)
.from(WifiEvent.class)
.as("t")
.innerJoin(WifiEvent.class) // ????
;
How do i create that inner join's select statement using dbflow?
This is what I found:
SELECT EMP_ID, NAME, DEPT FROM COMPANY LEFT OUTER JOIN DEPARTMENT
ON COMPANY.ID = DEPARTMENT.EMP_ID
in DBFlow:
SQLite.select(Company_Table.EMP_ID, Company_Table.DEPT)
.from(Company.class)
.leftOuterJoin(Department.class)
.on(Company_Table.ID.withTable().eq(Department_Table.EMP_ID.withTable()))
.queryList();
Hope this helps: (Link updated)
https://agrosner.gitbooks.io/dbflow/content/