GithubHelp home page GithubHelp logo

bod / android-contentprovider-generator Goto Github PK

View Code? Open in Web Editor NEW
618.0 54.0 151.0 961 KB

A tool to generate Android ContentProviders.

License: GNU General Public License v3.0

Java 82.65% Shell 0.08% FreeMarker 15.53% Groovy 1.73%
android android-contentprovider android-development

android-contentprovider-generator's Introduction

Android ContentProvider Generator (acpg)

Android Arsenal

A tool to generate Android ContentProviders. It takes a set of entity (a.k.a "table") definitions as the input, and generates:

  • a ContentProvider class
  • an SQLiteOpenHelper class
  • one Columns class per entity
  • one Cursor class per entity
  • one ContentValues class per entity
  • one Selection class per entity
  • one Model interface per entity
  • one Bean class per entity (optionally)

Usage

There are two possible ways to generate the code:

  1. as part of the build script (with a Gradle plugin)
  2. as a one-time step (using a command line tool)

The Gradle plugin is perhaps the 'cleaner' way in the sense that the generated code won't be part of the source (not checked into VCS). The configuration is declared inside the Gradle script which allows to update it easily.

Alternatively, a one-time generation can be done (typically at the beginning of the project.) The generated code is part of the source and checked into VCS: this allows you to modify it if you need to.

You can decide which option is the best for your project :)

Option 1: Gradle plugin

Add this to your app's build.gradle:

buildscript {
    dependencies {
        classpath 'org.jraf:acpg-gradle-plugin:1.13.1'
    }
}

apply plugin: 'org.jraf.acpg.gradleplugin'

(...)

// This is where you declare a few parameters used to generate the code
acpg {
    // Where to find the entity files (see 'Entity files' below)
    // Optional - default value: 'etc/acpg' in the root project
    entitiesDir file('etc/acpg-entities')

    // Java package in which all the code will be generated
    providerJavaPackage 'com.example.app.provider'

    // ContentProvider authority
    // "${applicationId}" will be substituted by BuildConfig.APPLICATION_ID in the generated code
    authority '${applicationId}.provider'

    // Name of the provider class
    providerClassName 'ExampleProvider'

    // Name of the db file
    databaseFileName 'example.db'

    // Version of the db
    databaseVersion 1

    // Name of the SQLiteOpenHelper class
    // Optional - default value: providerClassName + "SQLiteOpenHelper"
    sqliteOpenHelperClassName 'ExampleSQLiteOpenHelper'

    // Name of a subclass of BaseSQLiteOpenHelperCallbacks
    // Optional - this allows you to get called when the db is opened/created/upgraded
    sqliteOpenHelperCallbacksClassName 'ExampleSQLiteOpenHelperCallbacks'

    // Whether to enable foreign keys support (see 'Advanced usage' below)
    // Optional - default value: false
    enableForeignKeys true

    // Whether @Nullable/@NonNull annotations will be used in the generated code
    // Optional - default value: false
    useAnnotations true

    // Whether support library classes are used or the Android SDK ones (e.g. CursorLoader)
    // Optional - default value: false
    useSupportLibrary true

    // Whether to generate a 'Beans' class for each entity
    // Optional - default value: true
    generateBeans true

    // Name of a boolean field in BuildConfig to enable/disable debug logging in the generated code
    // Optional - default value: "DEBUG"
    debugLogsFieldName 'LOG_DEBUG_PROVIDER'

    // Version of the tool syntax (must be 4)
    // The allows to break the build immediately if an incompatible version of the tool is used. Safety first!
    // Optional - default value: 4
    syntaxVersion 4
}

Option 2: Command line tool

The configuration is the same, except you declare it in a file named _config.json in the same folder as the entity files.

Here is an example:

