I was trying to create a database view in android but getting this error:
error: There is a problem with the query: [SQLITE_ERROR] SQL error or
missing database (no such table: ChatsView)
public abstract androidx.lifecycle.LiveData>
getMessageViewLIst();
Please help me with this that how can i get data from a DatabaseView in room persistence android
DatabaseView Class
#DatabaseView("SELECT chats.username as username," +
"(SELECT chat.message FROM ChatEntityModel as chat WHERE chat.username=chats.username ORDER BY id DESC LIMIT 1) as lastMsg" +
"(SELECT chat.timeStamp FROM ChatEntityModel as chat WHERE chat.username=chats.username ORDER BY id DESC LIMIT 1) as lastMsgTime " +
"(SELECT count(1) FROM ChatEntityModel as chat WHERE chat.username=chats.username and status=4 ) as totalNewMsgCount From ChatEntityModel as chats",viewName = "ChatsView")
data class ChatsView (
val username:String,
val lastMsg:String,
val lastMsgTime:String,
val totalNewMsgCount:String
)
Entity Class
#Entity(tableName = "ChatEntityModel")
data class ChatEntityModel (
#ColumnInfo(name = "message") var KEY_MESSAGE: String,
#ColumnInfo(name = "username") var KEY_USERNAME : String,
#ColumnInfo(name = "msgsend") var KEY_MSG_SEND : Boolean,
#ColumnInfo(name = "timeStamp") var KEY_TIMESTAMP :String,
#ColumnInfo(name = "status") var KEY_STATUS :Int,
#ColumnInfo(name = "msgid") var KEY_MSG_ID :String,
#ColumnInfo(name = "timestamp_delivered") var KEY_TIME_DELIVERED :String,
#ColumnInfo(name = "timestamp_read") var KEY_TIME_READ :String,
#ColumnInfo(name = "progress") var KEY_PROGRESS :String,
#ColumnInfo(name = "type") var KEY_TYPE : String
){
#PrimaryKey(autoGenerate = true)#ColumnInfo(name = "id")var KEY_ID:Int=0
}
DaoAccess class
#Database(entities = [(ChatEntityModel::class)], views = [(ChatsView::class)], version = 1,
exportSchema = false)
abstract class DaoDatabaseAccess:RoomDatabase() {
abstract fun ChattingDao():ChattingDao
// abstract fun ChattViewDao():ChattViewDao
}
Dao
#Dao
interface ChattingDao {
#Insert(onConflict = OnConflictStrategy.ABORT)
fun insertChatList(chats: List<ChatEntityModel>)
#Insert(onConflict = OnConflictStrategy.ABORT)
fun inserChat(chats: ChatEntityModel)
#Update
fun updateMovie(chats: ChatEntityModel)
#Query("SELECT * FROM ChatEntityModel WHERE id = :id_")
fun getMovie(id_: Int): ChatEntityModel
#Query("SELECT * FROM ChatEntityModel WHERE username=:Jid ORDER BY id ASC")
fun getChatList(Jid:String?): LiveData<List<ChatEntityModel>>
#Query("Update ChatEntityModel SET status=2 , timestamp_delivered=:timeDelivered WHERE msgid=:msg_id
and status <> 3 and username=:Jid ")
fun setChatDelivered(timeDelivered: String?,msg_id:String?,Jid: String?)
#Query("SELECT * FROM ChatEntityModel WHERE status=0 ORDER BY id LIMIT 1" )
fun getUnsentMessage(): List<ChatEntityModel>
#Query("SELECT msgid FROM ChatEntityModel WHERE status=4 and username=:username ORDER BY id" )
fun getUnReadMessage(username:String): List<String>
#Query("UPDATE ChatEntityModel SET status=1 WHERE msgid= :msgId and status=0")
fun setMessageSent(msgId: String?)
#Query("SELECT * FROM ChatEntityModel WHERE msgid =:msgId")
fun checkIfExists(msgId:String?): List<ChatEntityModel>
#Query("Update ChatEntityModel SET status=3 , timestamp_read=:currentTimeMillis WHERE msgid in (:receiptId) and username=:Jid ")
fun setChatRead(currentTimeMillis: String?, receiptId:List<String>,Jid: String?)
#Query("SELECT * FROM ChatEntityModel WHERE status=4 and username=:Jid ")
fun getUnReadChats(Jid: String?):LiveData<List<ChatEntityModel>>
#Query("UPDATE ChatEntityModel set status=5 WHERE status=4 and msgid in (:chat) ")
fun setChatReceivedRead(chat:List<String>)
#Query("SELECT * FROM ChatsView")
fun getMessageViewLIst(): LiveData<List<ChatsView>>
}
Maybe it's not related to your problem, but maybe it is, sometimes errors are weird. The request of your View as several SELECT which isn't correct, maybe try fixing your Query.
EDIT: For more clarity, the issue is that the Query was wrong and was missing comas.
You need to add the database name to the abstract RoomDatabase class
#Database(entities = [ChatsView::class], version = 1)
abstract class DaoDatabaseAccess:RoomDatabase() {
}
Related
I am trying this below, that I been writing. However, I am a bit new to this whole thing with Room. It does remind me of the Microsoft.Linq to some extent, however, the MS version is easier and more straightforward. Whereas this one is confusing a bit.
#Dao
interface AllDao {
// Account Data Access Object:
#Transaction
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertAccount(account: Account)
#Delete
suspend fun deleteAccount(account: Account)
#Update
suspend fun updateAccount(account: Account)
#Transaction
#Query("SELECT * FROM `accounts` WHERE email = :email")
suspend fun getAccountByEmail(email: String): Flow<Account?>
// Post Data Access Object:
#Transaction
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertPost(post: Post)
#Delete
suspend fun deletePost(post: Post)
#Update
suspend fun updatePost(post: Post)
#Transaction
#Query("SELECT * FROM `posts` WHERE post_id = :post_id")
suspend fun getPostById(post_id: Int, user_id: Int): Flow<Post?>
#Transaction
#Query("SELECT * FROM `posts` ORDER BY posts.title")
fun getPostsByUserId(uid: Int): Flow<List<Posts>>
#Transaction
#Query("SELECT * FROM `posts` ORDER BY posts.title WHERE posts.post_id = :post_id AND accounts._id = :user_id")
fun getUserPostSingle(post_id: Int, user_id: Int) : Flow<Post?>
/*
Account with Post Data Access Object:
*/
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(join: AccountWithPost)
}
Data structure: This is how I have setup the entities, however, this isn't as mentioned as straight forward as anticipated e.g., like Microsoft Linq.
#Entity(tableName = "accounts")
data class Account(
#PrimaryKey(autoGenerate = true)
#NonNull
val id: Int,
#ColumnInfo(name = "first_name")
val firstName: String,
#ColumnInfo(name = "last_name")
val lastName: String?,
val email: String
)
#Entity(
tableName = "posts",
foreignKeys = [ForeignKey(
entity = Account::class,
parentColumns = ["id"],
childColumns = ["postUserId"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)]
)
data class post(
#PrimaryKey(autoGenerate = true)
#NonNull
val post_id: Int,
val title: String,
val content: String,
)
data class AccountWithPost(
#Embedded
var account: Account,
#Relation(
parentColumn = "id",
entity = Post::class,
entityColumn = "postUserId"
)
var posts: List<Post>,
)
You have a few issues, the most important as per the comments, is that you need to have something to relate a Post with it's PARENT account.
Another issue is that you appear to consider that AccountWithPost is a table (and you try to insert into this). It is not a table, rather it is a container that due to the #Relation annotation will retrieve an Account (the #Embedded) along with all the related Posts according to the ParentColumn and the Child Column (which is effectively the join).
Here's a working example (note without Flows/Suspends i.e. run on the mainThread for brevity/convenience).
The example (designed to just run once):-
adds 3 accounts and then
adds 5 posts to the first (Fred Bloggs)
adds 2 posts to the second account (Mary Smith)
adds 1 post to the third account (Jane Doe)
finally extracts everything as a List of AccountWithPosts
All of your classes PLUS an #Database annotated class :-
#Entity(tableName = "accounts")
data class Account(
#PrimaryKey(autoGenerate = true)
#NonNull
val id: Int,
#ColumnInfo(name = "first_name")
val firstName: String,
#ColumnInfo(name = "last_name")
val lastName: String?,
val email: String
)
#Entity(
tableName = "posts",
foreignKeys = [ForeignKey(
entity = Account::class,
parentColumns = ["id"],
childColumns = ["postUserId"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)]
)
data class Post /* Changed to Post from post */(
#PrimaryKey(autoGenerate = true)
#NonNull
val post_id: Int,
#ColumnInfo(index = true) /* Index on FK column (else warning issued) */
val postUserId: Int, /*<<<<<<<<<< Added the Userid that is the parent to the post IMPORTANT */
val title: String,
val content: String,
)
data class AccountWithPost(
#Embedded
var account: Account,
#Relation(
parentColumn = "id",
entity = Post::class,
entityColumn = "postUserId"
)
var posts: List<Post>,
)
#Dao
interface AllDao {
// Account Data Access Object:
//#Transaction will be in a single transaction anyway
#Insert(onConflict = OnConflictStrategy.IGNORE)
/*suspend*/ fun insertAccount(account: Account): Long /* Returns the rowid aka id of the inserted Account */
#Delete
/*suspend*/ fun deleteAccount(account: Account): Int /* Returns how many rows have been deleted */
#Update
/*suspend*/ fun updateAccount(account: Account): Int /* Returns how many rows have been updated */
//#Transaction will be in a single transaction anyway
#Query("SELECT * FROM `accounts` WHERE email = :email")
/*suspend*/ fun getAccountByEmail(email: String): /*Flow<Account?>*/ List<Account> /*changed for demo on main thread */
// Post Data Access Object:
//#Transaction
#Insert(onConflict = OnConflictStrategy.REPLACE)
/*suspend*/ fun insertPost(post: Post): Long /* id of inserted row */
#Delete
/*suspend*/ fun deletePost(post: Post): Int
#Update
/*suspend*/ fun updatePost(post: Post): Int
#Transaction
#Query("SELECT * FROM `posts` WHERE post_id = :post_id")
/*suspend*/ fun getPostById(post_id: Int/*, user_id: Int UNUSED */): /*Flow<Post?>*/ List<Post>
#Transaction
#Query("SELECT * FROM `posts` /* ADDED */ WHERE postUserId=:uid /* END OF ADDED*/ ORDER BY posts.title")
fun getPostsByUserId(uid: Int): /*Flow<List<Post>>*/ List<Post>
#Transaction
#Query("SELECT * FROM `posts` WHERE posts.post_id = :post_id AND postUserId = :user_id /* CHANGED to use postUserId columns */ ORDER BY posts.title")
fun getUserPostSingle(post_id: Int, user_id: Int) : /*Flow<Post?>*/ List<Post>
/*
Account with Post Data Access Object:
Account With Post is NOT a table, a Post contains the reference
Commented out
*/
//#Insert(onConflict = OnConflictStrategy.IGNORE)
//fun insert(join: AccountWithPost)
#Transaction
#Query("SELECT * FROM accounts")
fun getAllAccountsWithTheirPosts(): List<AccountWithPost>
}
#Database(entities = [Account::class,Post::class], exportSchema = false, version = 1)
abstract class TheDatabase: RoomDatabase() {
abstract fun getAllDao(): AllDao
companion object {
private var instance: TheDatabase? = null
fun getInstance(context: Context): TheDatabase {
if (instance==null) {
instance = Room.databaseBuilder(context,TheDatabase::class.java,"the_database.db")
.allowMainThreadQueries()
.build()
}
return instance as TheDatabase
}
}
}
Please refer to the comments contained in the code
In addition to the above the code in the Activity (MainActivity) is :-
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: AllDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = TheDatabase.getInstance(this)
dao = db.getAllDao()
val fbId = dao.insertAccount(Account(0,"Fred","Blogs","fred_bloggs#email.com"))
val msId = dao.insertAccount(Account(0,"Mary","Smith","m_smith#email.com"))
val jdId = dao.insertAccount(Account(0,"Jane","Doe","jane_doe#email.com"))
dao.insertPost(Post(0,fbId.toInt(),"FBP001","blah for fb p001"))
dao.insertPost(Post(0,fbId.toInt(),"FBP002","blah for fb p002"))
dao.insertPost(Post(0,fbId.toInt(),"FBP003","blah for fb p003"))
dao.insertPost(Post(0,fbId.toInt(),"FBP004","blah for fb p004"))
dao.insertPost(Post(0,fbId.toInt(),"FBP005","blah for fb p005"))
dao.insertPost(Post(0,msId.toInt(),"MSP001","blah for ms p001"))
dao.insertPost(Post(0,msId.toInt(),"MSP002","blah for ms p002"))
dao.insertPost(Post(0,jdId.toInt(),"JDP001","blah for jd p001"))
val sb = StringBuilder()
for(awp in dao.getAllAccountsWithTheirPosts()) {
sb.clear()
for (p in awp.posts) {
sb.append("\n\tPost Title is ${p.title} Content is ${p.content} ID is ${p.post_id} References User ${p.postUserId}")
}
Log.d("DBINFOI","Account FirstName is ${awp.account.firstName} " +
"Lastname is ${awp.account.lastName} " +
"Email is ${awp.account.email} ID is ${awp.account.id} " +
"The account has ${awp.posts.size} posts, if any they are:-$sb"
)
}
}
}
Result
The output sent to the log is:-
D/DBINFOI: Account FirstName is Fred Lastname is Blogs Email is fred_bloggs#email.com ID is 1 The account has 5 posts, if any they are:-
Post Title is FBP001 Content is blah for fb p001 ID is 1 References User 1
Post Title is FBP002 Content is blah for fb p002 ID is 2 References User 1
Post Title is FBP003 Content is blah for fb p003 ID is 3 References User 1
Post Title is FBP004 Content is blah for fb p004 ID is 4 References User 1
Post Title is FBP005 Content is blah for fb p005 ID is 5 References User 1
D/DBINFOI: Account FirstName is Mary Lastname is Smith Email is m_smith#email.com ID is 2 The account has 2 posts, if any they are:-
Post Title is MSP001 Content is blah for ms p001 ID is 6 References User 2
Post Title is MSP002 Content is blah for ms p002 ID is 7 References User 2
D/DBINFOI: Account FirstName is Jane Lastname is Doe Email is jane_doe#email.com ID is 3 The account has 1 posts, if any they are:-
Post Title is JDP001 Content is blah for jd p001 ID is 8 References User 3
There are a bunch of questions like this in StackOverflow but most of that arent about room database, so I had to ask a new question.
I have an app that uses room database and that has near 4 tables and a big relationship between those tables, so for instance when I delete a user in user list fragment, that user delete(only userName and some personal info) but the user's TRANSACTIONS and LOANS hadn't been deleted.
Someone told me I have to use Cascade delete but I didn't find much info about it.
My User class model:
#Entity(tableName = "user_info")
data class UserInfo(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "user_id")
var userId: Long =0L,
#ColumnInfo(name = "full_name")
var fullName:String?,
#ColumnInfo(name= "account_id")
var accountId: String?,
#ColumnInfo(name = "mobile_number")
var mobileNumber:String?,
#ColumnInfo(name = "phone_number")
var phoneNumber:String?,
#ColumnInfo(name = "date_of_creation")
var dateOfCreation:String?,
#ColumnInfo(name = "address")
var address:String?,
)
Transactions model class:
#Entity(tableName = "transactions")
data class Transactions(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "trans_id")
var transId: Long = 0L,
#ColumnInfo(name = "user_id")
var userId: Long?,
#ColumnInfo(name = "create_date")
var createDate: String?,
#ColumnInfo(name = "bank_id")
var bankId: Long?,
#ColumnInfo(name = "description")
var description: String?,
#ColumnInfo(name = "increase")
var increase: String?,
#ColumnInfo(name = "decrease")
var decrease: String?,
#ColumnInfo(name = "loan_number")
var loanNumber: String?,
#ColumnInfo(name = "total")
var total: Long?,
#ColumnInfo(name = "type")
var type: String?
)
User DAO:
#Insert
suspend fun insert(ui: UserInfo): Long
#Update
suspend fun update(ui: UserInfo)
#Insert
suspend fun insertList(ui: MutableList<UserInfo>)
#Delete
suspend fun deleteUser(ui: UserInfo)
#Query("DELETE FROM user_info")
fun deleteAllUser()
#Query("SELECT user_info.user_id, user_info.full_name, transactions.total From user_info JOIN transactions ")
fun joinTable(): LiveData<List<UserAndMoney>>?
#Query("SELECT * from user_info WHERE user_id = :key")
fun get(key: Long): LiveData<UserInfo>?
#Query("SELECT * FROM user_info ORDER BY full_name DESC")
fun getAllUserInfo(): LiveData<List<UserInfo>>
#Query("SELECT * FROM user_info where full_name like '%' || :fullName || '%' ORDER BY full_name ASC")
fun searchUserName(fullName: String): LiveData<List<UserInfo>>
If It was not clear for you till now, let me makes it easy for you:
I need cascade delete that delets every thing about user and a record.
CASCADE is an option of a Foreign Key constraint. So you would need to define Foreign Key constraints. You define Foreign Key constraints in Room via the #Entity annotation.
As an example, as it would appear that a Transactions is related to a UserInfo via var userId: Long?, (column name user_id) you could have :-
#Entity(tableName = "transactions",
foreignKeys = [
ForeignKey(
entity = UserInfo::class,
parentColumns = ["user_id"],
childColumns = ["user_id"],
onDelete = ForeignKey.CASCADE, //<<<<<
onUpdate = ForeignKey.CASCADE // Optional
)
]
)
data class Transactions(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "trans_id")
var transId: Long = 0L,
#ColumnInfo(name = "user_id", index = true) // <<<<< best to have an index on the column, not required
var userId: Long?,
#ColumnInfo(name = "create_date")
var createDate: String?,
#ColumnInfo(name = "bank_id")
var bankId: Long?,
#ColumnInfo(name = "description")
var description: String?,
#ColumnInfo(name = "increase")
var increase: String?,
#ColumnInfo(name = "decrease")
var decrease: String?,
#ColumnInfo(name = "loan_number")
var loanNumber: String?,
#ColumnInfo(name = "total")
var total: Long?,
#ColumnInfo(name = "type")
var type: String?
)
Note
The constraint enforces referential integrity, that is a transaction can not be inserted/updated if the user_id value is not a value that exists in the user_id column of the user_info table.
The CASCADE onUpdate will cascade a change to the user_id value in the user_info table to the respective transactions.
Additional
Someone told me I have to use Cascade delete but I didn't find much info about it.
What you have been told is incorrect. You could replicate the functionality without ON DELETE CASCADE or without the Foreign Key constraint.
You could use
#Query("DELETE FROM transaction WHERE user_id=:userId")
fun cascadeDeletionsFromUser(userId: Long)
noting that if the Foreign Key constraint exists in the transactions table but didn't have an onDelete action specified, then the cascadeDeletionsFromUser function would have to be run before the user_info row is deleted. Otherwise the user_info row could not be deleted as the FK constraint would inhibit the deletion.
If you had an abstract class rather than interface then you could have:-
#Query("DELETE FROM user_info WHERE user_id=:userId")
abstract fun deleteUserById(userId: Long)
#Query("DELETE FROM transactions WHERE user_id=:userId")
abstract fun cascadeDeletionsFromUser(userId: Long)
#Transaction
#Query("")
fun deleteUserWithCascade(userId: Long) {
cascadeDeletionsFromUser(userId)
deleteUserById(userId)
}
and use the deleteUserWithCascade function to delete the transactions and user in one go.
It is more convenient to use ON DELETE CASCADE, and especially so if you have multiple depths of relationships (when it gets a little more complex ascertaining children)
I am using Room in my app as a Single Source of Truth, so everything that comes from the backend I save in my room database, which then returns a Flowable that fires an event every time the data changes. This is my PlacesDAO:
#Dao
abstract class PlacesDao {
#Query("select * from places where placeId = :id")
abstract fun getPlace(id: String): Flowable<PlaceVO>
#Query("select * from places where placeId in (:placesIds) order by distance, placeName ASC")
abstract fun getPlaces(placesIds: List<String>): Flowable<List<PlaceVO>>
#Query("select * from places join list_results where Places.placeId = list_results.id order by distance, placeName ASC")
abstract fun getFilteredPlaces(): Flowable<List<PlaceVO>>
#Query("select * from places join user_places where Places.placeId = user_places.placeId AND user_places.userId = :userId order by distance, placeName ASC ")
abstract fun getAllByUser(userId: String) : Flowable<List<PlaceVO>>
#Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun realSavePlaces(places:List<PlaceVO>)
fun savePlaces(places: List<PlaceVO>){
Timber.w("PAGELIST - Saving places again!!")
realSavePlaces(places)
}
#Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun savePlace(place: PlaceVO)
#Query("DELETE from places")
abstract fun deleteAll()
#Query("select * from places")
abstract fun getAll(): Single<List<PlaceVO>>
#Query("select * from places where (experienceId IS NOT NULL) AND (experienceId != '') order by placeName")
abstract fun getMyPlaces(): Flowable<List<PlaceVO>>
#Query("update places set distance = :distance and distanceSet = 1 where placeId = :id")
abstract fun updateDistance(id: String, distance: Float)
}
Now in my app theres a few actions that would trigger changing the data in this table, which then causes my UI to receive all items contained in the table (around 3000-5000).
It does niot always happen (which makes it hard to reproduce) but every now and then I will get the following crash:
Caused by android.database.sqlite.SQLiteBlobTooBigException: Row too big to fit into CursorWindow requiredPos=1223, totalRows=114
at android.database.sqlite.SQLiteConnection.nativeExecuteForCursorWindow(SQLiteConnection.java)
at android.database.sqlite.SQLiteConnection.executeForCursorWindow + 895(SQLiteConnection.java:895)
at android.database.sqlite.SQLiteSession.executeForCursorWindow + 836(SQLiteSession.java:836)
at android.database.sqlite.SQLiteQuery.fillWindow + 62(SQLiteQuery.java:62)
at android.database.sqlite.SQLiteCursor.fillWindow + 157(SQLiteCursor.java:157)
at android.database.sqlite.SQLiteCursor.onMove + 128(SQLiteCursor.java:128)
at android.database.AbstractCursor.moveToPosition + 237(AbstractCursor.java:237)
at android.database.AbstractCursor.moveToNext + 269(AbstractCursor.java:269)
at com.myapp.android.model.db.dao.PlacesDao_Impl$6.call + 814(PlacesDao_Impl.java:814)
at com.myapp.android.model.db.dao.PlacesDao_Impl$6.call + 771(PlacesDao_Impl.java:771)
at io.reactivex.internal.operators.maybe.MaybeFromCallable.subscribeActual + 46(MaybeFromCallable.java:46)
at io.reactivex.Maybe.subscribe + 4262(Maybe.java:4262)
at io.reactivex.internal.operators.flowable.FlowableFlatMapMaybe$FlatMapMaybeSubscriber.onNext + 132(FlowableFlatMapMaybe.java:132)
at io.reactivex.internal.operators.flowable.FlowableObserveOn$ObserveOnSubscriber.runAsync + 407(FlowableObserveOn.java:407)
at io.reactivex.internal.operators.flowable.FlowableObserveOn$BaseObserveOnSubscriber.run + 176(FlowableObserveOn.java:176)
at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker$BooleanRunnable.run + 260(ExecutorScheduler.java:260)
at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker.run + 225(ExecutorScheduler.java:225)
at java.util.concurrent.ThreadPoolExecutor.runWorker + 1167(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run + 641(ThreadPoolExecutor.java:641)
at java.lang.Thread.run + 764(Thread.java:764)
I am only storing text info, as proved by this class:
#Entity(tableName = "places")
data class PlaceVO(
#PrimaryKey
var placeId: String,
var googleId: String,
var placeName: String,
var phoneNumber: String,
#Embedded
var primaryCategory: Category?,
var primaryCategoryTags: List<CategoryTag> = emptyList(),
var secondaryCategories: List<Category>? = emptyList(),
var images: List<Image>,
var website: String,
var formattedAddress: String? = "",
var vicinity: String = "",
var vicinityShort: String = "",
var city: String? = "",
var neighbourhood: String?,
var longitude: Double,
var latitude: Double,
var openingHours: List<String>,
var combinedHighlights: List<HighlightCountWrapper>,
#Embedded
var ownExperience: OwnExperience?,
var otherExperiences: List<Experience>,
var distance: Float?,
var distanceSet: Boolean = false,
var comment: String
) : MarkerPlace {
}
Experience class:
#Entity
data class Experience(
#Json(name="id")
val experienceId: String,
#Embedded
val owner: User,
val description: String?,
val highlights: List<Highlight>?,
val images: List<Image> = emptyList(),
val createdDate: Date,
val updatedDate: Date,
var privacyLevel: AddExperience.Privacy? = null)
Some TypeConverters:
#TypeConverter
fun toHighlightWrapperList(value: String): List<HighlightCountWrapper> {
val type = Types.newParameterizedType(List::class.java, HighlightCountWrapper::class.java)
return moshi.adapter<List<HighlightCountWrapper>>(type).fromJson(value) ?: emptyList()
}
#TypeConverter
fun fromHighlightWrapperList(list: List<HighlightCountWrapper>): String {
val type = Types.newParameterizedType(List::class.java, HighlightCountWrapper::class.java)
var adapter: JsonAdapter<List<HighlightCountWrapper>> = moshi.adapter<List<HighlightCountWrapper>>(type)
return adapter.toJson(list)
}
#TypeConverter
fun toExperienceList(value: String): List<Experience> {
val type = Types.newParameterizedType(List::class.java, Experience::class.java)
return moshi.adapter<List<Experience>>(type).fromJson(value) ?: emptyList()
}
#TypeConverter
fun fromExperienceList(list: List<Experience>): String {
val type = Types.newParameterizedType(List::class.java, Experience::class.java)
var adapter: JsonAdapter<List<Experience>> = moshi.adapter<List<Experience>>(type)
return adapter.toJson(list)
}
#TypeConverter
fun toImageList(value: String): List<Image> {
val type = Types.newParameterizedType(List::class.java, Image::class.java)
return moshi.adapter<List<Image>>(type).fromJson(value) ?: emptyList()
}
#TypeConverter
fun fromImageList(list: List<Image>): String {
val type = Types.newParameterizedType(List::class.java, Image::class.java)
var adapter: JsonAdapter<List<Image>> = moshi.adapter<List<Image>>(type)
return adapter.toJson(list)
}
so how can it be that my rows are too big for SQLite? Especially when sometimes exactly the same data will be returned without a problem?
I found how to use length() and substr() to request only 1MB (the max for CursorWindow is 2MB), maybe it will help. (in your case you could simply divide the requests into chunks of 100 rows and then close the cursor, then repeat)
It seems that you are storing images, in which case it would be better to store them in internal storage, and only store the file path in the database.
Especially when sometimes exactly the same data will be returned without a problem?
If you mean exactly the same rows (like the rows from 1000 to 2000), but from different devices, it might be the case that they have different "max-sizes" for the CursorWindow, in my case it seems to be 2MB.
I'd like to set my own conflict columns not default primary key (just an auto increment integer in my case). So my table is:
#Entity(tableName = "users_tab")
data class User(
#PrimaryKey(autoGenerate = true)
var id: Int,
var str1: String,
var str2: String,
var str3: String
)
#Dao
interface UserDAO {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(users: List<User>)
}
I'd like to REPLACE when User has same str1 and str2 how should I modify my Insert adnotation?
I followed this post Android Room FOREIGN KEY constraint failed (code 787). Unfortunately, it not works in my case.
I always get the error when trying to insert a new Note. Of course, there is a Topic existed in Database.
Do you guys have any idea?
Please take a look at my code below.
Topic:
#Entity(tableName = "topic")
data class Topic(#PrimaryKey var id: Long? = null,
#ColumnInfo(name = "name") var name: String,
#ColumnInfo(name = "note_count") var noteCount: Int = 0,
#ColumnInfo(name = "view_count") var viewCount: Long = 0) : Serializable {
#Ignore
var notes: List<Note>? = null
}
Note:
#Entity(tableName = "note",
foreignKeys = arrayOf(ForeignKey(entity = Topic::class,
parentColumns = arrayOf("id"), childColumns = arrayOf("topic_id"), onDelete = ForeignKey.CASCADE)))
#TypeConverters(TimestampConverter::class)
data class Note(#PrimaryKey(autoGenerate = true) var noteId: Long? = null,
#ColumnInfo(name = "topic_id") var topicId: Long? = null,
#ColumnInfo(name = "title") var title: String,
#ColumnInfo(name = "caption") var caption: String? = "",
#ColumnInfo(name = "created_at") var createdAt: Date? = null) : Serializable {
}
NoteDao:
#Dao
interface NoteDao {
#Query("SELECT * from note")
fun getAll(): LiveData<List<Note>>
#Insert(onConflict = REPLACE)
fun insert(note: Note)
#Query("DELETE from note")
fun deleteAll()
#Delete
fun delete(category: Note)
#Query("SELECT * FROM note WHERE title = :title COLLATE NOCASE LIMIT 1")
fun findNoteByTitle(title: String): Note?
#Query("SELECT * FROM note WHERE topic_id = :topicId")
fun findNotesByTopicId(topicId: Long): LiveData<List<Note>>
#Query("SELECT count(*) FROM note WHERE topic_id = :topicId")
fun countNotesByTopicId(topicId: Long): Long
#Update(onConflict = IGNORE)
fun update(category: Note)
}
I found the problem. Because I have been created 2 different Databases: TopicDataBase and NoteDataBase.
So I just need to remove 1 of them.
My bad >"<
(#PrimaryKey var id: Long? = null, looks like a problem. Primary key can't be null