agrosner / DBFlow

A blazing fast, powerful, and very simple ORM android database library that writes database code for you.
MIT License
4.87k stars 598 forks source link

Add logging to insert and update #607

Closed freshteapot closed 8 years ago

freshteapot commented 8 years ago

Today, it is possible to get logging via:

FlowLog.setMinimumLoggingLevel(FlowLog.Level.V);

for:

It would be great to get logging for:

Looking into the code. I think we just need to add the following:

        if (FlowLog.isEnabled(FlowLog.Level.V)) {
            FlowLog.log(FlowLog.Level.V, queryBuilder.getQuery());
        }

To.

gauravsak commented 8 years ago

If you have already investigated over the issue, could you please submit a pull request? Thanks.

agrosner commented 8 years ago

you simply subclass the ModelSaver class and provide your own implementation which logs what you need.

zmorris commented 6 years ago

@agrosner I'm trying to add global insert and update logging as well. Here is a breadcrumb with another example and more information, but unfortunately I can't get it to work (I'm using DBFlow 3.1.1):

https://github.com/Raizlabs/DBFlow/issues/919

Here is what I have so far:

public class MyModelSaver extends ModelSaver<Model, Model, ModelAdapter<Model>> {
//    public MyModelSaver() {
//        setAdapter(FlowManager.getModelAdapter(Model.class));
//        setModelAdapter(FlowManager.getModelAdapter(Model.class));
//    }

    @Override
    @SuppressWarnings("unchecked")
    public synchronized boolean update(@NonNull Model model, @NonNull DatabaseWrapper wrapper,
                                       @NonNull ContentValues contentValues) {
        return super.update(model, wrapper, contentValues);
    }

    @Override
    @SuppressWarnings("unchecked")
    public synchronized long insert(@NonNull Model model, @NonNull DatabaseStatement insertStatement) {
        return super.insert(model, insertStatement);
    }

    @Override
    @SuppressWarnings("unchecked")
    public synchronized boolean delete(@NonNull Model model, @NonNull DatabaseWrapper wrapper) {
        return super.delete(model, wrapper);
    }
}

//public class MyModelSaver<TModel extends Model, TTable extends Model,
//        TAdapter extends RetrievalAdapter & InternalAdapter> extends ModelSaver<TModel, TTable, TAdapter> {
////    public MyModelSaver() {
////        setAdapter(FlowManager.getModelAdapter(Model.class));
////        setModelAdapter(FlowManager.getModelAdapter(Model.class));
////    }
//
//    @Override
//    @SuppressWarnings("unchecked")
//    public synchronized boolean update(@NonNull TTable model, @NonNull DatabaseWrapper wrapper,
//                                       @NonNull ContentValues contentValues) {
//        return super.update(model, wrapper, contentValues);
//    }
//
//    @Override
//    @SuppressWarnings("unchecked")
//    public synchronized long insert(@NonNull TTable model, @NonNull DatabaseStatement insertStatement) {
//        return super.insert(model, insertStatement);
//    }
//
//    @Override
//    @SuppressWarnings("unchecked")
//    public synchronized boolean delete(@NonNull TTable model, @NonNull DatabaseWrapper wrapper) {
//        return super.delete(model, wrapper);
//    }
//}

I've tried all permutations of the above but can never get it to compile.

I've also tried creating an anonymous instance of ModelSaver and overriding its insert(), update() and delete() methods so that I can add my own logging:

FlowManager.init(new FlowConfig.Builder(this).build());
{
    for (Class<? extends Model> model : FlowManager.getDatabase("database").getModelClasses()) {
        FlowManager.getModelAdapter(model).setModelSaver(new ModelSaver<Model, Model, ModelAdapter<Model>>() {
            @Override
            @SuppressWarnings("unchecked")
            public synchronized boolean update(@NonNull Model model, @NonNull DatabaseWrapper wrapper, @NonNull ContentValues contentValues) {
                return super.update(model, wrapper, contentValues);
            }

            @Override
            @SuppressWarnings("unchecked")
            public synchronized long insert(@NonNull Model model, @NonNull DatabaseStatement insertStatement) {
                return super.insert(model, insertStatement);
            }

            @Override
            @SuppressWarnings("unchecked")
            public synchronized boolean delete(@NonNull Model model, @NonNull DatabaseWrapper wrapper) {
                return super.delete(model, wrapper);
            }
        });
    }

    FlowLog.setMinimumLoggingLevel(FlowLog.Level.V);
}

No matter what I try, I always get:

Error:(73, 66) error: incompatible types: <anonymous ModelSaver<Model,Model,ModelAdapter<Model>>> cannot be converted to ModelSaver<CAP#1,CAP#1,ModelAdapter<CAP#1>>
where CAP#1 is a fresh type-variable:
CAP#1 extends Model from capture of ? extends Model

There is little to no information about this on the web. This was the only substantial answer I could find:

https://stackoverflow.com/a/20548655/539149

So it seems that generics are where Java really falls down (coincidentally that's the reason I left C++).

Here is another way to (possibly) log queries through ADB:

https://stackoverflow.com/questions/5966584/logging-sql-queries-in-android

But I need to be able to set a breakpoint where each query is called so that I can isolate an elusive TypeConverter issue.

If someone knows how to override ModelSaver, please post a solution thanks. Here are all the links I have on this:

https://github.com/Raizlabs/DBFlow/issues/422 https://github.com/Raizlabs/DBFlow/issues/820 https://github.com/Raizlabs/DBFlow/issues/677 https://self-learning-java-tutorial.blogspot.com/2014/03/wild-card-capture.html https://stackoverflow.com/questions/39684999/incompatible-types-somex-cannot-be-converted-to-cap1 https://stackoverflow.com/questions/21569868/try-to-do-typecast-but-getting-cap1-error

zmorris commented 6 years ago

I managed to cobble this together. It's not ideal, because it sets the ModelSaver inside getModelAdapter(). But this at least provides places where breakpoints can be set to log all insert/update/delete:

// BaseModel inherited by all other models in the project
public abstract class MyBaseModel extends BaseModel {
    @Override
    public void insert() {
        super.insert();
    }

    @Override
    public void update() {
        super.update();
    }

    @Override
    public void delete() {
        super.delete();
    }

    private class MyModelSaver extends ModelSaver<BaseModel, BaseModel, ModelAdapter<BaseModel>> {
        @Override
        @SuppressWarnings("unchecked")
        public synchronized long insert(@NonNull BaseModel model, @NonNull DatabaseStatement insertStatement) {
            ContentValues tempContentValues = new ContentValues();
            getAdapter().bindToContentValues(tempContentValues, model);

            Log.i("ModelSaver.insert()", getAdapter().getTableName() + " [" + tempContentValues.toString() + "]");

            return super.insert(model, insertStatement);
        }

        @Override
        @SuppressWarnings("unchecked")
        public synchronized boolean update(@NonNull BaseModel model, @NonNull DatabaseWrapper wrapper, @NonNull ContentValues contentValues) {
            ContentValues tempContentValues = new ContentValues();
            getAdapter().bindToContentValues(tempContentValues, model);

            Log.i("ModelSaver.update()", getAdapter().getTableName() + " [" + tempContentValues.toString() + "] where [" + getAdapter().getPrimaryConditionClause(model) + "]");

            return super.update(model, wrapper, contentValues);
        }

        @Override
        @SuppressWarnings("unchecked")
        public synchronized boolean delete(@NonNull BaseModel model, @NonNull DatabaseWrapper wrapper) {
            Log.i("ModelSaver.delete()", getAdapter().getTableName() + " where [" + getAdapter().getPrimaryConditionClause(model) + "]");

            return super.delete(model, wrapper);
        }
    }

    public ModelAdapter getModelAdapter() {
        ModelAdapter modelAdapter = super.getModelAdapter();

        if (!(modelAdapter.getModelSaver() instanceof MyModelSaver)) {
            MyModelSaver modelSaver = new MyModelSaver();

            modelSaver.setAdapter(modelAdapter);
            modelSaver.setModelAdapter(modelAdapter);

            modelAdapter.setModelSaver(modelSaver);
            modelAdapter.getListModelSaver();   // calls modelAdapter.createListModelSaver() which sets modelAdapter.listModelSaver.modelSaver = modelSaver
        }

        return modelAdapter;
    }
}

After writing this, I realized that probably all of the logic flows through MyBaseModel.insert(), MyBaseModel.update() and MyBaseModel.update() so I'm not sure that the ModelSaver is even necessary.

Also I recommend taking a look at ListModelSaver because it calls ModelSaver repeatedly (instead of doing multi-row operations via SQL) which is not performant in many cases. Maybe this has changed in DBFlow 4+ but I'm not certain.