{
	"syntaxVersion": 4,
	"packageName": "com.example.app",
	"providerJavaPackage": "com.example.app.provider",
	"authority": "${applicationId}.provider",
	"providerClassName": "ExampleProvider",
	"databaseFileName": "example.db",
	"databaseVersion": 1,
	"sqliteOpenHelperClassName": "ExampleSQLiteOpenHelper",
	"sqliteOpenHelperCallbacksClassName": "ExampleSQLiteOpenHelperCallbacks",
	"enableForeignKeys": true,
	"useAnnotations": true,
	"useSupportLibrary": true,
	"generateBeans": true,
	"debugLogsFieldName": "LOG_DEBUG_PROVIDER"
}

About packageName: this must be the same as the value of the package attribute in your manifest. Not to be confused with the applicationId (see https://developer.android.com/studio/build/application-id.html)

Get and run the tool

Download the acpg-cli-1.13.1.jar file here: https://github.com/BoD/android-contentprovider-generator/releases/latest

java -jar acpg-cli-1.13.1.jar -i <input folder> -o <output folder>

  • Input folder: where to find _config.json and your entity json files
  • Output folder: where the resulting files will be generated

Entity files

Create one file per entity, naming it <entity_name>.json. Inside each file, declare your fields (a.k.a "columns") with a name and a type. You can also optionally declare a default value, an index flag, a documentation and a nullable flag.

Currently the type can be:

  • String (SQLite type: TEXT)
  • Integer (INTEGER)
  • Long (INTEGER)
  • Float (REAL)
  • Double (REAL)
  • Boolean (INTEGER)
  • Date (INTEGER)
  • byte[] (BLOB)
  • enum (INTEGER).

You can also optionally declare table constraints.

Here is a person.json file as an example:

{
	"documentation": "A human being which is part of a team.",
	"fields": [
		{
			"documentation": "First name of this person. For instance, John.",
			"name": "first_name",
			"type": "String",
			"defaultValue": "John"
		},
		{
			"documentation": "Last name (a.k.a. Given name) of this person. For instance, Smith.",
			"name": "last_name",
			"type": "String",
			"nullable": true,
			"defaultValue": "Doe"
		},
		{
			"name": "age",
			"type": "Integer",
			"index": true
		},
		{
			"name": "gender",
			"type": "enum",
			"enumName": "Gender",
			"enumValues": [
				"MALE",
				"FEMALE",
				{"OTHER": "Value to use when neither male nor female"}
			],
			"nullable": false
		}
	],

	"constraints": [
		{
			"name": "unique_name",
			"definition": "UNIQUE (first_name, last_name) ON CONFLICT REPLACE"
		}
	],
	
	"defaultOrder": "first_name, last_name, age DESC"
}

Notes:

  • An _id primary key field is automatically (implicitly) declared for all entities. It must not be declared in the json file.
  • nullable is optional (true by default).
  • if documentation is present the value will be copied in Javadoc blocks in the generated code.
  • the constraints and defaultOrder sections are optional

A more comprehensive sample is available in the sample-app/etc/acpg folder.

You can have a look at the corresponding generated code in the etc/sample-generated-code folder.

By convention, you should name your entities and fields in lower case with words separated by '_', like in the example above.

The header.txt file (optional)

If a header.txt file is present, its contents will be inserted at the top of every generated file.

Use the generated files

  • When querying a table, use the corresponding Selection class as shown in this example:
PersonSelection where = new PersonSelection();
where.firstName("John").or().age(42);
Cursor c = context.getContentResolver().query(where.uri(), projection,
        where.sel(), where.args(), null);
  • When using the results of a query, wrap the resulting Cursor in the corresponding wrapper class. You can then use the generated getters directly as shown in this example:
PersonCursor person = new PersonCursor(c);
String lastName = person.getLastName();
Long age = person.getAge();
  • You can also conveniently combine these two facilities by using the query (or delete) method:
PersonSelection where = new PersonSelection();
where.firstName("John").or().age(42).orderByFirstName();
PersonCursor person = where.query(context);
person.moveToNext();
String lastName = person.getLastName();
Long age = person.getAge();

or, use a CursorLoader:

where.getCursorLoader(context);
  • When updating or inserting into a table, use the corresponding ContentValues class as shown in this example:
PersonContentValues values = new PersonContentValues();
values.putFirstName("John").putAge(42);
context.getContentResolver().update(values.uri(), values.values(), null, null);

or

values.insert(context);

Advanced usage

Foreign key / joins

There is limited support for foreign keys and joins. Here is an example of the syntax:

{
	"fields": [
		{
			"name": "main_team_id",
			"type": "Long",
			"nullable": false,
			"foreignKey": {
				"table": "team",
				"onDelete": "CASCADE"
			}
		},
		{
			"name": "first_name",
			"type": "String",
			"nullable": false
		},

		(...)
}

In this example, the field main_team_id is a foreign key referencing the primary key of the team table.

  • The appropriate FOREIGN KEY SQL constraint is generated (if enableForeignKeys is set to true in _config.json).
  • The team table will be automatically joined when querying the person table [1].
  • Getters for team columns are generated in the PersonCursor wrapper.
  • Of course if team has foreign keys they will also be handled (and recursively).

[1] A table is automatically joined if at least one of its columns is included in the projection. If the projection is null (i.e. all columns), all the tables are joined. Caution: you should be extra careful when using a null projection with joins because you will get several columns named _id in the results!

Limitations

  • Foreign keys always reference the _id column (the implicit primary key of all tables) and thus must always be of type Long - by design.
  • Only one foreign key to a particular table is allowed per table. In the example above only one column in person can point to team.
  • Loops (i.e. A has a foreign key to B and B has a foreign key to A) aren't detected. The generator will infinitely loop if they exist.
  • Cases such as "A has a FK to B, B has a FK to C, A has a FK to C" generate ambiguities in the queries, because C columns appear twice. In the sample app you can see an example of how to deal with this case, using prefixes and aliases (SQL's AS keyword).

Sample

A sample is available in the sample-app folder, with the entities in sample-app/etc/acpg.

You can have a look at the corresponding generated code in the etc/sample-generated-code folder.

Here is the table shema of the sample: Table shema of the sample

Building

This is a Gradle project.

./gradlew install to 'install' the Gradle plugin to your local maven repo

./gradlew shadowJar to build the cli tool

Similar tools

Here is a list of other tools that try to tackle the same problem.

I did not have the chance to try them out.

Licence

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Just to be absolutely clear, this license applies to this program itself, not to the source it will generate!

android-contentprovider-generator's People

Contributors

almilli avatar ansman avatar bhandfast avatar bod avatar caarmen avatar friedger avatar ivru avatar jbanse avatar julienbanse avatar konstantinritt avatar littleli avatar mdupierreux avatar myanimal avatar pomepuyn avatar robux4 avatar yargray avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

android-contentprovider-generator's Issues

Not qualified columns when projection is null

With the 1.8.1 version of the lib, there are two bugs:

  • With a null projection, the index returned by getCachedColumnIndexOrThrow of AbstractCursor is not qualified and therefor is the first one found that have the colName
  • In the XXXProvider.getQueryParams method, the qualifyAmbiguousColumns should be called on all joined tables

Compiling error (maven)

Hi, when I run mvn assembly:single I get this error:

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 17.171s
[INFO] Finished at: Thu Nov 21 18:45:59 CET 2013
[INFO] Final Memory: 7M/108M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-assembly-plugin:2.2-beta-5:single (default-cli) on project android_contentprovider_generator: Error reading assemblies: Descriptor with ID 'bundle' not found -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

Can you help me?
Thanks!

IMPROVEMENT: Simple creating CursorLoader, counting and ordering

It would be nice to add this code to AbstractSelection

    private String mOrderBy = null;

    public T orderBy(String order, boolean desc) {
        mOrderBy = order + (desc ? " DESC" : "");
        return (T) this;
    }

    public CursorLoader cursorLoader(Context context) {
        return new CursorLoader(context, uri(), null, sel(), args(), mOrderBy);
    }

    public int count(ContentResolver resolver) {
        final Cursor cursor = resolver.query(uri(), new String[0], sel(), args(), null);
        if (cursor == null)
            return 0;
        try {
            return cursor.getCount();
        } finally {
            cursor.close();
        }
    }

This simple change greatly simplifies common tasks like these:

@Override public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    return new EventSelection().number(mEventNumber).orderBy("date", true).cursorLoader(mContext);
}
int count = new EventSelection().number(mEventNumber).count(getContentResolver());
if (count > 0) {
    // Do something useful
}

Can't have not nullable with default value

For some reason when I try to set a non-nullable field to have a default value, it ignores the default value and only applies the NOT NULL on the create table statement.

    {
        "name": "quantity",
        "type": "Integer",
        "nullable": false,
        "default_value": "1"
    },

Unnecessary autoboxing in AbstractCursor and generated cursors

I think it isn't very efficient:

public Integer getIntegerOrNull(String colName) {
        Integer index = getCachedColumnIndexOrThrow(colName); // int to Integer
        if (isNull(index)) return null; // Integer to int
        return getInt(index); // Integer to int and int to Integer
}

and

public class GeneratedCursor extends AbstractCursor {
        ...
        public String getTitle() {
                Integer index = getCachedColumnIndexOrThrow(GeneratedColumns.TITLE); // int to Integer
                return getString(index); // Integer to int
        }
        ...
}

Some support for simple cases of upgrades

For instance, an attribute "addedInVersion" could be added on tables, and the tool would generate CREATE TABLE statements when upgrading from version n-1 to n.
Another one "deletedInVersion", to delete a no longer used table (a less useful feature, but why not).
I guess it may also be possible to handle table rename cases.

And also columns creation/deletion/renames, maybe.

LIMIT support

Add a LIMIT support, the same way GROUP BY is implemented.

_id usage

hello,
currently, _id can't be setted and used as normal field, and foreign keys always reference the _id column.
In most apps, an idea is sent by a server, and we have to manage two different ids, and put in database is not easy :
insert object
query object to get _ids
insert subObject.
Instead of just use an applyBatch.

I feel that all limitations are added by contentprovidergenerator to preserve misuse, but its use as a normal fields (ObjectContentValues, ObjectSelection...) could be useful in some cases.

did I miss something ?

thanks

Add `Model#getId()`

The newly added models are awesome but they don't expose any way to get the ID of the model, is there any reason for this?

A gradle plugin to start the generation

This will bring several advantages:

  • easier to get the tool, and to get the latest version than manually downloading it
  • easier than using command line to start the tool
  • bonus: it could be smart and start the generation automatically if the json files have been touched...

FEATURE REQUEST: Support for SQL Views

Hi again.
Another topic i recently came across is the support for SQL Views. I need to define a View to easily merge/Join some tables for simple usage in Lists with a CursorAdapter. Therefore i had to change the genrated classes and i think it would be possible to support Views out of the box.

What we need to use a View:
-a Type property for each .json file indicating if this is a table or a view
-Selection
-Cursor
-Columns
-SQL_CREATE -> i currently added it in the OpenHelperCallbacks class, but i think it would be nicer to add the views directly in the DatabaseHelper, e.g. with defining a method:
public String getStatementFor in the OpenHelperCallbacks Interface/class
-additional URI for the View and the appropriate Mappings in the Contentprovider itself, alternatively the possibility to define and Extension class to add this kind of URIs

Would be nice to discuss it with you.
Regards.

Make logging optional

The Log.d calls may be conditioned by a single Field / Constant to allow the dev to switch it off in one change.

AbstractSelection in vulnerable to SQL injection when passing in multiple values

The selection builder does not escape any arguments passed to and and just appends them to the query string when you pass multiple arguments.

To fix use prepared statements instead.

 protected void addEquals(String column, Object... value) {
        mSelection.append(column);

        if (value == null) {
            // Single null value
            mSelection.append(IS_NULL);
        } else if (value.length > 1) {
            // Multiple values ('in' clause)
            mSelection.append(IN);
            for (int i = 0; i < value.length; i++) {
                mSelection.append("?");
                if (i < value.length - 1) {
                    mSelection.append(COMMA);
                }
                mSelectionArgs.add(valueOf(value[i]));
            }
            mSelection.append(PAREN_CLOSE);
        } else {
            // Single value
            mSelection.append(EQ);
            mSelectionArgs.add(valueOf(value[0]));
        }
    }

Ability to not have an "id" (primary key) column at all

HI,

Sqlite documentation says:The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and disk I/O overhead and should be avoided if not strictly needed. It is usually not needed.

And sometime I don't need it?

Can you add an extra option in the json template.

Thanks.

Cannot get mandatory column after doing an "optional" join

We have these tables:

// table1.json
{
  "fields": [{
    "name": "capture_id",
    "type": "Long",
    "nullable": true,
    "index": true,
    "foreignKey": {
        "table": "table2",
        "onDelete": "CASCADE"
    }
  }]
}

// table2.json
{
  "fields": [{
    "name": "name",
    "type": "String",
    "nullable": false
  }]
}

So when we do this:

Table1Cursor cursor = new Table1Selection()
    .id(1)
    .query(context.getContentResolver(), new String[] {Table2.name, ...});
cursor.moveToNext();
cursor.getTable2Name(); // Crash because it throws a NullPointerException

It possible it would be great if we allowed non null columns for tables joined with nullable foreign keys to return null

Adding support for CursorLoader

I have been playing around with the idea that the _Object_Selection class would have a method to return a CursorLoader.

Something like this:

public CursorLoader cursorLoader(Context context, String[] projection) {
   return new CursorLoader(context, *Object*Columns.CONTENT_URI, projection, sel(), args(), order()) {
        @Override
        public Cursor loadInBackground() {
             return new *Object*Cursor(super.loadInBackground());
        }
   };
}

I am personally stuck on how to get the AbstractSelection to come into effect. Would we need a _Object_CursorLoader that extends a _Object_AbstractCursorLoader?

It seems that adding that Edited code above to the selection class would work well but I am not sure if it is nessessary.

Method errors in generated code of v1.9.3

I get following errors in Android Studio when using the code generated by android-contentprovider-generator 1.9.3, with "TableName" being the provided name for a database table:

error: method orderById(boolean) is already defined in class TableNameSelection
error: method orderById() is already defined in class TableNameSelection
error: method getId() is already defined in class TableNameCursor
error: TableNameCursor is not abstract and does not override abstract method getId() in TableNameModel
error: getId() in TableNameCursor cannot implement getId() in TableNameModel
return type long is not compatible with Integer

FEATURE REQUEST: Notify multiple URIs for related tables

I have the following tables:
Person:
{
"fields": [
{
"name": "person_name",
"type": "String",
"nullable": false
}
]
}
Addresses:
{
"fields": [
{
"name": "person_id",
"type": "Long",
"nullable": false,
"foreignKey": {
"table": "Person",
"onDelete": "CASCADE"
}
},
{
"name": "address_name",
"type": "String",
"nullable": false
}
]
}

I use CursorLoaders for the Person and Addresses table. A person can have multiple addresses. The problem is that if I delete a Person, I need the ContentProvider to also notify the CursorLoader for Adresses. Basically for delete notify besides the URI for Person, the URI for Addresses. This can be expanded to all CRUD operations.

Notify option is not respected by *ContentValues.update method

The generated SomeModelContentValues.update(Context context, SomeModelSelection where) method uses uri() where where.uri() should be used. And likewise the SomeModelContentValues.update(ContentProvider contentProvider, SomeModelSelection where) method.

I've issued a pull request to fix this problem and make some other improvements.

Or please edit the contentprovider.ftl file and fix this problem. Thanks.

String equality called with equal signs

The columns class for a table uses == to check string equality in hasColumns, is this intentional because it would seem risky to do this if the content provider will be used in a cross process environment.

Code doesn't compile with non-nullable boolean field

I added a non-nullable field is_favorite to my db. In order for the content provider to compile, I had to make this change:

-
-    public PoemSelection isFavorite(boolean... value) {
-        addEquals(PoemColumns.IS_FAVORITE, toObjectArray(value));
-        return this;
-    }
-
-    public PoemSelection isFavoriteNot(boolean... value) {
-        addNotEquals(PoemColumns.IS_FAVORITE, toObjectArray(value));
+    public PoemSelection isFavorite(boolean value) {
+        addEquals(PoemColumns.IS_FAVORITE, value ? new Object[] { "1" } : new Object[] { "0" });
         return this;
     }

Package import statements do not match package declarations

I have a configuration file, named entity_name.json:

{
    "fields": [
        {
            "name": "entity_name_field",
            "type": "String"
        }
    ]
}

When the entity_nameclasses are generated, their package names lack underscores:

package com.app.provider.entityname;

However, in the generated classes that import the generated entity_name classes, the import statements include underscores:

import com.app.provider.entity_name.*;

I'm using version 1.8.0.

FEATURE REQUEST: Use Extension classes to support Customizations

Hi.
I am currently using the generator and i thought about extending some of the generators functionality without touching the generated classes and the generator itself.

I would like to discuss if it would be a valuable Feature to add Extension classes that the user can use to add custom methods/constants etc.

Actually you generate a class like AbstractSelection. From my personal point of view there is e.g. a count method missing. So i would like to add it to the AbstractSelection functionality. but after regenerating the Code it will be dropped. So it would be nice to have a defined Extension Point at nearly every class as it is done with the OpenHelpherCallbacks.

I'll fid it nice if the hierarchy for this example was:
AbstractSelection <-AbstractSelectionExtension <-MyClassSelection(domain) <-MyClassSelectionExtension
So i would have two Extension points, one per entity and one per generated set of classes. I would do the same with Cursor (here i often add a fromRow method to get a Domain object), Columns, ContentValues (here i add a Constructor with domain object parameter to fill the CV initially). Even the ContentProvider itself needs some Extension Points for adding e.g. custom URIs and so on.

if you like the idea, i would like to discuss it. I could also fork make a code suggestions if it helps.
Regards.And thanks for the work!

Notify at the end of applyBatch

@Override
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws OperationApplicationException {
    Log.v(TAG, "applyBatch: " + operations);
    Set<Uri> urisToNotify = new HashSet<Uri>();
    for (ContentProviderOperation operation : operations)
        urisToNotify.add(operation.getUri());
    Log.v(TAG, "applyBatch: will notify these uris after persisting: " + urisToNotify);
    SQLiteDatabase db = mNetworkMonitorDatabase.getWritableDatabase();
    db.beginTransaction();
    try {
        ContentProviderResult[] result = super.applyBatch(operations);
        db.setTransactionSuccessful();
        for (Uri uri : urisToNotify)
            getContext().getContentResolver().notifyChange(uri, null);
        return result;
    } finally {
        db.endTransaction();
    }

"Default order by" field in entities

Currently there is a generated default order by which is hardcoded to be _id.
Instead this should be definable in the entities.
Also, if not defined, the default behavior should be to not have an ORDER BY clause at all.

To use the tool

Hi, i'm very happy when you created this the tool, but i don't understend input and output when run this tool, can you guide step by tep for me ?

Add setCursor to AbstractCursor to prevent extra initialization of model Cursors

Hello,

I have a code that does something like this

cursor.moveToFirst();
do {
      UserCursor userCursor = new UserCursor(cursor);
      if (userCursor.getIsRegistered() != null && !userCursor.getIsRegistered()) {
               // do Something
       }
} while (cursor.moveToNext());

Instead of creating a new UserCursor each time, why don't we add a setCursor method and reuse the object instead of creating new one, I think that would save memory, right ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.