{"id":13507715,"url":"https://github.com/emilsjolander/sprinkles","last_synced_at":"2025-03-30T09:33:01.972Z","repository":{"id":46032119,"uuid":"13521187","full_name":"emilsjolander/sprinkles","owner":"emilsjolander","description":"Sprinkles is a boiler-plate-reduction-library for dealing with databases in android applications","archived":false,"fork":false,"pushed_at":"2021-11-18T19:18:56.000Z","size":2700,"stargazers_count":772,"open_issues_count":25,"forks_count":84,"subscribers_count":40,"default_branch":"master","last_synced_at":"2024-11-01T07:33:13.322Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/emilsjolander.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-10-12T12:04:41.000Z","updated_at":"2024-09-29T13:29:02.000Z","dependencies_parsed_at":"2022-09-11T06:22:58.114Z","dependency_job_id":null,"html_url":"https://github.com/emilsjolander/sprinkles","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emilsjolander%2Fsprinkles","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emilsjolander%2Fsprinkles/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emilsjolander%2Fsprinkles/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emilsjolander%2Fsprinkles/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/emilsjolander","download_url":"https://codeload.github.com/emilsjolander/sprinkles/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246301963,"owners_count":20755512,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T02:00:38.094Z","updated_at":"2025-03-30T09:33:00.997Z","avatar_url":"https://github.com/emilsjolander.png","language":"Java","funding_links":[],"categories":["CN","Java","Libs","数据库开发"],"sub_categories":["[Emil Sjölander](https://github.com/emilsjolander)","\u003cA NAME=\"Orm\"\u003e\u003c/A\u003eOrm"],"readme":"![Icon](https://github.com/emilsjolander/sprinkles/raw/master/sprinkles.png) Sprinkles [![Build Status](https://travis-ci.org/emilsjolander/sprinkles.png)](https://travis-ci.org/emilsjolander/sprinkles)\n=========\nSprinkles is a boiler-plate-reduction-library for dealing with databases in android applications. Some would call it a kind of ORM but I don't see it that way. Sprinkles lets SQL do what it is good at, making complex queries. SQL however is a mess (in my opinion) when is comes to everything else. This is why sprinkles helps you with things such as inserting, updating, and destroying models. Sprinkles will also help you with the tedious task of unpacking a cursor into a model. Sprinkles actively supports version 2.3 of Android and above but it should work on older versions as well.\n\nDownload\n--------\nUsing gradle, add the following to your `build.gradle`. Just replace `x.x.x` with the correct version of the library (found under the releases tab).\n\n```Groovy\ndependencies {\n    compile 'se.emilsjolander:sprinkles:x.x.x'\n}\n```\n\nIf you are not using gradle for whatever reason i suggest you clone the repository and check out the latest tag.\n\nGetting started\n---------------\nWhen you have added the library to your project add a model class to it. I will demonstrate this with a `Note.java` class. I have omitted the import statements to keep it brief.\n```java\n@Table(\"Notes\")\npublic class Note extends Model {\n\n    @Key\n\t@AutoIncrement\n\t@Column(\"id\")\n\tprivate long id;\n\n\t@Column(\"title\")\n\tpublic String title;\n\n\t@Column(\"body\")\n\tpublic String body;\n\n\tpublic long getId() {\n\t\treturn id;\n\t}\n\n}\n```\nOk, a lot of important stuff in this short class. First of all, a model must subclass `se.emilsjolander.sprinkles.Model` and it also must have a `@Table` annotations specifying the table name that the model corresponds to. After the class declaration we have declared three members: `id`, `title` and `body`. Notice how all of them have a `@Column` annotation to mark that they are not only a member of this class but also a column of the table that this class represents. We have one last annotation in the above example. The `@AutoIncrement` annotation tells sprinkles that the field should automatically be set upon the creation of its corresponding row in the table. Key columns are the columns that are used to decide whether a model is already stored in the database when using methods such as `delete()` and `save()`.\n\nBefore using this class you must migrate it into the database. I recommend doing this in the `onCreate()` method of an `Application` subclass like this:\n```java\npublic class MyApplication extends Application {\n\n\t@Override\n\tpublic void onCreate() {\n\t\tsuper.onCreate();\n\n\t\tSprinkles sprinkles = Sprinkles.init(getApplicationContext());\n\n        sprinkles.addMigration(new Migration() {\n            @Override\n            protected void onPreMigrate() {\n                // do nothing\n            }\n\n            @Override\n            protected void doMigration(SQLiteDatabase db) {\n                db.execSQL(\n                        \"CREATE TABLE Notes (\" +\n                                \"id INTEGER PRIMARY KEY AUTOINCREMENT,\"+\n                                \"title TEXT,\"+\n                                \"body TEXT\"+\n                        \")\"\n                );\n            }\n\n            @Override\n            protected void onPostMigrate() {\n                // do nothing\n            }\n        });\n\t}\n\n}\n```\n\nNow you can happily create new instances of this class and save it to the database like so:\n```java\npublic void saveStuff() {\n\tNote n = new Note();\n\tn.title = \"Sprinkles is awesome!\";\n\tn.body = \"yup, sure is!\";\n\tn.save(); // when this call finishes n.getId() will return a valid id\n}\n```\n\nYou can also query for this note like this:\n```java\npublic void queryStuff() {\n\tNote n = Query.one(Note.class, \"select * from Notes where title=?\", \"Sprinkles is awesome!\").get();\n}\n```\n\nThere is a lot more you can do with sprinkles so please read the next section which covers the whole API!\n\nAPI\n---\n###Annotations\n- `@Table` Used to associate a model class with a SQL table.\n- `@AutoIncrement` Used to mark a field as an auto-incrementing. The field must be an `int` or a `long`.\n- `@Column` Used to associate a class field with a SQL column.\n- `@DynamicColumn` Used to associate a class field with a dynamic SQL column such as an alias in a query.\n- `@Key` Used to mark a field as a key. Multiple keys in a class are allowed and will result in a composite key. Keys will most often want to be mapped directly to primary keys in your database.\n\n###Saving\nThe save method is both an insert and an update method, the correct operation will be done depending on the model's existence in the database. The first two methods below are synchronous, the second is for use together with a transaction (more on that later). There are also two asynchronous methods, one with a callback and one without. The synchronous methods will return a boolean indicating if the model was saved or not. The asynchronous method with a callback will just not invoke the callback if saving failed.\n```java\nboolean save();\nboolean save(Transaction t);\nvoid saveAsync();\nvoid saveAsync(OnSavedCallback callback);\n```\n\nAll the save methods use this method to check if a model exists in the database. You are free to use it as well.\n```java\nboolean exists();\n```\n\n###Deleting\nSimilar to saving there are four methods that let you delete a model. These work in the same way as save but will not return a boolean indicating the result.\n```java\nvoid delete();\nvoid delete(Transaction t);\nvoid deleteAsync();\nvoid deleteAsync(OnDeletedCallback callback);\n```\n\n###Querying\nStart a query with on of the following static methods:\n```java\nQuery.one(Class\u003c? extends QueryResult\u003e clazz, String sql, Object[] args);\nQuery.many(Class\u003c? extends QueryResult\u003e clazz, String sql, Object[] args);\nQuery.all(Class\u003c? extends Model\u003e clazz);\n```\nNotice that unlike androids built in query methods you can send in an array of objects instead of an array of strings.\n\nOnce the query has been started you can get the result with two different methods:\n```java\n\u003cT extends QueryResult\u003e get();\nboolean getAsync(LoaderManager lm, ResultHandler\u003c? extends Model\u003e handler, Class\u003c? extends Model\u003e... respondsToUpdatedOf);\n```\n\n`get()` returns either the `QueryResult` or a list of the `QueryResult` represented by the `Class` you sent in as the first argument to the query method. `getAsync()` is the same only that the result is delivered on a callback function after executing `get()` on another thread. `getAsync()` also delivers updated results once the backing model of the query is updated if you return `true` indicating you want further updates. `getAsync()` uses loaders and therefore needs a `LoaderManager` instance. `getAsync()` also takes an optional array of classes which is used when the query relies on more models than the one you are querying for and you want the query to be updated when those models change as well.\n\n###CursorList\nAll Queries return a `CursorList` subclass. This is a `Iterable` subclass which lazily unpacks a cursor into its corresponding model when you ask for the next item. This leads to having the efficiency of a `Cursor` but without the pain. Excluding the `Iterable` methods `CursorList` also provides the following methods.\n```java\npublic int size();\npublic T get(int pos);\npublic List\u003cT\u003e asList();\n```\nRemember to always call `close()` on a `CursorList` instance! This will close the underlying cursor.\n\n###ModelList\nFor mass saving/deletion of models you can use the `ModelList` class. It extends `ArrayList` and has the following additional methods:\n```java\npublic static \u003cE extends Model\u003e ModelList\u003cE\u003e from(CursorList\u003cE\u003e cursorList);\npublic boolean saveAll();\npublic boolean saveAll(Transaction t);\npublic void saveAllAsync();\npublic void saveAllAsync(OnAllSavedCallback callback);\npublic void deleteAll();\npublic void deleteAll(Transaction t);\npublic void deleteAllAsync();\npublic void deleteAllAsync(OnAllDeletedCallback callback);\n```\n\n`from(CursorList\u003cE extends Model\u003e cursorList)` is a helper method which creates a `ModelList` from a `CursorList`, so you can e.g. delete all models from a previous query in one batch. Be aware, that the cursor is not closed for you when calling this method and you have to do it yourself!\n\n###Transactions\nBoth `save()` and `delete()` methods exist which take in a `Transaction`. Here is a quick example on how to use them. If any exception is thrown while saving a model or if any model fails to save the transaction will be rolled back.\n```java\npublic void doTransaction(List\u003cNote\u003e notes) {\n\tTransaction t = new Transaction();\n\ttry {\n\t\tfor (Note n : notes) {\n\t\t\tif (!n.save(t)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tt.setSuccessful(true);\n\t} finally {\n\t\tt.finish();\n\t}\n}\n```\n\n###Callbacks\nEach model subclass can override a couple of callbacks.\n\nUse the following callback to ensure that your model is not saved in an invalid state.\n```java\n@Override\npublic boolean isValid() {\n\t// check model validity\n}\n```\n\nUse the following callback to update a variable before the model is created\n```java\n@Override\nprotected void beforeCreate() {\n\tmCreatedAt = System.currentTimeMillis();\n}\n```\n\nUse the following callback to update a variable before the model is saved. This is called directly before `beforeCreate()` if the model is saved for the first time.\n```java\n@Override\nprotected void beforeSave() {\n\tmUpdatedAt = System.currentTimeMillis();\n}\n```\n\nUse the following callback to clean up things related to the model but not stored in the database. Perhaps a file on the internal storage?\n```java\n@Override\nprotected void afterDelete() {\n\t// clean up some things?\n}\n```\n\n###Migrations\nMigrations are the way you add things to your database. I suggest putting all your migrations in the `onCreate()` method of a `Application` subclass. Here is a quick example of how that would look:\n```java\npublic class MyApplication extends Application {\n\n\t@Override\n\tpublic void onCreate() {\n\t\tsuper.onCreate();\n\n\t\tSprinkles sprinkles = Sprinkles.init(getApplicationContext());\n\n        sprinkles.addMigration(new Migration() {\n            @Override\n            protected void onPreMigrate() {\n                // do nothing\n            }\n\n            @Override\n            protected void doMigration(SQLiteDatabase db) {\n                db.execSQL(\n                        \"CREATE TABLE Notes (\" +\n                                \"id INTEGER PRIMARY KEY AUTOINCREMENT,\"+\n                                \"title TEXT,\"+\n                                \"body TEXT\"+\n                        \")\"\n                );\n            }\n\n            @Override\n            protected void onPostMigrate() {\n                // do nothing\n            }\n        });\n\t}\n\n}\n```\nMigrations are performed using raw SQL, this allowes full freedom to use all of the powerfull contraints that are possible to put on columns. Two optional methods are provided that allow you do some form of processing of your data before and after a migration, this can be usefull when recreating a table with different properties but you want to keep the data that was previously stored in the now deleted table. Once a migration has been added with `sprinkles.addMigration()` it should NEVER be changed, and all new migrations should be added after the previous migration. This ensures both old and new clients will have a consistent database and you will not need to care about database versioning.\n\n###Type serializers\nThrough an instance of `Sprinkles` you can register your own `TypeSerializer` instances via `registerType()` for serializing an object in your model into a column in the database. Sprinkles uses a `TypeSerializer` implementation internally for all the different data types that it supports. So check out the `se.emilsjolander.sprinkles.typeserializers` package for example implementations. These serializers will be used both when saving a model and when querying rows from the database.\n\n###ContentObservers\nSprinkles supports ContentObservers for change notifications. By registering your models for observation you can ensure your ContentObserver will be notified of changes.\n```java\nSprinklesContentObserver observer;\nContentObserver myCustomObserver = ...;\n\nthis.observer = new SprinklesContentObserver(myCustomObserver);\n\n@Override\npublic void onResume() {\n    super.onResume();\n    this.observer.register(Note.class, true); // true/false for notify descendants\n}\n\n@Override\npublic void onPause() {\n    super.onPause();\n    this.observer.unregister();\n}\n```\n\n###Relationships\nSprinkles does nothing to handle relationships for you; this is by design. You will have to use the regular ways to handle relationships in SQL. Sprinkles gives you all the tools needed for this and it works very well.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femilsjolander%2Fsprinkles","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femilsjolander%2Fsprinkles","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femilsjolander%2Fsprinkles/lists"}