Not able to update sqlite_sequence table using RoomDatabase.query - android

We try to update sqlite_sequence with the following code.
WeNoteRoomDatabase weNoteRoomDatabase = WeNoteRoomDatabase.instance();
weNoteRoomDatabase.query(new SimpleSQLiteQuery("UPDATE sqlite_sequence SET seq = 0 WHERE name = 'attachment'"));
However, it has no effect at all. I exam the sqlite_sequence table content using SQLite browser. The counter is not reset to 0.
If we try to run the same query manually using SQLite browser on same SQLite file, it works just fine.
Our Room database is pretty straightforward.
#Database(
entities = {Attachment.class},
version = 6
)
public abstract class WeNoteRoomDatabase extends RoomDatabase {
private volatile static WeNoteRoomDatabase INSTANCE;
private static final String NAME = "wenote";
public abstract AttachmentDao attachmentDao();
public static WeNoteRoomDatabase instance() {
if (INSTANCE == null) {
synchronized (WeNoteRoomDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(
WeNoteApplication.instance(),
WeNoteRoomDatabase.class,
NAME
)
.build();
}
}
}
return INSTANCE;
}
}
Any idea what we had missed out?
Additional information : clearing sqlite_sequence is not working in android room

Room doesn't use SQLiteDatabase - but it uses SupportSQLiteDatabase, while it's source code states, that it delegates all calls to an implementation of {#link SQLiteDatabase}... I could even dig further - but I'm convinced, that this is a consistency feature and not a bug.
SQLiteDatabase.execSQL() still works fine, but with SupportSQLiteDatabase.execSQL() the same UPDATE or DELETE queries against internal tables have no effect and do not throw errors.
my MaintenanceHelper is available on GitHub. it is important that one initially lets Room create the database - then one can manipulate the internal tables with SQLiteDatabase.execSQL(). while researching I've came across annotation #SkipQueryVerification, which could possibly permit UPDATE or DELETE on table sqlite_sequence; I've only managed to perform a SELECT with Dao... which in general all hints for the internal tables are being treated as read-only, from the perspective of the publicly available API; all manipulation attempts are being silently ignored.

i think query is wrong, you should try below query
weNoteRoomDatabase.query(new SimpleSQLiteQuery("UPDATE sqlite_sequence SET seq = 0 WHERE name = attachment"));

I'm using room database version 2.2.5
Here I'm unable to execute this query using Room Dao structure, so make one simple class and access method as like this and I got successful outcomes so this one is tested result. I'm using RxJava and RxAndroid for same.
public class SqlHelper {
private static SqlHelper helper = null;
public static SqlHelper getInstance() {
if (helper == null) {
helper = new SqlHelper();
}
return helper;
}
public Completable resetSequence(Context context) {
return Completable.create(emitter -> {
try {
AppDatabase.getDatabase(context)
.getOpenHelper()
.getWritableDatabase()
.execSQL("DELETE FROM sqlite_sequence WHERE name='<YOUR_TABLE_NAME>'");
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
});
}
}
Execute:
SqlHelper.getInstance()
.resetQuizSequence(context)
.subscribeOn(Schedulers.io()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(() -> {}, error -> {});

Table sql_sequence is not managed by Room, so you need to edit it using a SupportSQLiteDatabase.
Try this:
String sqlQuery = "DELETE FROM sqlite_sequence WHERE name='attachment'";
weNoteRoomDatabase().getOpenHelper().getWritableDatabase().execSQL(sqlQuery);

This works for me - Room 2.2.6
String sqlQuery = "DELETE FROM sqlite_sequence WHERE name='attachment'";
<YourDatabase>.getInstance(mContext).getOpenHelper().getWritableDatabase().execSQL(sqlQuery);

Related

How to do Room migration testing properly by inserting and testing data?

I'm trying to test Room DB after including a new Column date. But I'm not getting how to insert data as I cannot use context in Testing.java file.
my code looks like this,
#RunWith(AndroidJUnit4.class)
public class MigrationTest {
private static final String TEST_DB = "note_database";
#Rule
public MigrationTestHelper helper;
public MigrationTest() {
helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
NoteDatabase.class.getCanonicalName(),
new FrameworkSQLiteOpenHelperFactory());
}
#Test
public void migrateAll() throws IOException {
// Create earliest version of the database.
SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 2);
db.execSQL("INSERT INTO note_table (title,description,priority,date)" +
" VALUES ('title_test','description_test','priority_test','10/12/2019'); ");
db.close();
// Open latest version of the database. Room will validate the schema
// once all migrations execute.
NoteDatabase appDb = Room.databaseBuilder(
InstrumentationRegistry.getInstrumentation().getTargetContext(),
NoteDatabase.class,
TEST_DB)
.addMigrations(ALL_MIGRATIONS).build();
appDb.getOpenHelper().getWritableDatabase();
appDb.close();
}
// Array of all migrations
private static final Migration[] ALL_MIGRATIONS = new Migration[]{
MIGRATION_1_2};
}
This code was working fine but , look at this reference code. i need to do something like this.
and I'm not getting how to read getMigratedRoomDatabase data. can anyone help me on this.
// MigrationTestHelper automatically verifies the schema
//changes, but not the data validity
// Validate that the data was migrated properly.
User dbUser = getMigratedRoomDatabase().userDao().getUser();
assertEquals(dbUser.getId(), USER.getId());
assertEquals(dbUser.getUserName(), USER.getUserName());
// The date was missing in version 2, so it should be null in
//version 3
assertEquals(dbUser.getDate(), null);
That is nothing but appDb in your code. it will look like
appDb.userDao().getUser();

Save complex objects and their sons entities ORM Lite

I'm using ORM Lite on a project , I decided to use the facility to make the persistence part of the Web service once and can reuse it on Android.
But I am suffering a lot because possou complex objects that have multiple ForeignCollectionField and Foreign object , and at the hour of perssistir temenda these data is a headache, because I have to enter one by one of their children , I think the idea of ​​an ORM is make life easier , ie you have to persist the object and father and all the rest is done behind the scenes ...
Well, it is now too late to give up lite ORM , I wonder if there is a way to do what sitei above ..
I found a piece of code here
tried to implement but it seems not work , just keeps saving the parent object .
follows the function I'm trying to use , but do not know whether imports are correct because the code I found in the link above did not have this data
public int create(Object entity, Context context) throws IllegalArgumentException, IllegalAccessException, SQLException, SQLException {
try{
if (entity!=null){
// Class type of entity used for reflection
Class clazz = entity.getClass();
// Search declared fields and save child entities before saving parent.
for(Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
// Inspect annotations
Annotation[] annotations = field.getDeclaredAnnotations();
try{
for(Annotation annotation : annotations) {
// Only consider fields with the DatabaseField annotation
if(annotation instanceof DatabaseField) {
// Check for foreign attribute
DatabaseField databaseField = (DatabaseField)annotation;
if(databaseField.foreign()) {
// Check for instance of Entity
Object object = field.get(entity);
Dao gDao = getDatabase(context).getDao(object.getClass());
gDao.create(object);
}
}else if (annotation instanceof ForeignCollectionField){
Object object = field.get(entity);
for(Object obj : new ArrayList<Object>((Collection<?>)object)){
Class c = obj.getClass();
Dao gDao = getDatabase(context).getDao(obj.getClass());
gDao.create(obj);
}
}
}
}catch (NullPointerException e){
e.printStackTrace();
}
}
// Retrieve the common DAO for the entity class
Dao dao = getDatabase(context).getDao(entity.getClass());
// Persist the entity to the database
return dao.create(entity);
}else
return 0;
}finally {
if (database != null) {
OpenHelperManager.releaseHelper();
database = null;
}
}
}
Leveraging the same post, also need a colução to delete cascade, imagine a situation where I have the following tables:
Company > Category> person> contact> Phone and email
Deleting and now I do as described in the documentation:
public int deleteCascade(Prefeitura prefeitura, Context context){
try{
Dao<Prefeitura, Integer> dao = getDatabase(context).getDao(Prefeitura.class);
DeleteBuilder db = dao.deleteBuilder();
db.where().eq("prefeitura_id", prefeitura.getId());
dao.delete(db.prepare());
// then call the super to delete the city
return dao.delete(prefeitura);
}catch (SQLException e){
e.printStackTrace();
}
return 0;
}
But the objects that are not directly linked the company would still be in the database, how could I do?
But without hacks, I want a clean code ...
I know ORM Lite really is lite, but one that saves the children create and delete cascade is essential for any ORM, hopefully for the next versions it is implemented, it is regrettable not have these features, for simple projects is very good, but in a complex project because a lot of headaches, I'm feeling on the skin.
Any help is welcome!

ORMLite request using SQLCipher

I have a problem with making request using these both libraries. I wanted to get all queries from my table Table_events with .queryforall() but then I saw this thread and tried to make query with rawQuery but it offers only queryRaw(). The request looks like this:
private Dao<Table_Events, Integer> tEventDao;
public DAOManager(final DatabaseHelper databaseHelper)
{
this.tEventDao= GettEventDAO(databaseHelper);
}
public String[] Getall()
{
GenericRawResults<String[]> rawResults = null;
try
{
rawResults = tEventDao.queryRaw("select * from tableEvents");
}
catch (SQLException e)
{
e.printStackTrace();
}
return resultArray;
}
any ideas how make a query?
Your GettEventDAO function is returning an object that conforms to the Dao interface of ORMLite. You would need to make sure the implementation of ORMLite is able to interface with SQLCipher for Android which would typically return a Cursor representing the resultset of your query.

how to delete a record from ORMLITE?

I need to delete a record from ORMLite Database I can delete a record by id using as below
#Override
public void Delete(int id) throws SQLException {
this.dao.deleteById(id);
}
but what if I have to delete a record from same table not by id but by name or any other field
I want something like
public void Deletefromcanteen(String name,MealType mealtype) {
this.dao.deletebyName(name);
}
what query should i write using querybuilder to delete a record where name = name and mealtype = say (lunch)
I tried something like this in my databasehelper class
public void deletefromcanteen(int id, String mealtype) {
try {
Dao<CanteenLog, Integer> canteenDao = getCanteen();
DeleteBuilder<CanteenLog, Integer> deleteBuilder = canteenDao
.deleteBuilder();
deleteBuilder.where().eq("user_id", id).and().eq("meal", mealtype);
canteenDao.delete(deleteBuilder.prepare());
} catch (Exception e) {
...
}
}
deleteBuilder.where().eq("FIELD_NAME", arg);
deleteBuilder.delete();
Update:
For example :
//Get helper
DatabaseHelper helper = OpenHelperManager.getHelper(App.getContext(), DatabaseHelper.class);
//get dao
Dao dao = helper.getDao(YOUR_CLASS.class);
//delete elements from table in field by arg
DeleteBuilder<CanteenLog, Integer> deleteBuilder = dao.deleteBuilder();
deleteBuilder.where().eq("FIELD_NAME", arg);
deleteBuilder.delete();
Good luck!
To save building a query, you can do a select to find the ID, then do a delete by ID. Thsi will be straightforward if you already have the select query set up for this table.
It can be done also like that
DatabaseHelper.getInstance().getDao(YourObject.class).delete(yourObjectInstance);
If you are using Kotlin, you will need to do something like this instead since the type inference is not currently working correctly for the fluent syntax:
val deleteBuilder = dao.deleteBuilder()
val where = deleteBuilder.where().eq("address", address)
deleteBuilder.setWhere(where)
deleteBuilder.delete()
If you already have an instance of the object (or object list) you want to delete, just do :
//Get helper
DatabaseHelper helper =
OpenHelperManager.getHelper(App.getContext(), DatabaseHelper.class);
// delete 'em !
helper.getDao(YourObject.class).delete(yourObjectInstance);
If you don't have the instance to delete or you want to do it SQL way(!):
//Get helper
DatabaseHelper helper =
OpenHelperManager.getHelper(App.getContext(), DatabaseHelper.class);
//get dao
Dao dao = helper.getDao(YOUR_CLASS.class);
//delete elements from table in field by arg
DeleteBuilder<CanteenLog, Integer> deleteBuilder = dao.deleteBuilder();
deleteBuilder.where().eq("FIELD_NAME", "FIELD_VALUE");
deleteBuilder.delete();
If you need a more complicated Where logic, then use deleteBuilder.where().or() or deleteBuilder.where().and() to add more constraints.

Robolectric: Testing with ormlite

I'm trying to test ORMLite DAOs with robolectric, but database behaviour is not the same as when it's used from my android app. My DAOs are working perfectly well on the android application.
Reading about robolectric shadows and debugging code, I encountered ShadowSQLiteOpenHelper (code here).
Does anyone know if this Shadow is enough to test ormlite daos? Or I have to create my own shadow to achieve that? Any clue/tip/suggestion/example here?
Thanks in advance.
Extra info:
Test method:
#Test
public void basicTest() throws SQLException {
assertNotNull(randomStringResource); // Injection of an android resource: OK
assertThat(randomStringResource, equalTo("Event")); // With correct value: OK
assertNotNull(eventDao); // Dao injection: OK
assertThat(eventDao.countOf(), equalTo(0L)); // Table empty: OK
Event e1 = new Event("e1", new Date());
eventDao.create(e1);
assertNotNull(e1.getId()); // ID generated by OrmLite: OK
assertThat(eventDao.countOf(), equalTo(1L)); // Table not empty: OK
assertThat("e1", equalTo(eventDao.queryForId(e1.getId()).getName())); // Query for inserted event: Throws exception
}
Some of the problems encountered running this test:
Errors querying entities with "camelCased" property names: error thrown at last line of test (related problem). Never had a problem like this running the android app.
When I changed one of these properties name (e.g., isEnabled to enabled) in order to avoid the camelCase problem, the previous error persisted... seems like memory database didn't apply the changes that I made on the entity.
Versions used:
Robolectric 1.1
OrmLite 4.41
Sorry for resurrecting your topic but I ran into the same problem.
I'm using OrmLite 4.45 and Robolectric 2.1.
In ShadowSQLiteCursor.java, cacheColumnNames method calls toLowerCase on each column name. So I decided to extend ShadowSQLiteCursor with my own (which doesn't call toLowerCase):
/**
* Simulates an Android Cursor object, by wrapping a JDBC ResultSet.
*/
#Implements(value = SQLiteCursor.class, inheritImplementationMethods = true)
public class ShadowCaseSensitiveSQLiteCursor extends ShadowSQLiteCursor {
private ResultSet resultSet;
public void __constructor__(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) {}
/**
* Stores the column names so they are retrievable after the resultSet has closed
*/
private void cacheColumnNames(ResultSet rs) {
try {
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
columnNameArray = new String[columnCount];
for(int columnIndex = 1; columnIndex <= columnCount; columnIndex++) {
String cName = metaData.getColumnName(columnIndex);
this.columnNames.put(cName, columnIndex - 1);
this.columnNameArray[columnIndex - 1] = cName;
}
} catch(SQLException e) {
throw new RuntimeException("SQL exception in cacheColumnNames", e);
}
}
}
My answer obviously comes too late but may help others!

Categories

Resources