{"id":16560816,"url":"https://github.com/jeppeman/highlite","last_synced_at":"2025-03-21T11:31:47.444Z","repository":{"id":57725594,"uuid":"76169459","full_name":"jeppeman/HighLite","owner":"jeppeman","description":"An SQLite ORM for Android with automatic database migrations built with annotation processing","archived":false,"fork":false,"pushed_at":"2018-09-21T16:21:11.000Z","size":426,"stargazers_count":77,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-10-12T20:30:06.205Z","etag":null,"topics":["android","annotation-processing","java","sqlite","sqlite-android"],"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/jeppeman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-12-11T11:13:26.000Z","updated_at":"2024-03-31T14:18:46.000Z","dependencies_parsed_at":"2022-09-26T21:51:13.902Z","dependency_job_id":null,"html_url":"https://github.com/jeppeman/HighLite","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeppeman%2FHighLite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeppeman%2FHighLite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeppeman%2FHighLite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeppeman%2FHighLite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jeppeman","download_url":"https://codeload.github.com/jeppeman/HighLite/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221814760,"owners_count":16885059,"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":["android","annotation-processing","java","sqlite","sqlite-android"],"created_at":"2024-10-11T20:30:03.569Z","updated_at":"2024-10-28T10:01:26.849Z","avatar_url":"https://github.com/jeppeman.png","language":"Java","readme":"[![Build Status](https://travis-ci.org/jeppeman/HighLite.svg?branch=master)](https://travis-ci.org/jeppeman/HighLite)\n\nHighLite\n===\n\nHighLite is an SQLite library for Android that makes use of annotation processing to generate boilerplate for your SQLite operations.\n\n\u003cb\u003eKey features:\u003c/b\u003e\n\n* Automated table creation and table upgrades (column additions / changes / deletions are automatic) with opt-out possibilities for those who do not want it. \n* Query builder that eliminates the need to have to deal with the null-argument passing to the standard Android SQLite API.\n* Easy to use API with simple but flexible operations for get, save and delete.\n* Reactive! Each operation can be Rx-ified for those who use RxJava.\n* Supports inheritance of database models\n* Support for foreign keys and relationships in database models.\n* Support for the rest of the column constraints available for SQLite, i.e. UNIQUE, NOT NULL and AUTOINCREMENT\n* Support for multiple databases\n\n\u003cb\u003eOther positives:\u003c/b\u003e\n\n* Fast! No reflection resolving at runtime, all operations are carried out through compile time generated code\n* Errors in user setup are caught and reported at compile time\n* Lint warnings for errors that can't be caught at compile time\n* Comprehensive test coverage\n* Type safe operations\n* No need to subclass SQLiteOpenHelper; all necessary interactions with it are done under the hood.\n\nGetting started\n---\n```groovy\ndependencies {\n    compile 'com.jeppeman:highlite:1.1.3'\n    annotationProcessor 'com.jeppeman:highlite-compiler:1.1.3'\n}\n\n```\n\nKotlin users will have to replace `annotationProcessor` with `kapt`.\n\nBasic setup\n---\nAnnotate a class with ```@SQLiteDatabaseDescriptor``` as follows:\n```java\n\n@SQLiteDatabaseDescriptor(\n    dbName = \"companyDatabase\",\n    dbVersion = 1 // Increment this to trigger an upgrade\n)\npublic class CompanyDatabase {\n\n    // Optional: define a method like this if you want to manually handle onOpen.\n    // Note: PRAGMA foreign_keys = ON is set automatically if any foreign\n    // keys are found for any table in the database.\n    @OnOpen\n    public static void onOpen(SQLiteDatabase db) {\n        ...\n    }\n    \n    // Optional: define a method like this if you want to manually handle onCreate;\n    // i.e. if you opt out from automatic table creation on some table.\n    @OnCreate\n    public static void onCreate(SQLiteDatabase db) {\n        ...\n    }\n    \n    // Optional: define a method like this if you want to manually handle onUpgrade;\n    // i.e. if you opt out from automatic upgrades on some table \n    @OnUpgrade\n    public static void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\n        ...\n    }\n    \n}\n```\n\nThen define a class for a table that links to the database class\n\n```java\n@SQLiteTable(\n        database = CompanyDatabase.class, \n        tableName = \"companies\", // If left empty, the name of the table defaults to the class name snake cased\n        autoCreate = true, // defaults to true, set to false if you do not want the table to be created automatically\n        autoAddColumns = true, // defaults to true, set to false if you do not want new columns to be added automatically on upgrades\n        autoDeleteColumns = false // defaults to false, set to true if you want deleted fields to be removed from the database automatically on upgrades\n)\npublic class Company {\n    \n    @SQLiteColumn(primaryKey = @PrimaryKey(autoIncrement = true))\n    long id; // fields annotated with @SQLiteColumn need to be at least package local\n    \n    @SQLiteColumn(\"companyName\") // Column name becomes companyName here\n    String name;\n    \n    @SQLiteColumn\n    Date created; // Dates are stored as INTEGER's with the amount of seconds since UNIX epoch\n    \n    @SQLiteColumn\n    List\u003cString\u003e employees; // Fields with types that cannot be matched against an SQLite data type will be serialized and stored as BLOB's \n}\n```\n\nThat's it, you're now ready to start doing some actual database operations.\n\n\u003cb\u003eNote to Kotlin users\u003c/b\u003e\n\n\nFor now, Kotlin properties with primitive types have to be annotated with `@JvmField`, and non-primitive have to be marked with `lateinit` or be annotated with `@JvmField`. Here follows an example:\n\n```kotlin\n@SQLiteTable(database = CompanyDatabase::class)\nclass Company {\n    \n    @JvmField\n    @SQLiteColumn(primaryKey = PrimaryKey(autoIncrement = true))\n    var id : Int = 0 // Primitive type, annotate with @JvmField\n    \n    @SQLiteColumn(\"companyName\")\n    lateinit var name : String // Non-primitive, mark with lateinit\n}\n```\n\nThe reason for this is that a Kotlin property by default is compiled to a private Java field with a getter \nand a setter method. With `@JvmField` and `lateinit` the compiled java class has its corresponding field exposed publicly\n\nOperations\n---\n\n### Insert an object\n\n```java\nSQLiteOperator\u003cCompany\u003e operator = SQLiteOperator.from(getContext(), Company.class);\nfinal Company companyObject = new Company();\ncompanyObject.name = \"My awesome company\";\ncompanyObject.created = new Date();\ncompanyObject.employees = Arrays.asList(\"John\", \"Bob\");\n\n// Blocking\noperator.save(companyObject).executeBlocking(); // the save method inserts if the object's id is not present in the table, otherwise updates\n\n// Non-blocking\noperator.save(companyObject)\n    .asCompletable() // or .asFlowable(), .asObservable(), .asSingle() or .asMaybe();\n```\n\n### Fetch by id and update\n\n```java\n// If you pass an argument to getSingle it will be matched against the table's primary key field,\n// in this case `id` = 1\nfinal Company fetchedObject = operator.getSingle(1).executeBlocking();\nfetchedObject.name = \"My not so awesome company\";\noperator.save(fetchedObject).executeBlocking();\n```\n\n### Fetch by query\n\n```java\nfinal List\u003cCompany\u003e list = operator\n    .getList()\n    .withQuery(\n        SQLiteQuery\n            .builder()\n            .where(\"`id` = ? AND `companyName` = ?\", 1, \"My not so awesome company\")\n            .build()\n    ).executeBlocking();\n```\n\n### Fetch by raw query and delete\n\n```java\nfinal List\u003cCompany\u003e list = operator\n    .getList()\n    .withRawQuery(\"SELECT * FROM companies where `id` = ?\", 1)\n    .executeBlocking();\n\noperator.delete(list).executeBlocking();\n\n```\n\n### Delete by query\n\n```java\noperator\n    .delete()\n    .withQuery(\n        SQLiteQuery\n            .builder()\n            .where(\"`id` = ?\", 1)\n            .build()\n    ).executeBlocking();\n```\n\n### Save by query\n\n```java\noperator\n    .save()\n    .withQuery(\n        SQLiteQuery\n            .builder()\n            .set(\"companyName\", \"Changed name\")\n            .set(\"created\", new Date())\n            .where(\"`id` = ?\", 1)\n            .build()\n    ).executeBlocking();\n```\n\nForeign keys and relationships\n---\n\nHighLite supports foreign keys and relationships, here's an example of how you can use them:\n\n```java\n@SQLiteTable(\n        database = CompanyDatabase.class, \n        tableName = \"companies\"\n)\npublic class Company {\n    \n    @SQLiteColumn(primaryKey = @PrimaryKey(autoIncrement = true))\n    long id;\n    \n    @SQLiteColumn(\"companyName\")\n    String name;\n    \n    @SQLiteRelationship(backReference = \"company\") // backReference needs to be the name of the foreign key field of the class it is referring to\n    List\u003cEmployee\u003e employeeList; // When a company is fetched from the database, its related employees gets fetched as well\n}\n\n@SQLiteTable(\n        database = CompanyDatabase.class, \n        tableName = \"employees\"\n)\npublic class Employee {\n    \n    @SQLiteColumn(primaryKey = @PrimaryKey(autoIncrement = true))\n    long id; // fields annotated with @SQLiteColumn need to be package local\n    \n    @SQLiteColumn(\"employeeName\")\n    String name;\n    \n    @SQLiteColumn\n    float salary;\n    \n    @SQLiteColumn(foreignKey = @ForeignKey(\n          fieldReference = \"id\", // Note: this is the name of the field of the class you are referring to, not the database column name; the field has to be unique\n          cascadeOnDelete = true, // defaults to false\n          cascadeOnUpdate = true // defaults to false\n    ))\n    Company company; // When an employee is fetched, this field is automatically instantiated as its corresponding Company\n}\n```\n\nLet's create a company with a couple of employees:\n\n```java\nSQLiteOperator\u003cCompany\u003e companyOperator = SQLiteOperator.from(getContext(), Company.class);\nCompany company = new Company();\ncompany.name = \"My awesome company\";\ncompanyOperator.save(company).executeBlocking();\n\nSQLiteOperator\u003cEmployee\u003e employeeOperator = SQLiteOperator.from(getContext(), Employee.class);\nEmployee john = new Employee(),\n    bob = new Employee();\njohn.name = \"John\";\njohn.salary = 1000f;\njohn.company = company;\nbob.name = \"Bob\";\nbob.salary = 10000f;\nbob.company = company;\nemployeeOperator.save(john, bob).executeBlocking();\n```\n\nNow if we fetch the company from the database the employees will follow:\n```java\nCompany companyFromDatabase = companyOperator\n    .getSingle()\n    .withRawQuery(\"SELECT * FROM companies WHERE `name` = ?\", \"My awesome company\")\n    .executeBlocking();\n    \nLog.d(\"employees\", companyFromDatabase.employeeList /* \u003c- this is now [john, bob]*/)\n```\n\nInheritance\n-----------\n\nHighLite supports inheritance of classes annotated with `SQLiteTable`, consider the following:\n\n```java\n@SQLiteTable(\n        database = CompanyDatabase.class\n)\npublic class Developer extends Employee {\n\n    @SQLiteColumn\n    String type;\n}\n```\n\nHere the class `Developer` extends `Employee`, which is already annotated with `SQLiteTable`, the \ncreate statement that is generated from this setup looks like this:\n\n```roomsql\nCREATE TABLE IF NOT EXISTS developer (\n    `type` TEXT,\n    `employees_ptr_id` INTEGER PRIMARY KEY NOT NULL,\n    FOREIGN KEY(`employees_ptr_id`) REFERENCES employees(`id`) ON DELETE CASCADE ON UPDATE CASCADE\n);\n```\n\nSo we have a one-to-one relationship between `Developer` and `Employee`, therefore the primary key\nfor `Developer` is automatically created as a pointer to the primary key of `Employee`.\n\nLet's have a look at what happens when we use operations on the `Developer` class.\n\n```java\nSQLiteOperator\u003cDeveloper\u003e operator = SQLiteOperator.from(getContext(), Developer.class);\nDeveloper dev = new Developer();\ndev.name = \"Bob\";\ndev.salary = 10000f;\ndev.company = company;\ndev.type = \"Android\";\n\n// When we save the object, the values of the fields are saved to the table they correspond to in\n// the class hierarchy; in this case, name, salary and company are saved to the employees table,\n// whereas type is saved to the developer table\noperator.save(dev).executeBlocking();\n\n// Now if we fetch all developers from the database, a JOIN will be performed on the developer and\n// employees tables and fields will be populated accordingly.\nList\u003cDeveloper\u003e devsFromDb = operator.getList().executeBlocking();\n```\n\nYou may also want to inherit from a base class that is not corresponding to a table in the database,\nin that case, the following works:\n\n```java\npublic class TimestampedModel {\n\n    @SQLiteColumn\n    Date created;\n    \n    @SQLiteColumn\n    Date modified;\n}\n\n@SQLiteTable(\n        database = CompanyDatabase.class, \n        tableName = \"companies\"\n)\npublic class Company extends TimestampedModel {\n    \n    @SQLiteColumn(primaryKey = @PrimaryKey(autoIncrement = true))\n    long id;\n    \n    @SQLiteColumn(\"companyName\")\n    String name;\n    \n    @SQLiteRelationship(backReference = \"company\") // backReference needs to be the name of the foreign key field of the class it is referring to\n    List\u003cEmployee\u003e employeeList; // When a company is fetched from the database, its related employees gets fetched as well\n}\n```\n\nWith this setup, the following create statement is generated:\n\n```roomsql\nCREATE TABLE IF NOT EXISTS companies (\n    `id` INTEGER PRIMARY KEY AUTOINCREMENT,\n    `companyName` TEXT,\n    `created` INTEGER,\n    `employees` BLOB,\n    `created` INTEGER,\n    `modified` INTEGER\n);\n```\n\nUpcoming features\n-----------\n\n* More flexibility when it comes to the migrations\n* Composite primary key support\n* Kotlin improvements\n\n\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeppeman%2Fhighlite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjeppeman%2Fhighlite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeppeman%2Fhighlite/lists"}