├── .github
└── workflows
│ └── gradle.yml
├── .gitignore
├── LICENSE
├── README.md
├── android-perf-screenshot.png
├── app
├── build.gradle.kts
├── objectbox-models
│ └── default.json
├── proguard-rules.pro
├── schemas
│ └── io.objectbox.performanceapp.room.AppDatabase
│ │ └── 1.json
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── io
│ │ └── objectbox
│ │ └── performanceapp
│ │ ├── Benchmark.java
│ │ ├── MainActivity.java
│ │ ├── PerfTest.java
│ │ ├── PerfTestRunner.java
│ │ ├── RandomValues.java
│ │ ├── TestType.java
│ │ ├── greendao
│ │ ├── DaoMaster.java
│ │ ├── DaoSession.java
│ │ ├── GreendaoPerfTest.java
│ │ ├── SimpleEntity.java
│ │ ├── SimpleEntityDao.java
│ │ ├── SimpleEntityIndexed.java
│ │ └── SimpleEntityIndexedDao.java
│ │ ├── objectbox
│ │ ├── ObjectBoxPerfTest.java
│ │ ├── SimpleEntity.java
│ │ └── SimpleEntityIndexed.java
│ │ ├── realm
│ │ ├── RealmPerfTest.java
│ │ ├── SimpleEntity.java
│ │ └── SimpleEntityIndexed.java
│ │ └── room
│ │ ├── AppDatabase.java
│ │ ├── RoomPerfTest.java
│ │ ├── SimpleEntity.java
│ │ ├── SimpleEntityDao.java
│ │ ├── SimpleEntityIndexed.java
│ │ └── SimpleEntityIndexedDao.java
│ └── res
│ ├── layout
│ └── activity_main.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle.kts
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── greenDAO-generator
├── build.gradle
└── src
│ └── io
│ └── objectbox
│ └── performanceapp
│ └── generator
│ └── TestDaoGenerator.java
└── settings.gradle
/.github/workflows/gradle.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Gradle
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
3 |
4 | name: Java CI with Gradle
5 |
6 | on:
7 | push:
8 | pull_request:
9 | workflow_dispatch: # Allow running manually from web UI
10 |
11 | jobs:
12 | build:
13 |
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - name: Check out
18 | uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
19 |
20 | - name: Set up JDK 17
21 | uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
22 | with:
23 | distribution: 'temurin'
24 | java-version: '17'
25 | cache: gradle
26 |
27 | - name: Gradle Info
28 | run: ./gradlew -version
29 |
30 | - name: Build with Gradle
31 | run: ./gradlew build
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | build/
7 | /captures
8 | .externalNativeBuild
9 | default.json.bak
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ObjectBox Java Database Performance Benchmarks
2 |
3 | This is an Android app to measure object persistence performance of
4 | - [ObjectBox](/app/src/main/java/io/objectbox/performanceapp/objectbox)
5 | - [Realm](/app/src/main/java/io/objectbox/performanceapp/realm)
6 | - [SQLite using Room](/app/src/main/java/io/objectbox/performanceapp/room)
7 | - [SQLite using greenDAO](/app/src/main/java/io/objectbox/performanceapp/greendao) (deprecated)
8 |
9 | Results are printed on the UI and saved as tab-separated files (`.tvs`) that can be easily imported
10 | into a spreadsheet. The files are located on external storage.
11 |
12 |
13 |
14 | ## How to get good results
15 |
16 | * Tests perform differently when multiple databases are selected:
17 | For comparable results, run only a single database at a time.
18 | * Put the test device into air plane mode to avoid background apps doing sync over the network.
19 | * Screen must be on at all times (e.g. plug the device in).
20 | * Beware of lazy loaded data (e.g. properties on live objects of Realm):
21 | loading objects may seem very fast because no data is actually loaded.
22 | For better comparison it may be necessary to access data (at least once) and combine load and access time to get actual read time.
23 | * We also have written some general notes on [benchmarking on Android](https://greenrobot.org/android/benchmarking-on-android/).
--------------------------------------------------------------------------------
/android-perf-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-performance/e8d3c35b2ab4fc774c04284489b7c275499f5470/android-perf-screenshot.png
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("io.objectbox")
4 | id("realm-android")
5 | }
6 |
7 | android {
8 | namespace = "io.objectbox.performanceapp"
9 | compileSdk = 34 // Android 14
10 |
11 | buildFeatures {
12 | viewBinding = true
13 | }
14 |
15 | compileOptions {
16 | sourceCompatibility = JavaVersion.VERSION_1_8
17 | targetCompatibility = JavaVersion.VERSION_1_8
18 | }
19 |
20 | defaultConfig {
21 | applicationId = "io.objectbox.performanceapp"
22 | minSdk = 19 // Android 4.4
23 | targetSdk = 34 // Android 14
24 | versionCode = 1
25 | versionName = "1.0"
26 |
27 | javaCompileOptions {
28 | annotationProcessorOptions {
29 | arguments(mapOf("room.schemaLocation" to "$projectDir/schemas"))
30 | }
31 | }
32 | }
33 |
34 | buildTypes {
35 | getByName("release") {
36 | isMinifyEnabled = false
37 | proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
38 | signingConfig = signingConfigs.getByName("debug")
39 | }
40 | create("releaseDebugCert") {
41 | initWith(getByName("release"))
42 | // Just to use without checkjni
43 | signingConfig = signingConfigs.getByName("debug")
44 | }
45 | create("debugJniNoDebug") {
46 | initWith(getByName("debug"))
47 | // Just to use without checkjni
48 | isJniDebuggable = false
49 | }
50 | }
51 | }
52 |
53 | // Print deprecation warnings like Kotlin
54 | tasks.withType(JavaCompile::class).configureEach {
55 | options.isDeprecation = true
56 | }
57 |
58 | dependencies {
59 | implementation("androidx.preference:preference:1.2.1")
60 | implementation("androidx.room:room-runtime:2.6.1")
61 | annotationProcessor("androidx.room:room-compiler:2.6.1")
62 | implementation("org.greenrobot:greendao:3.3.0")
63 | implementation("org.greenrobot:essentials:3.1.0")
64 | }
65 |
--------------------------------------------------------------------------------
/app/objectbox-models/default.json:
--------------------------------------------------------------------------------
1 | {
2 | "_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.",
3 | "_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
4 | "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
5 | "entities": [
6 | {
7 | "id": "1:2066508037409008861",
8 | "lastPropertyId": "10:8979168811650087844",
9 | "name": "SimpleEntity",
10 | "properties": [
11 | {
12 | "id": "1:8357181620038296370",
13 | "name": "id",
14 | "type": 6,
15 | "flags": 1
16 | },
17 | {
18 | "id": "2:8014957599658721377",
19 | "name": "simpleBoolean",
20 | "type": 1
21 | },
22 | {
23 | "id": "3:486615229902264705",
24 | "name": "simpleByte",
25 | "type": 2
26 | },
27 | {
28 | "id": "4:6601224481371519086",
29 | "name": "simpleShort",
30 | "type": 3
31 | },
32 | {
33 | "id": "5:2118306784392599510",
34 | "name": "simpleInt",
35 | "type": 5
36 | },
37 | {
38 | "id": "6:5812772956445341632",
39 | "name": "simpleLong",
40 | "type": 6
41 | },
42 | {
43 | "id": "7:1065418842978193908",
44 | "name": "simpleFloat",
45 | "type": 7
46 | },
47 | {
48 | "id": "8:7220777031930057031",
49 | "name": "simpleDouble",
50 | "type": 8
51 | },
52 | {
53 | "id": "9:6669079511137612739",
54 | "name": "simpleString",
55 | "type": 9
56 | },
57 | {
58 | "id": "10:8979168811650087844",
59 | "name": "simpleByteArray",
60 | "type": 23
61 | }
62 | ],
63 | "relations": []
64 | },
65 | {
66 | "id": "2:1960446593108937414",
67 | "lastPropertyId": "10:7622112042154959206",
68 | "name": "SimpleEntityIndexed",
69 | "properties": [
70 | {
71 | "id": "1:3687894186713655139",
72 | "name": "id",
73 | "type": 6,
74 | "flags": 1
75 | },
76 | {
77 | "id": "2:744491936175748739",
78 | "name": "simpleBoolean",
79 | "type": 1
80 | },
81 | {
82 | "id": "3:930101374761292306",
83 | "name": "simpleByte",
84 | "type": 2
85 | },
86 | {
87 | "id": "4:5019932040401094666",
88 | "name": "simpleShort",
89 | "type": 3
90 | },
91 | {
92 | "id": "5:9036275660773544260",
93 | "name": "simpleInt",
94 | "indexId": "1:7769255088226738365",
95 | "type": 5,
96 | "flags": 8
97 | },
98 | {
99 | "id": "6:5133250005110876084",
100 | "name": "simpleLong",
101 | "type": 6
102 | },
103 | {
104 | "id": "7:7794332651287140849",
105 | "name": "simpleFloat",
106 | "type": 7
107 | },
108 | {
109 | "id": "8:2875693831440918677",
110 | "name": "simpleDouble",
111 | "type": 8
112 | },
113 | {
114 | "id": "9:7453429302134663068",
115 | "name": "simpleString",
116 | "indexId": "2:2825443978184014074",
117 | "type": 9,
118 | "flags": 2048
119 | },
120 | {
121 | "id": "10:7622112042154959206",
122 | "name": "simpleByteArray",
123 | "type": 23
124 | }
125 | ],
126 | "relations": []
127 | }
128 | ],
129 | "lastEntityId": "2:1960446593108937414",
130 | "lastIndexId": "2:2825443978184014074",
131 | "lastRelationId": "0:0",
132 | "lastSequenceId": "0:0",
133 | "modelVersion": 5,
134 | "modelVersionParserMinimum": 5,
135 | "retiredEntityUids": [],
136 | "retiredIndexUids": [],
137 | "retiredPropertyUids": [],
138 | "retiredRelationUids": [],
139 | "version": 1
140 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\Java\adt\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/schemas/io.objectbox.performanceapp.room.AppDatabase/1.json:
--------------------------------------------------------------------------------
1 | {
2 | "formatVersion": 1,
3 | "database": {
4 | "version": 1,
5 | "identityHash": "3440b72ef8e427f23f501299b3de69d9",
6 | "entities": [
7 | {
8 | "tableName": "SimpleEntity",
9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `simpleBoolean` INTEGER NOT NULL, `simpleByte` INTEGER NOT NULL, `simpleShort` INTEGER NOT NULL, `simpleInt` INTEGER NOT NULL, `simpleLong` INTEGER NOT NULL, `simpleFloat` REAL NOT NULL, `simpleDouble` REAL NOT NULL, `simpleString` TEXT, `simpleByteArray` BLOB, PRIMARY KEY(`id`))",
10 | "fields": [
11 | {
12 | "fieldPath": "id",
13 | "columnName": "id",
14 | "affinity": "INTEGER",
15 | "notNull": true
16 | },
17 | {
18 | "fieldPath": "simpleBoolean",
19 | "columnName": "simpleBoolean",
20 | "affinity": "INTEGER",
21 | "notNull": true
22 | },
23 | {
24 | "fieldPath": "simpleByte",
25 | "columnName": "simpleByte",
26 | "affinity": "INTEGER",
27 | "notNull": true
28 | },
29 | {
30 | "fieldPath": "simpleShort",
31 | "columnName": "simpleShort",
32 | "affinity": "INTEGER",
33 | "notNull": true
34 | },
35 | {
36 | "fieldPath": "simpleInt",
37 | "columnName": "simpleInt",
38 | "affinity": "INTEGER",
39 | "notNull": true
40 | },
41 | {
42 | "fieldPath": "simpleLong",
43 | "columnName": "simpleLong",
44 | "affinity": "INTEGER",
45 | "notNull": true
46 | },
47 | {
48 | "fieldPath": "simpleFloat",
49 | "columnName": "simpleFloat",
50 | "affinity": "REAL",
51 | "notNull": true
52 | },
53 | {
54 | "fieldPath": "simpleDouble",
55 | "columnName": "simpleDouble",
56 | "affinity": "REAL",
57 | "notNull": true
58 | },
59 | {
60 | "fieldPath": "simpleString",
61 | "columnName": "simpleString",
62 | "affinity": "TEXT",
63 | "notNull": false
64 | },
65 | {
66 | "fieldPath": "simpleByteArray",
67 | "columnName": "simpleByteArray",
68 | "affinity": "BLOB",
69 | "notNull": false
70 | }
71 | ],
72 | "primaryKey": {
73 | "columnNames": [
74 | "id"
75 | ],
76 | "autoGenerate": false
77 | },
78 | "indices": [],
79 | "foreignKeys": []
80 | },
81 | {
82 | "tableName": "SimpleEntityIndexed",
83 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `simpleBoolean` INTEGER NOT NULL, `simpleByte` INTEGER NOT NULL, `simpleShort` INTEGER NOT NULL, `simpleInt` INTEGER NOT NULL, `simpleLong` INTEGER NOT NULL, `simpleFloat` REAL NOT NULL, `simpleDouble` REAL NOT NULL, `simpleString` TEXT, `simpleByteArray` BLOB, PRIMARY KEY(`id`))",
84 | "fields": [
85 | {
86 | "fieldPath": "id",
87 | "columnName": "id",
88 | "affinity": "INTEGER",
89 | "notNull": true
90 | },
91 | {
92 | "fieldPath": "simpleBoolean",
93 | "columnName": "simpleBoolean",
94 | "affinity": "INTEGER",
95 | "notNull": true
96 | },
97 | {
98 | "fieldPath": "simpleByte",
99 | "columnName": "simpleByte",
100 | "affinity": "INTEGER",
101 | "notNull": true
102 | },
103 | {
104 | "fieldPath": "simpleShort",
105 | "columnName": "simpleShort",
106 | "affinity": "INTEGER",
107 | "notNull": true
108 | },
109 | {
110 | "fieldPath": "simpleInt",
111 | "columnName": "simpleInt",
112 | "affinity": "INTEGER",
113 | "notNull": true
114 | },
115 | {
116 | "fieldPath": "simpleLong",
117 | "columnName": "simpleLong",
118 | "affinity": "INTEGER",
119 | "notNull": true
120 | },
121 | {
122 | "fieldPath": "simpleFloat",
123 | "columnName": "simpleFloat",
124 | "affinity": "REAL",
125 | "notNull": true
126 | },
127 | {
128 | "fieldPath": "simpleDouble",
129 | "columnName": "simpleDouble",
130 | "affinity": "REAL",
131 | "notNull": true
132 | },
133 | {
134 | "fieldPath": "simpleString",
135 | "columnName": "simpleString",
136 | "affinity": "TEXT",
137 | "notNull": false
138 | },
139 | {
140 | "fieldPath": "simpleByteArray",
141 | "columnName": "simpleByteArray",
142 | "affinity": "BLOB",
143 | "notNull": false
144 | }
145 | ],
146 | "primaryKey": {
147 | "columnNames": [
148 | "id"
149 | ],
150 | "autoGenerate": false
151 | },
152 | "indices": [
153 | {
154 | "name": "index_SimpleEntityIndexed_simpleInt",
155 | "unique": false,
156 | "columnNames": [
157 | "simpleInt"
158 | ],
159 | "createSql": "CREATE INDEX `index_SimpleEntityIndexed_simpleInt` ON `${TABLE_NAME}` (`simpleInt`)"
160 | },
161 | {
162 | "name": "index_SimpleEntityIndexed_simpleString",
163 | "unique": false,
164 | "columnNames": [
165 | "simpleString"
166 | ],
167 | "createSql": "CREATE INDEX `index_SimpleEntityIndexed_simpleString` ON `${TABLE_NAME}` (`simpleString`)"
168 | }
169 | ],
170 | "foreignKeys": []
171 | }
172 | ],
173 | "setupQueries": [
174 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
175 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"3440b72ef8e427f23f501299b3de69d9\")"
176 | ]
177 | }
178 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
13 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/Benchmark.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp;
18 |
19 | import android.os.Build;
20 | import android.os.SystemClock;
21 | import android.util.Log;
22 | import android.util.Pair;
23 |
24 | import org.greenrobot.essentials.StringUtils;
25 | import org.greenrobot.essentials.io.FileUtils;
26 |
27 | import java.io.File;
28 | import java.io.FileNotFoundException;
29 | import java.io.IOException;
30 | import java.text.SimpleDateFormat;
31 | import java.util.ArrayList;
32 | import java.util.Arrays;
33 | import java.util.Date;
34 | import java.util.List;
35 |
36 | public class Benchmark {
37 | public static final String TAG = "Benchmark";
38 |
39 | private final List> fixedColumns = new ArrayList<>();
40 | private final List> values = new ArrayList<>();
41 | private final File file;
42 | private final SimpleDateFormat dateFormat;
43 | private final char separator = '\t';
44 |
45 | private String[] headers;
46 | private boolean storeThreadTime;
47 |
48 | private boolean started;
49 | private long threadTimeMillis;
50 | private long timeMillis;
51 | private String name;
52 | private int runs;
53 | private int warmUpRuns;
54 |
55 | public Benchmark(File file) {
56 | this.file = file;
57 | dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
58 | checkForLastHeader(file);
59 | }
60 |
61 | private void checkForLastHeader(File file) {
62 | String contents = null;
63 | try {
64 | contents = FileUtils.readUtf8(file);
65 | } catch (FileNotFoundException e) {
66 | // OK
67 | } catch (IOException e) {
68 | throw new RuntimeException(e);
69 | }
70 | if (contents == null) {
71 | return;
72 | }
73 |
74 | String[] lines = StringUtils.split(contents, '\n');
75 | for (int i = lines.length - 1; i >= 0; i--) {
76 | String[] columnValues = StringUtils.split(lines[i], separator);
77 | if (columnValues.length > 1) {
78 | boolean longValueFound = false;
79 | for (String value : columnValues) {
80 | try {
81 | Long.parseLong(value);
82 | longValueFound = true;
83 | break;
84 | } catch (NumberFormatException e) {
85 | // OK, header candidate
86 | }
87 | }
88 | if (!longValueFound) {
89 | headers = columnValues;
90 | break;
91 | }
92 | }
93 | }
94 | }
95 |
96 | public Benchmark warmUpRuns(int warmUpRuns) {
97 | this.warmUpRuns = warmUpRuns;
98 | return this;
99 | }
100 |
101 | public Benchmark enableThreadTime() {
102 | this.storeThreadTime = true;
103 | return this;
104 | }
105 |
106 | public Benchmark disableThreadTime() {
107 | this.storeThreadTime = false;
108 | return this;
109 | }
110 |
111 | public Benchmark addFixedColumn(String key, String value) {
112 | fixedColumns.add(new Pair(key, value));
113 | return this;
114 | }
115 |
116 | public Benchmark addFixedColumnDevice() {
117 | addFixedColumn("device", Build.MODEL);
118 | return this;
119 | }
120 |
121 | public void start(String name) {
122 | if (started) {
123 | throw new RuntimeException("Already started");
124 | }
125 | started = true;
126 | prepareForNextRun();
127 | if (values.isEmpty()) {
128 | values.addAll(fixedColumns);
129 | String startTime = dateFormat.format(new Date());
130 | values.add(new Pair<>("time", startTime));
131 | }
132 | this.name = name;
133 | threadTimeMillis = SystemClock.currentThreadTimeMillis();
134 | timeMillis = SystemClock.elapsedRealtime();
135 | }
136 |
137 | /**
138 | * Try to give GC & finalization some time to settle down.
139 | */
140 | public void prepareForNextRun() {
141 | for (int i = 0; i < 5; i++) {
142 | System.gc();
143 | System.runFinalization();
144 | try {
145 | Thread.sleep(20);
146 | } catch (InterruptedException e) {
147 | e.printStackTrace();
148 | }
149 | }
150 | }
151 |
152 | public String stop() {
153 | long time = SystemClock.elapsedRealtime() - timeMillis;
154 | long timeThread = SystemClock.currentThreadTimeMillis() - threadTimeMillis;
155 | if (!started) {
156 | throw new RuntimeException("Not started");
157 | }
158 | started = false;
159 |
160 | String logMessage = name + ": " + time + " ms (thread: " + timeThread + " ms)";
161 | values.add(new Pair<>(name, Long.toString(time)));
162 | if (storeThreadTime) {
163 | values.add(new Pair<>(name + "-thread", Long.toString(timeThread)));
164 | }
165 | name = null;
166 | return logMessage;
167 | }
168 |
169 | public void commit() {
170 | runs++;
171 | if (runs > warmUpRuns) {
172 | Log.d(TAG, "Writing results for run " + runs);
173 | String[] collectedHeaders = getAllFirsts(values);
174 | if (!Arrays.equals(collectedHeaders, headers)) {
175 | headers = collectedHeaders;
176 | String line = StringUtils.join(headers, "" + separator) + '\n';
177 | try {
178 | FileUtils.appendUtf8(file, line);
179 | } catch (IOException e) {
180 | throw new RuntimeException("Could not write header in benchmark file", e);
181 | }
182 | }
183 |
184 | StringBuilder line = new StringBuilder();
185 | for (Pair pair : values) {
186 | line.append(pair.second).append(separator);
187 | }
188 | line.append('\n');
189 | try {
190 | FileUtils.appendUtf8(file, line);
191 | } catch (IOException e) {
192 | throw new RuntimeException("Could not write header in benchmark file", e);
193 | }
194 | } else {
195 | Log.d(TAG, "Ignoring results for run " + runs + " (warm up)");
196 | }
197 | values.clear();
198 | }
199 |
200 | private String[] getAllFirsts(List> columns) {
201 | String[] firsts = new String[columns.size()];
202 | for (int i = 0; i < firsts.length; i++) {
203 | firsts[i] = columns.get(i).first;
204 | }
205 | return firsts;
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/MainActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017-2024 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp;
18 |
19 | import android.app.Activity;
20 | import android.content.Context;
21 | import android.content.SharedPreferences;
22 | import android.os.Bundle;
23 | import android.view.View;
24 | import android.view.inputmethod.InputMethodManager;
25 | import android.widget.ArrayAdapter;
26 | import android.widget.EditText;
27 |
28 | import java.util.ArrayList;
29 | import java.util.List;
30 |
31 | import androidx.preference.PreferenceManager;
32 | import io.objectbox.performanceapp.PerfTestRunner.Callback;
33 | import io.objectbox.performanceapp.databinding.ActivityMainBinding;
34 | import io.objectbox.performanceapp.greendao.GreendaoPerfTest;
35 | import io.objectbox.performanceapp.objectbox.ObjectBoxPerfTest;
36 | import io.objectbox.performanceapp.realm.RealmPerfTest;
37 | import io.objectbox.performanceapp.room.RoomPerfTest;
38 |
39 | public class MainActivity extends Activity implements Callback {
40 |
41 | private static final String PREF_TYPE = "io.objectbox.performance.type";
42 | private static final String PREF_RUNS = "io.objectbox.performance.runs";
43 | private static final String PREF_COUNT = "io.objectbox.performance.count";
44 |
45 | private ActivityMainBinding binding;
46 | private PerfTestRunner testRunner;
47 |
48 | @Override
49 | protected void onCreate(Bundle savedInstanceState) {
50 | super.onCreate(savedInstanceState);
51 | binding = ActivityMainBinding.inflate(getLayoutInflater());
52 | setContentView(binding.getRoot());
53 |
54 | binding.buttonRunTest.setOnClickListener(view -> {
55 | binding.buttonRunTest.setEnabled(false);
56 | View currentFocus = MainActivity.this.getCurrentFocus();
57 | if (currentFocus != null) {
58 | InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
59 | if (imm != null) {
60 | imm.hideSoftInputFromWindow(currentFocus.getWindowToken(), 0);
61 | }
62 | currentFocus.clearFocus();
63 | }
64 | boolean objectBox = binding.checkBoxObjectBox.isChecked();
65 | boolean realm = binding.checkBoxRealm.isChecked();
66 | boolean greenDao = binding.checkBoxGreenDao.isChecked();
67 | boolean room = binding.checkBoxRoom.isChecked();
68 | TestType type = (TestType) binding.spinnerTestType.getSelectedItem();
69 |
70 | int runs = getIntegerFromEditTextOrZero(binding.editTextRuns);
71 | int numberEntities = getIntegerFromEditTextOrZero(binding.editTextNumberEntities);
72 |
73 | runTests(type, runs, numberEntities, objectBox, realm, greenDao, room);
74 | });
75 |
76 | ArrayAdapter adapter = new ArrayAdapter<>(
77 | this,
78 | android.R.layout.simple_spinner_item,
79 | TestType.ALL
80 | );
81 | adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
82 | binding.spinnerTestType.setAdapter(adapter);
83 |
84 | // Restore type, runs and count or set defaults.
85 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
86 | int previousTypeSelection = prefs.getInt(PREF_TYPE, 0);
87 | if (previousTypeSelection > TestType.ALL.length - 1 || previousTypeSelection < 0) {
88 | previousTypeSelection = 0;
89 | }
90 | binding.spinnerTestType
91 | .setSelection(previousTypeSelection, false);
92 | binding.editTextRuns
93 | .setText(String.valueOf(prefs.getInt(PREF_RUNS, 1)));
94 | binding.editTextNumberEntities
95 | .setText(String.valueOf(prefs.getInt(PREF_COUNT, 100000)));
96 | }
97 |
98 | private int getIntegerFromEditTextOrZero(EditText editText) {
99 | try {
100 | return Integer.parseInt(editText.getText().toString());
101 | } catch (NumberFormatException e) {
102 | binding.textViewResults.append(e.getMessage() + "\n");
103 | return 0;
104 | }
105 | }
106 |
107 | @Override
108 | protected void onPause() {
109 | super.onPause();
110 | // Save type, runs and count.
111 | PreferenceManager.getDefaultSharedPreferences(this).edit()
112 | .putInt(PREF_TYPE, binding.spinnerTestType.getSelectedItemPosition())
113 | .putInt(PREF_RUNS,
114 | getIntegerFromEditTextOrZero(binding.editTextRuns))
115 | .putInt(PREF_COUNT,
116 | getIntegerFromEditTextOrZero(binding.editTextNumberEntities))
117 | .apply();
118 | }
119 |
120 | @Override
121 | protected void onDestroy() {
122 | if (testRunner != null) {
123 | testRunner.destroy();
124 | }
125 | testRunner = null;
126 | super.onDestroy();
127 | }
128 |
129 | private void runTests(TestType type, int runs, int numberEntities, boolean objectBox, boolean realm, boolean greenDao, boolean room) {
130 | binding.textViewResults.setText("");
131 | List tests = new ArrayList<>();
132 | if (objectBox) {
133 | tests.add(new ObjectBoxPerfTest());
134 | }
135 | if (realm) {
136 | tests.add(new RealmPerfTest());
137 | }
138 | if (greenDao) {
139 | tests.add(new GreendaoPerfTest());
140 | }
141 | if (room) {
142 | tests.add(new RoomPerfTest());
143 | }
144 | testRunner = new PerfTestRunner(this, this, binding.textViewResults, runs, numberEntities);
145 | testRunner.run(type, tests);
146 | }
147 |
148 | @Override
149 | public void done() {
150 | testRunner = null;
151 | runOnUiThread(() -> binding.buttonRunTest.setEnabled(true));
152 | }
153 |
154 | }
155 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/PerfTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp;
18 |
19 | import android.content.Context;
20 | import androidx.annotation.CallSuper;
21 |
22 | import java.util.Random;
23 |
24 | public abstract class PerfTest {
25 |
26 | protected Random random;
27 | protected Context context;
28 | protected PerfTestRunner testRunner;
29 | protected int numberEntities;
30 | protected Benchmark benchmark;
31 |
32 | @CallSuper
33 | public void setUp(Context context, PerfTestRunner testRunner) {
34 | random = new Random();
35 | this.context = context.getApplicationContext();
36 | this.testRunner = testRunner;
37 | }
38 |
39 | public void tearDown() {
40 | }
41 |
42 | protected void log(String text) {
43 | testRunner.log(text);
44 | }
45 |
46 | public abstract String name();
47 |
48 | public abstract void run(TestType type);
49 |
50 | public void setNumberEntities(int numberEntities) {
51 | this.numberEntities = numberEntities;
52 | }
53 |
54 | public void setBenchmark(Benchmark benchmark) {
55 | this.benchmark = benchmark;
56 | }
57 |
58 | protected void startBenchmark(String name) {
59 | benchmark.start(name);
60 | }
61 |
62 | protected void stopBenchmark() {
63 | log(benchmark.stop());
64 | }
65 |
66 | /**
67 | * Convenience for {@link #startBenchmark(String)} followed by {@link #stopBenchmark()}.
68 | */
69 | protected void benchmark(String name, Runnable runnable) {
70 | startBenchmark(name);
71 | runnable.run();
72 | stopBenchmark();
73 | }
74 |
75 | public String randomString() {
76 | return RandomValues.createRandomString(random, 0, 100);
77 | }
78 |
79 | public byte[] randomBytes() {
80 | int length = random.nextInt(100);
81 | byte[] bytes = new byte[length];
82 | random.nextBytes(bytes);
83 | return bytes;
84 | }
85 |
86 | public void allTestsComplete() {
87 | }
88 |
89 | protected void assertEntityCount(long size) {
90 | if (size != numberEntities) {
91 | throw new IllegalStateException("Expected " + numberEntities + " but actual number is " + size);
92 | }
93 | }
94 |
95 | protected void assertGreaterOrEqualToNumberOfEntities(long count) {
96 | if (count < numberEntities) {
97 | throw new IllegalStateException("Expected at least " + numberEntities + " but actual number is " + count);
98 | }
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/PerfTestRunner.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp;
18 |
19 | import android.app.Activity;
20 | import android.app.ActivityManager;
21 | import android.graphics.Color;
22 | import android.os.Build;
23 | import android.os.Environment;
24 | import android.text.Spannable;
25 | import android.text.SpannableString;
26 | import android.text.style.ForegroundColorSpan;
27 | import android.util.Log;
28 | import android.widget.ScrollView;
29 | import android.widget.TextView;
30 |
31 | import java.io.File;
32 | import java.util.Date;
33 | import java.util.List;
34 | import java.util.concurrent.CountDownLatch;
35 | import java.util.concurrent.TimeUnit;
36 |
37 | /**
38 | * Created by Markus on 01.10.2016.
39 | */
40 |
41 | public class PerfTestRunner {
42 |
43 | interface Callback {
44 | void done();
45 | }
46 |
47 | private final Activity activity;
48 | private final Callback callback;
49 | private final TextView textViewResults;
50 | private final int runs;
51 | private final int numberEntities;
52 | private ScrollView scrollViewResults;
53 |
54 | boolean running;
55 | boolean destroyed;
56 |
57 | public PerfTestRunner(Activity activity, Callback callback, TextView textViewResults, int runs, int numberEntities) {
58 | this.activity = activity;
59 | this.callback = callback;
60 | this.textViewResults = textViewResults;
61 | if (textViewResults.getParent() instanceof ScrollView) {
62 | scrollViewResults = (ScrollView) textViewResults.getParent();
63 | }
64 | this.runs = runs;
65 | this.numberEntities = numberEntities;
66 | }
67 |
68 | public void run(final TestType type, final List tests) {
69 | if (running) {
70 | throw new IllegalStateException("Already running");
71 | }
72 | running = true;
73 | Thread thread = new Thread(() -> {
74 | try {
75 | for (PerfTest test : tests) {
76 | if (!destroyed) {
77 | try {
78 | PerfTestRunner.this.run(type, test);
79 | } catch (Exception e) {
80 | logError("Aborted because of " + e.getMessage());
81 | Log.e("PERF", "Error while running tests", e);
82 | }
83 | }
84 | }
85 | } finally {
86 | running = false;
87 | callback.done();
88 | }
89 | });
90 | thread.setPriority(Thread.MAX_PRIORITY);
91 | thread.start();
92 | }
93 |
94 | public void destroy() {
95 | destroyed = true;
96 | }
97 |
98 | public void log(final String text) {
99 | log(text, false);
100 | }
101 |
102 | public void logError(final String text) {
103 | log(text, true);
104 | }
105 |
106 | private void log(final String text, final boolean error) {
107 | Log.d("PERF", text);
108 | final CountDownLatch joinLatch = new CountDownLatch(1);
109 | activity.runOnUiThread(() -> {
110 | if (error) {
111 | Spannable errorSpan = new SpannableString(text.concat("\n"));
112 | errorSpan.setSpan(new ForegroundColorSpan(Color.RED), 0, errorSpan.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
113 | textViewResults.append(errorSpan);
114 | } else {
115 | textViewResults.append(text.concat("\n"));
116 | }
117 | // post so just appended text is visible
118 | if (scrollViewResults != null) {
119 | textViewResults.post(() -> scrollViewResults.fullScroll(ScrollView.FOCUS_DOWN));
120 | }
121 | textViewResults.postDelayed(joinLatch::countDown, 20);
122 | });
123 | try {
124 | boolean ok = joinLatch.await(10, TimeUnit.SECONDS);
125 | if (!ok) {
126 | throw new RuntimeException("Not joined");
127 | }
128 | // Give UI time to settle (> 1 frame)
129 | Thread.sleep(20);
130 | } catch (InterruptedException e) {
131 | throw new RuntimeException(e);
132 | }
133 | }
134 |
135 | private void run(TestType type, PerfTest test) {
136 | printDeviceInfo();
137 |
138 | test.setNumberEntities(numberEntities);
139 | Benchmark benchmark = createBenchmark(type, test, numberEntities);
140 | test.setBenchmark(benchmark);
141 | log("\nStarting tests with " + numberEntities + " entities at " + new Date());
142 | for (int i = 1; i <= runs; i++) {
143 | log("\n" + test.name() + " " + type + " (" + i + "/" + runs + ")\n" +
144 | "------------------------------");
145 | test.setUp(activity, this);
146 |
147 | RuntimeException exDuringRun = null;
148 | try {
149 | test.run(type);
150 | } catch (RuntimeException ex) {
151 | exDuringRun = ex;
152 | }
153 |
154 | RuntimeException exDuringTearDown = null;
155 | try {
156 | test.tearDown();
157 | } catch (RuntimeException ex) {
158 | exDuringTearDown = ex;
159 | }
160 | if (exDuringRun != null) {
161 | throw exDuringRun;
162 | } else if (exDuringTearDown != null) {
163 | throw exDuringTearDown;
164 | }
165 | benchmark.commit();
166 | if (destroyed) {
167 | break;
168 | }
169 | }
170 | test.allTestsComplete();
171 | log("\nTests done at " + new Date());
172 | }
173 |
174 | private void printDeviceInfo() {
175 | log("Model: " + Build.MANUFACTURER + " " + Build.MODEL
176 | + ", Android " + Build.VERSION.RELEASE);
177 |
178 | ActivityManager activityManager =
179 | (ActivityManager) activity.getSystemService(Activity.ACTIVITY_SERVICE);
180 | int memoryClassMb = activityManager.getMemoryClass();
181 | int largeMemoryClassMb = activityManager.getLargeMemoryClass();
182 | log("MemoryClass: " + memoryClassMb + " MB");
183 | log("LargeMemoryClass: " + largeMemoryClassMb + " MB");
184 | }
185 |
186 | protected Benchmark createBenchmark(TestType type, PerfTest test, int numberEntities) {
187 | String name = test.name() + "-" + type.nameShort + "-" + numberEntities + ".tsv";
188 | File dir = Environment.getExternalStorageDirectory();
189 | File file = new File(dir, name);
190 | if (dir == null || !dir.canWrite()) {
191 | File appFile = new File(activity.getFilesDir(), name);
192 | Log.i("PERF", "Using file " + appFile.getAbsolutePath() + " because " + file.getAbsolutePath() +
193 | " is not writable - please grant the storage permission to the app");
194 | file = appFile;
195 | }
196 | return new Benchmark(file);
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/RandomValues.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp;
18 |
19 | import java.util.Random;
20 |
21 | /**
22 | * Helper class to generate a pre-determined set of random values (strings/ints) .
23 | */
24 | public class RandomValues {
25 |
26 | // Fixed seed so we generate the same set of strings every time.
27 | public static final long SEED = -2662502316022774L;
28 | private static final int MIN_LENGTH = 5;
29 | private static final int MAX_LENGTH = 500;
30 |
31 | // limit to a fixed set of chars
32 | private static final char[] CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
33 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
34 | 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
35 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
36 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
37 |
38 | /**
39 | * Creates the same random sequence of strings.
40 | */
41 | public static String[] createFixedRandomStrings(int count) {
42 | return createFixedRandomStrings(count, MIN_LENGTH, MAX_LENGTH);
43 | }
44 |
45 | public static String[] createFixedRandomStrings(int count, int minLength, int maxLength) {
46 | String[] strings = new String[count];
47 | Random random = new Random(SEED);
48 | for (int i = 0; i < count; i++) {
49 | strings[i] = createRandomString(random, minLength, maxLength);
50 | }
51 | return strings;
52 | }
53 |
54 | public static String createRandomString(Random random, int minLength, int maxLength) {
55 | int length = minLength + random.nextInt(maxLength - minLength);
56 | return createRandomString(random, length);
57 | }
58 |
59 | public static String createRandomString(Random random, int length) {
60 | char[] chars = new char[length + 4];
61 | for (int i = 0; i < length; ) {
62 | int intVal = random.nextInt();
63 | chars[i++] = CHARS[((intVal & 0xff) % CHARS.length)];
64 | chars[i++] = CHARS[(((intVal >> 8) & 0xff) % CHARS.length)];
65 | chars[i++] = CHARS[(((intVal >> 16) & 0xff) % CHARS.length)];
66 | chars[i++] = CHARS[(((intVal >> 24) & 0xff) % CHARS.length)];
67 | }
68 | return new String(chars, 0, length);
69 | }
70 |
71 | /**
72 | * Creates the same random sequence of indexes. To be used to select strings by {@link
73 | * #createFixedRandomStrings(int)}.
74 | */
75 | public static int[] createFixedRandomInts(int count, int maxInt) {
76 | int[] ints = new int[count];
77 | Random random = new Random(SEED);
78 | for (int i = 0; i < count; i++) {
79 | ints[i] = random.nextInt(maxInt + 1);
80 | }
81 | return ints;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/TestType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp;
18 |
19 | /**
20 | * Created by Markus on 01.10.2016.
21 | */
22 | public class TestType {
23 | public static final String CRUD = "Basic operations (CRUD)";
24 | public static final String CRUD_SCALARS = "Basic operations (CRUD) - scalars";
25 | public static final String CRUD_INDEXED = "Basic operations (CRUD) - indexed";
26 | public static final String QUERY_STRING = "Query by string";
27 | public static final String QUERY_STRING_INDEXED = "Query by string - indexed";
28 | public static final String QUERY_INTEGER = "Query by integer";
29 | public static final String QUERY_INTEGER_INDEXED = "Query by integer - indexed";
30 | public static final String QUERY_ID = "Query by ID";
31 | public static final String QUERY_ID_RANDOM = "Query by ID - random";
32 |
33 | public static TestType[] ALL = {
34 | new TestType(CRUD, "crud"),
35 | new TestType(CRUD_SCALARS, "crud-scalars"),
36 | new TestType(CRUD_INDEXED, "crud-indexed"),
37 | new TestType(QUERY_STRING, "query-string"),
38 | new TestType(QUERY_STRING_INDEXED, "query-string-indexed"),
39 | new TestType(QUERY_INTEGER, "query-integer"),
40 | new TestType(QUERY_INTEGER_INDEXED, "query-integer-indexed"),
41 | new TestType(QUERY_ID, "query-id"),
42 | new TestType(QUERY_ID_RANDOM, "query-id-random"),
43 | };
44 |
45 | public final String name;
46 | public final String nameShort;
47 |
48 | public TestType(String name, String nameShort) {
49 | this.name = name;
50 | this.nameShort = nameShort;
51 | }
52 |
53 | @Override
54 | public String toString() {
55 | return name;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/greendao/DaoMaster.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp.greendao;
18 |
19 | import android.content.Context;
20 | import android.database.sqlite.SQLiteDatabase;
21 | import android.database.sqlite.SQLiteDatabase.CursorFactory;
22 | import android.util.Log;
23 |
24 | import org.greenrobot.greendao.AbstractDaoMaster;
25 | import org.greenrobot.greendao.database.StandardDatabase;
26 | import org.greenrobot.greendao.database.Database;
27 | import org.greenrobot.greendao.database.DatabaseOpenHelper;
28 | import org.greenrobot.greendao.identityscope.IdentityScopeType;
29 |
30 |
31 | // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
32 | /**
33 | * Master of DAO (schema version 1): knows all DAOs.
34 | */
35 | public class DaoMaster extends AbstractDaoMaster {
36 | public static final int SCHEMA_VERSION = 1;
37 |
38 | /** Creates underlying database table using DAOs. */
39 | public static void createAllTables(Database db, boolean ifNotExists) {
40 | SimpleEntityDao.createTable(db, ifNotExists);
41 | SimpleEntityIndexedDao.createTable(db, ifNotExists);
42 | }
43 |
44 | /** Drops underlying database table using DAOs. */
45 | public static void dropAllTables(Database db, boolean ifExists) {
46 | SimpleEntityDao.dropTable(db, ifExists);
47 | SimpleEntityIndexedDao.dropTable(db, ifExists);
48 | }
49 |
50 | /**
51 | * WARNING: Drops all table on Upgrade! Use only during development.
52 | * Convenience method using a {@link DevOpenHelper}.
53 | */
54 | public static DaoSession newDevSession(Context context, String name) {
55 | Database db = new DevOpenHelper(context, name).getWritableDb();
56 | DaoMaster daoMaster = new DaoMaster(db);
57 | return daoMaster.newSession();
58 | }
59 |
60 | public DaoMaster(SQLiteDatabase db) {
61 | this(new StandardDatabase(db));
62 | }
63 |
64 | public DaoMaster(Database db) {
65 | super(db, SCHEMA_VERSION);
66 | registerDaoClass(SimpleEntityDao.class);
67 | registerDaoClass(SimpleEntityIndexedDao.class);
68 | }
69 |
70 | public DaoSession newSession() {
71 | return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
72 | }
73 |
74 | public DaoSession newSession(IdentityScopeType type) {
75 | return new DaoSession(db, type, daoConfigMap);
76 | }
77 |
78 | /**
79 | * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} -
80 | */
81 | public static abstract class OpenHelper extends DatabaseOpenHelper {
82 | public OpenHelper(Context context, String name) {
83 | super(context, name, SCHEMA_VERSION);
84 | }
85 |
86 | public OpenHelper(Context context, String name, CursorFactory factory) {
87 | super(context, name, factory, SCHEMA_VERSION);
88 | }
89 |
90 | @Override
91 | public void onCreate(Database db) {
92 | Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
93 | createAllTables(db, false);
94 | }
95 | }
96 |
97 | /** WARNING: Drops all table on Upgrade! Use only during development. */
98 | public static class DevOpenHelper extends OpenHelper {
99 | public DevOpenHelper(Context context, String name) {
100 | super(context, name);
101 | }
102 |
103 | public DevOpenHelper(Context context, String name, CursorFactory factory) {
104 | super(context, name, factory);
105 | }
106 |
107 | @Override
108 | public void onUpgrade(Database db, int oldVersion, int newVersion) {
109 | Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
110 | dropAllTables(db, true);
111 | onCreate(db);
112 | }
113 | }
114 |
115 | }
116 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/greendao/DaoSession.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp.greendao;
18 |
19 | import org.greenrobot.greendao.AbstractDao;
20 | import org.greenrobot.greendao.AbstractDaoSession;
21 | import org.greenrobot.greendao.database.Database;
22 | import org.greenrobot.greendao.identityscope.IdentityScopeType;
23 | import org.greenrobot.greendao.internal.DaoConfig;
24 |
25 | import java.util.Map;
26 |
27 | // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
28 |
29 | /**
30 | * {@inheritDoc}
31 | *
32 | * @see org.greenrobot.greendao.AbstractDaoSession
33 | */
34 | public class DaoSession extends AbstractDaoSession {
35 |
36 | private final DaoConfig simpleEntityDaoConfig;
37 | private final DaoConfig simpleEntityIndexedDaoConfig;
38 |
39 | private final SimpleEntityDao simpleEntityDao;
40 | private final SimpleEntityIndexedDao simpleEntityIndexedDao;
41 |
42 | public DaoSession(Database db, IdentityScopeType type, Map>, DaoConfig>
43 | daoConfigMap) {
44 | super(db);
45 |
46 | simpleEntityDaoConfig = daoConfigMap.get(SimpleEntityDao.class).clone();
47 | simpleEntityDaoConfig.initIdentityScope(type);
48 |
49 | simpleEntityIndexedDaoConfig = daoConfigMap.get(SimpleEntityIndexedDao.class).clone();
50 | simpleEntityIndexedDaoConfig.initIdentityScope(type);
51 |
52 | simpleEntityDao = new SimpleEntityDao(simpleEntityDaoConfig, this);
53 | simpleEntityIndexedDao = new SimpleEntityIndexedDao(simpleEntityIndexedDaoConfig, this);
54 |
55 | registerDao(SimpleEntity.class, simpleEntityDao);
56 | registerDao(SimpleEntityIndexed.class, simpleEntityIndexedDao);
57 | }
58 |
59 | public void clear() {
60 | simpleEntityDaoConfig.clearIdentityScope();
61 | simpleEntityIndexedDaoConfig.clearIdentityScope();
62 | }
63 |
64 | public SimpleEntityDao getSimpleEntityDao() {
65 | return simpleEntityDao;
66 | }
67 |
68 | public SimpleEntityIndexedDao getSimpleEntityIndexedDao() {
69 | return simpleEntityIndexedDao;
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/greendao/GreendaoPerfTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp.greendao;
18 |
19 | import android.content.Context;
20 | import android.database.Cursor;
21 |
22 | import org.greenrobot.greendao.database.Database;
23 | import org.greenrobot.greendao.identityscope.IdentityScopeType;
24 | import org.greenrobot.greendao.query.Query;
25 |
26 | import java.util.ArrayList;
27 | import java.util.List;
28 |
29 | import io.objectbox.performanceapp.PerfTest;
30 | import io.objectbox.performanceapp.PerfTestRunner;
31 | import io.objectbox.performanceapp.TestType;
32 | import io.objectbox.performanceapp.greendao.DaoMaster.DevOpenHelper;
33 | import io.objectbox.performanceapp.greendao.SimpleEntityIndexedDao.Properties;
34 |
35 | public class GreendaoPerfTest extends PerfTest {
36 | public static final String DB_NAME = "sqlite-greendao";
37 |
38 | private Database db;
39 | private DaoSession daoSession;
40 | private SimpleEntityDao dao;
41 |
42 | private boolean versionLoggedOnce;
43 | private SimpleEntityIndexedDao daoIndexed;
44 |
45 | @Override
46 | public String name() {
47 | return "greenDAO";
48 | }
49 |
50 | public void setUp(Context context, PerfTestRunner testRunner) {
51 | super.setUp(context, testRunner);
52 | boolean deleted = context.deleteDatabase(DB_NAME);
53 | if (deleted) {
54 | log("DB existed before start - deleted");
55 | }
56 | db = new DevOpenHelper(context, DB_NAME).getWritableDb();
57 | daoSession = new DaoMaster(db).newSession(IdentityScopeType.None);
58 | dao = daoSession.getSimpleEntityDao();
59 | daoIndexed = daoSession.getSimpleEntityIndexedDao();
60 |
61 | if (!versionLoggedOnce) {
62 | Cursor cursor = db.rawQuery("select sqlite_version() AS sqlite_version", null);
63 | if (cursor != null) {
64 | try {
65 | if (cursor.moveToFirst()) {
66 | log("SQLite version " + cursor.getString(0));
67 | }
68 | } finally {
69 | cursor.close();
70 | }
71 | }
72 | versionLoggedOnce = true;
73 | }
74 | }
75 |
76 | @Override
77 | public void run(TestType type) {
78 | switch (type.name) {
79 | case TestType.CRUD:
80 | runBatchPerfTest(false);
81 | break;
82 | case TestType.CRUD_SCALARS:
83 | runBatchPerfTest(true);
84 | break;
85 | case TestType.CRUD_INDEXED:
86 | runBatchPerfTestIndexed();
87 | break;
88 | case TestType.QUERY_STRING:
89 | runQueryByString();
90 | break;
91 | case TestType.QUERY_STRING_INDEXED:
92 | runQueryByStringIndexed();
93 | break;
94 | case TestType.QUERY_ID:
95 | runQueryById(false);
96 | break;
97 | case TestType.QUERY_ID_RANDOM:
98 | runQueryById(true);
99 | break;
100 | }
101 | }
102 |
103 | public void runBatchPerfTest(boolean scalarsOnly) {
104 | List list = new ArrayList<>(numberEntities);
105 | for (int i = 0; i < numberEntities; i++) {
106 | list.add(createEntity((long) i, scalarsOnly));
107 | }
108 | startBenchmark("insert");
109 | dao.insertInTx(list);
110 | stopBenchmark();
111 |
112 | for (SimpleEntity entity : list) {
113 | if (scalarsOnly) {
114 | setRandomScalars(entity);
115 | } else {
116 | setRandomValues(entity);
117 | }
118 | }
119 | startBenchmark("update");
120 | dao.updateInTx(list);
121 | stopBenchmark();
122 |
123 | list = null;
124 |
125 | startBenchmark("load");
126 | List reloaded = dao.loadAll();
127 | stopBenchmark();
128 |
129 | startBenchmark("access");
130 | accessAll(reloaded);
131 | stopBenchmark();
132 |
133 | startBenchmark("delete");
134 | dao.deleteInTx(reloaded);
135 | stopBenchmark();
136 | }
137 |
138 | protected void setRandomValues(SimpleEntity entity) {
139 | setRandomScalars(entity);
140 | entity.setSimpleString(randomString());
141 | entity.setSimpleByteArray(randomBytes());
142 | }
143 |
144 | private void setRandomScalars(SimpleEntity entity) {
145 | entity.setSimpleBoolean(random.nextBoolean());
146 | entity.setSimpleByte((byte) random.nextInt());
147 | entity.setSimpleShort((short) random.nextInt());
148 | entity.setSimpleInt(random.nextInt());
149 | entity.setSimpleLong(random.nextLong());
150 | entity.setSimpleDouble(random.nextDouble());
151 | entity.setSimpleFloat(random.nextFloat());
152 | }
153 |
154 | public SimpleEntity createEntity(Long key, boolean scalarsOnly) {
155 | SimpleEntity entity = new SimpleEntity();
156 | if (key != null) {
157 | entity.setId(key);
158 | }
159 | if (scalarsOnly) {
160 | setRandomScalars(entity);
161 | } else {
162 | setRandomValues(entity);
163 | }
164 | return entity;
165 | }
166 |
167 | protected void accessAll(List list) {
168 | for (SimpleEntity entity : list) {
169 | entity.getId();
170 | entity.getSimpleBoolean();
171 | entity.getSimpleByte();
172 | entity.getSimpleShort();
173 | entity.getSimpleInt();
174 | entity.getSimpleLong();
175 | entity.getSimpleFloat();
176 | entity.getSimpleDouble();
177 | entity.getSimpleString();
178 | entity.getSimpleByteArray();
179 | }
180 | }
181 |
182 | public void runBatchPerfTestIndexed() {
183 | List list = new ArrayList<>(numberEntities);
184 | for (int i = 0; i < numberEntities; i++) {
185 | list.add(createEntityIndexed((long) i));
186 | }
187 | startBenchmark("insert");
188 | daoIndexed.insertInTx(list);
189 | stopBenchmark();
190 |
191 | for (SimpleEntityIndexed entity : list) {
192 | setRandomValues(entity);
193 | }
194 | startBenchmark("update");
195 | daoIndexed.updateInTx(list);
196 | stopBenchmark();
197 |
198 | list = null;
199 |
200 | startBenchmark("load");
201 | List reloaded = daoIndexed.loadAll();
202 | stopBenchmark();
203 |
204 | startBenchmark("access");
205 | accessAllIndexed(reloaded);
206 | stopBenchmark();
207 |
208 | startBenchmark("delete");
209 | daoIndexed.deleteInTx(reloaded);
210 | stopBenchmark();
211 | }
212 |
213 | protected void setRandomValues(SimpleEntityIndexed entity) {
214 | entity.setSimpleBoolean(random.nextBoolean());
215 | entity.setSimpleByte((byte) random.nextInt());
216 | entity.setSimpleShort((short) random.nextInt());
217 | entity.setSimpleInt(random.nextInt());
218 | entity.setSimpleLong(random.nextLong());
219 | entity.setSimpleDouble(random.nextDouble());
220 | entity.setSimpleFloat(random.nextFloat());
221 | entity.setSimpleString(randomString());
222 | entity.setSimpleByteArray(randomBytes());
223 | }
224 |
225 | public SimpleEntityIndexed createEntityIndexed(Long key) {
226 | SimpleEntityIndexed entity = new SimpleEntityIndexed();
227 | if (key != null) {
228 | entity.setId(key);
229 | }
230 | setRandomValues(entity);
231 | return entity;
232 | }
233 |
234 | protected void accessAllIndexed(List list) {
235 | for (SimpleEntityIndexed entity : list) {
236 | entity.getId();
237 | entity.getSimpleBoolean();
238 | entity.getSimpleByte();
239 | entity.getSimpleShort();
240 | entity.getSimpleInt();
241 | entity.getSimpleLong();
242 | entity.getSimpleFloat();
243 | entity.getSimpleDouble();
244 | entity.getSimpleString();
245 | entity.getSimpleByteArray();
246 | }
247 | }
248 |
249 | private void runQueryByString() {
250 | if (numberEntities > 10000) {
251 | log("Reduce number of entities to 10000 to avoid extremely long test runs");
252 | return;
253 | }
254 | List entities = new ArrayList<>(numberEntities);
255 | for (int i = 0; i < numberEntities; i++) {
256 | entities.add(createEntity((long) i, false));
257 | }
258 |
259 | startBenchmark("insert");
260 | dao.insertInTx(entities);
261 | stopBenchmark();
262 |
263 | String[] stringsToLookup = new String[numberEntities];
264 | for (int i = 0; i < numberEntities; i++) {
265 | String text = "";
266 | while (text.length() < 2) {
267 | text = entities.get(random.nextInt(numberEntities)).getSimpleString();
268 | }
269 | stringsToLookup[i] = text;
270 | }
271 |
272 | long entitiesFound = 0;
273 | startBenchmark("query");
274 | Query query = dao.queryBuilder().where(SimpleEntityDao.Properties.SimpleString.eq(null)).build();
275 | db.beginTransaction();
276 | for (int i = 0; i < numberEntities; i++) {
277 | query.setParameter(0, stringsToLookup[i]);
278 | List result = query.list();
279 | accessAll(result);
280 | entitiesFound += result.size();
281 | }
282 | db.endTransaction();
283 | stopBenchmark();
284 | log("Entities found: " + entitiesFound);
285 | }
286 |
287 | private void runQueryByStringIndexed() {
288 | List entities = new ArrayList<>(numberEntities);
289 | for (int i = 0; i < numberEntities; i++) {
290 | entities.add(createEntityIndexed((long) i));
291 | }
292 |
293 | startBenchmark("insert");
294 | daoIndexed.insertInTx(entities);
295 | stopBenchmark();
296 |
297 | String[] stringsToLookup = new String[numberEntities];
298 | for (int i = 0; i < numberEntities; i++) {
299 | String text = "";
300 | while (text.length() < 2) {
301 | text = entities.get(random.nextInt(numberEntities)).getSimpleString();
302 | }
303 | stringsToLookup[i] = text;
304 | }
305 |
306 | long entitiesFound = 0;
307 | startBenchmark("query");
308 | Query query = daoIndexed.queryBuilder().where(Properties.SimpleString.eq(null)).build();
309 | db.beginTransaction();
310 | for (int i = 0; i < numberEntities; i++) {
311 | query.setParameter(0, stringsToLookup[i]);
312 | List result = query.list();
313 | accessAllIndexed(result);
314 | entitiesFound += result.size();
315 | }
316 | db.endTransaction();
317 | stopBenchmark();
318 | log("Entities found: " + entitiesFound);
319 | }
320 |
321 | private void runQueryById(boolean randomIds) {
322 | List entities = new ArrayList<>(numberEntities);
323 | for (int i = 0; i < numberEntities; i++) {
324 | entities.add(createEntity((long) i, false));
325 | }
326 |
327 | startBenchmark("insert");
328 | dao.insertInTx(entities);
329 | stopBenchmark();
330 |
331 | assertEntityCount(dao.count());
332 |
333 | long[] idsToLookup = new long[numberEntities];
334 | for (int i = 0; i < numberEntities; i++) {
335 | idsToLookup[i] = randomIds ? random.nextInt(numberEntities) : i;
336 | }
337 |
338 | startBenchmark("query");
339 | for (int i = 0; i < numberEntities; i++) {
340 | SimpleEntity entity = dao.load(idsToLookup[i]);
341 | accessAll(entity);
342 | }
343 | stopBenchmark();
344 | }
345 |
346 | private void accessAll(SimpleEntity entity) {
347 | entity.getId();
348 | entity.getSimpleBoolean();
349 | entity.getSimpleByte();
350 | entity.getSimpleShort();
351 | entity.getSimpleInt();
352 | entity.getSimpleLong();
353 | entity.getSimpleFloat();
354 | entity.getSimpleDouble();
355 | entity.getSimpleString();
356 | entity.getSimpleByteArray();
357 | }
358 |
359 | @Override
360 | public void tearDown() {
361 | daoSession.getDatabase().close();
362 | boolean deleted = context.deleteDatabase(DB_NAME);
363 | log("DB deleted: " + deleted);
364 | }
365 |
366 | }
367 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/greendao/SimpleEntity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp.greendao;
18 |
19 | import org.greenrobot.greendao.annotation.*;
20 |
21 | // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. Enable "keep" sections if you want to edit.
22 |
23 | /**
24 | * Entity mapped to table "SIMPLE_ENTITY".
25 | */
26 | @Entity
27 | public class SimpleEntity {
28 |
29 | @Id
30 | private long id;
31 | private boolean simpleBoolean;
32 | private byte simpleByte;
33 | private short simpleShort;
34 | private int simpleInt;
35 | private long simpleLong;
36 | private float simpleFloat;
37 | private double simpleDouble;
38 | private String simpleString;
39 | private byte[] simpleByteArray;
40 |
41 | @Generated
42 | public SimpleEntity() {
43 | }
44 |
45 | public SimpleEntity(long id) {
46 | this.id = id;
47 | }
48 |
49 | @Generated
50 | public SimpleEntity(long id, boolean simpleBoolean, byte simpleByte, short simpleShort, int simpleInt, long simpleLong, float simpleFloat, double simpleDouble, String simpleString, byte[] simpleByteArray) {
51 | this.id = id;
52 | this.simpleBoolean = simpleBoolean;
53 | this.simpleByte = simpleByte;
54 | this.simpleShort = simpleShort;
55 | this.simpleInt = simpleInt;
56 | this.simpleLong = simpleLong;
57 | this.simpleFloat = simpleFloat;
58 | this.simpleDouble = simpleDouble;
59 | this.simpleString = simpleString;
60 | this.simpleByteArray = simpleByteArray;
61 | }
62 |
63 | public long getId() {
64 | return id;
65 | }
66 |
67 | public void setId(long id) {
68 | this.id = id;
69 | }
70 |
71 | public boolean getSimpleBoolean() {
72 | return simpleBoolean;
73 | }
74 |
75 | public void setSimpleBoolean(boolean simpleBoolean) {
76 | this.simpleBoolean = simpleBoolean;
77 | }
78 |
79 | public byte getSimpleByte() {
80 | return simpleByte;
81 | }
82 |
83 | public void setSimpleByte(byte simpleByte) {
84 | this.simpleByte = simpleByte;
85 | }
86 |
87 | public short getSimpleShort() {
88 | return simpleShort;
89 | }
90 |
91 | public void setSimpleShort(short simpleShort) {
92 | this.simpleShort = simpleShort;
93 | }
94 |
95 | public int getSimpleInt() {
96 | return simpleInt;
97 | }
98 |
99 | public void setSimpleInt(int simpleInt) {
100 | this.simpleInt = simpleInt;
101 | }
102 |
103 | public long getSimpleLong() {
104 | return simpleLong;
105 | }
106 |
107 | public void setSimpleLong(long simpleLong) {
108 | this.simpleLong = simpleLong;
109 | }
110 |
111 | public float getSimpleFloat() {
112 | return simpleFloat;
113 | }
114 |
115 | public void setSimpleFloat(float simpleFloat) {
116 | this.simpleFloat = simpleFloat;
117 | }
118 |
119 | public double getSimpleDouble() {
120 | return simpleDouble;
121 | }
122 |
123 | public void setSimpleDouble(double simpleDouble) {
124 | this.simpleDouble = simpleDouble;
125 | }
126 |
127 | public String getSimpleString() {
128 | return simpleString;
129 | }
130 |
131 | public void setSimpleString(String simpleString) {
132 | this.simpleString = simpleString;
133 | }
134 |
135 | public byte[] getSimpleByteArray() {
136 | return simpleByteArray;
137 | }
138 |
139 | public void setSimpleByteArray(byte[] simpleByteArray) {
140 | this.simpleByteArray = simpleByteArray;
141 | }
142 |
143 | }
144 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/greendao/SimpleEntityDao.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp.greendao;
18 |
19 | import android.database.Cursor;
20 | import android.database.sqlite.SQLiteStatement;
21 |
22 | import org.greenrobot.greendao.AbstractDao;
23 | import org.greenrobot.greendao.Property;
24 | import org.greenrobot.greendao.internal.DaoConfig;
25 | import org.greenrobot.greendao.database.Database;
26 | import org.greenrobot.greendao.database.DatabaseStatement;
27 |
28 | // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
29 | /**
30 | * DAO for table "SIMPLE_ENTITY".
31 | */
32 | public class SimpleEntityDao extends AbstractDao {
33 |
34 | public static final String TABLENAME = "SIMPLE_ENTITY";
35 |
36 | /**
37 | * Properties of entity SimpleEntity.
38 | * Can be used for QueryBuilder and for referencing column names.
39 | */
40 | public static class Properties {
41 | public final static Property Id = new Property(0, long.class, "id", true, "_id");
42 | public final static Property SimpleBoolean = new Property(1, boolean.class, "simpleBoolean", false, "SIMPLE_BOOLEAN");
43 | public final static Property SimpleByte = new Property(2, byte.class, "simpleByte", false, "SIMPLE_BYTE");
44 | public final static Property SimpleShort = new Property(3, short.class, "simpleShort", false, "SIMPLE_SHORT");
45 | public final static Property SimpleInt = new Property(4, int.class, "simpleInt", false, "SIMPLE_INT");
46 | public final static Property SimpleLong = new Property(5, long.class, "simpleLong", false, "SIMPLE_LONG");
47 | public final static Property SimpleFloat = new Property(6, float.class, "simpleFloat", false, "SIMPLE_FLOAT");
48 | public final static Property SimpleDouble = new Property(7, double.class, "simpleDouble", false, "SIMPLE_DOUBLE");
49 | public final static Property SimpleString = new Property(8, String.class, "simpleString", false, "SIMPLE_STRING");
50 | public final static Property SimpleByteArray = new Property(9, byte[].class, "simpleByteArray", false, "SIMPLE_BYTE_ARRAY");
51 | }
52 |
53 |
54 | public SimpleEntityDao(DaoConfig config) {
55 | super(config);
56 | }
57 |
58 | public SimpleEntityDao(DaoConfig config, DaoSession daoSession) {
59 | super(config, daoSession);
60 | }
61 |
62 | /** Creates the underlying database table. */
63 | public static void createTable(Database db, boolean ifNotExists) {
64 | String constraint = ifNotExists? "IF NOT EXISTS ": "";
65 | db.execSQL("CREATE TABLE " + constraint + "\"SIMPLE_ENTITY\" (" + //
66 | "\"_id\" INTEGER PRIMARY KEY NOT NULL ," + // 0: id
67 | "\"SIMPLE_BOOLEAN\" INTEGER NOT NULL ," + // 1: simpleBoolean
68 | "\"SIMPLE_BYTE\" INTEGER NOT NULL ," + // 2: simpleByte
69 | "\"SIMPLE_SHORT\" INTEGER NOT NULL ," + // 3: simpleShort
70 | "\"SIMPLE_INT\" INTEGER NOT NULL ," + // 4: simpleInt
71 | "\"SIMPLE_LONG\" INTEGER NOT NULL ," + // 5: simpleLong
72 | "\"SIMPLE_FLOAT\" REAL NOT NULL ," + // 6: simpleFloat
73 | "\"SIMPLE_DOUBLE\" REAL NOT NULL ," + // 7: simpleDouble
74 | "\"SIMPLE_STRING\" TEXT," + // 8: simpleString
75 | "\"SIMPLE_BYTE_ARRAY\" BLOB);"); // 9: simpleByteArray
76 | }
77 |
78 | /** Drops the underlying database table. */
79 | public static void dropTable(Database db, boolean ifExists) {
80 | String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"SIMPLE_ENTITY\"";
81 | db.execSQL(sql);
82 | }
83 |
84 | @Override
85 | protected final void bindValues(DatabaseStatement stmt, SimpleEntity entity) {
86 | stmt.clearBindings();
87 | stmt.bindLong(1, entity.getId());
88 | stmt.bindLong(2, entity.getSimpleBoolean() ? 1L: 0L);
89 | stmt.bindLong(3, entity.getSimpleByte());
90 | stmt.bindLong(4, entity.getSimpleShort());
91 | stmt.bindLong(5, entity.getSimpleInt());
92 | stmt.bindLong(6, entity.getSimpleLong());
93 | stmt.bindDouble(7, entity.getSimpleFloat());
94 | stmt.bindDouble(8, entity.getSimpleDouble());
95 |
96 | String simpleString = entity.getSimpleString();
97 | if (simpleString != null) {
98 | stmt.bindString(9, simpleString);
99 | }
100 |
101 | byte[] simpleByteArray = entity.getSimpleByteArray();
102 | if (simpleByteArray != null) {
103 | stmt.bindBlob(10, simpleByteArray);
104 | }
105 | }
106 |
107 | @Override
108 | protected final void bindValues(SQLiteStatement stmt, SimpleEntity entity) {
109 | stmt.clearBindings();
110 | stmt.bindLong(1, entity.getId());
111 | stmt.bindLong(2, entity.getSimpleBoolean() ? 1L: 0L);
112 | stmt.bindLong(3, entity.getSimpleByte());
113 | stmt.bindLong(4, entity.getSimpleShort());
114 | stmt.bindLong(5, entity.getSimpleInt());
115 | stmt.bindLong(6, entity.getSimpleLong());
116 | stmt.bindDouble(7, entity.getSimpleFloat());
117 | stmt.bindDouble(8, entity.getSimpleDouble());
118 |
119 | String simpleString = entity.getSimpleString();
120 | if (simpleString != null) {
121 | stmt.bindString(9, simpleString);
122 | }
123 |
124 | byte[] simpleByteArray = entity.getSimpleByteArray();
125 | if (simpleByteArray != null) {
126 | stmt.bindBlob(10, simpleByteArray);
127 | }
128 | }
129 |
130 | @Override
131 | public Long readKey(Cursor cursor, int offset) {
132 | return cursor.getLong(offset + 0);
133 | }
134 |
135 | @Override
136 | public SimpleEntity readEntity(Cursor cursor, int offset) {
137 | SimpleEntity entity = new SimpleEntity( //
138 | cursor.getLong(offset + 0), // id
139 | cursor.getShort(offset + 1) != 0, // simpleBoolean
140 | (byte) cursor.getShort(offset + 2), // simpleByte
141 | cursor.getShort(offset + 3), // simpleShort
142 | cursor.getInt(offset + 4), // simpleInt
143 | cursor.getLong(offset + 5), // simpleLong
144 | cursor.getFloat(offset + 6), // simpleFloat
145 | cursor.getDouble(offset + 7), // simpleDouble
146 | cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8), // simpleString
147 | cursor.isNull(offset + 9) ? null : cursor.getBlob(offset + 9) // simpleByteArray
148 | );
149 | return entity;
150 | }
151 |
152 | @Override
153 | public void readEntity(Cursor cursor, SimpleEntity entity, int offset) {
154 | entity.setId(cursor.getLong(offset + 0));
155 | entity.setSimpleBoolean(cursor.getShort(offset + 1) != 0);
156 | entity.setSimpleByte((byte) cursor.getShort(offset + 2));
157 | entity.setSimpleShort(cursor.getShort(offset + 3));
158 | entity.setSimpleInt(cursor.getInt(offset + 4));
159 | entity.setSimpleLong(cursor.getLong(offset + 5));
160 | entity.setSimpleFloat(cursor.getFloat(offset + 6));
161 | entity.setSimpleDouble(cursor.getDouble(offset + 7));
162 | entity.setSimpleString(cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8));
163 | entity.setSimpleByteArray(cursor.isNull(offset + 9) ? null : cursor.getBlob(offset + 9));
164 | }
165 |
166 | @Override
167 | protected final Long updateKeyAfterInsert(SimpleEntity entity, long rowId) {
168 | entity.setId(rowId);
169 | return rowId;
170 | }
171 |
172 | @Override
173 | public Long getKey(SimpleEntity entity) {
174 | if(entity != null) {
175 | return entity.getId();
176 | } else {
177 | return null;
178 | }
179 | }
180 |
181 | @Override
182 | public boolean hasKey(SimpleEntity entity) {
183 | throw new UnsupportedOperationException("Unsupported for entities with a non-null key");
184 | }
185 |
186 | @Override
187 | protected final boolean isEntityUpdateable() {
188 | return true;
189 | }
190 |
191 | }
192 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/greendao/SimpleEntityIndexed.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp.greendao;
18 |
19 | import org.greenrobot.greendao.annotation.*;
20 |
21 | // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. Enable "keep" sections if you want to edit.
22 |
23 | /**
24 | * Entity mapped to table "SIMPLE_ENTITY_INDEXED".
25 | */
26 | @Entity
27 | public class SimpleEntityIndexed {
28 |
29 | @Id
30 | private long id;
31 | private boolean simpleBoolean;
32 | private byte simpleByte;
33 | private short simpleShort;
34 |
35 | @Index
36 | private int simpleInt;
37 | private long simpleLong;
38 | private float simpleFloat;
39 | private double simpleDouble;
40 |
41 | @Index
42 | private String simpleString;
43 | private byte[] simpleByteArray;
44 |
45 | @Generated
46 | public SimpleEntityIndexed() {
47 | }
48 |
49 | public SimpleEntityIndexed(long id) {
50 | this.id = id;
51 | }
52 |
53 | @Generated
54 | public SimpleEntityIndexed(long id, boolean simpleBoolean, byte simpleByte, short simpleShort, int simpleInt, long simpleLong, float simpleFloat, double simpleDouble, String simpleString, byte[] simpleByteArray) {
55 | this.id = id;
56 | this.simpleBoolean = simpleBoolean;
57 | this.simpleByte = simpleByte;
58 | this.simpleShort = simpleShort;
59 | this.simpleInt = simpleInt;
60 | this.simpleLong = simpleLong;
61 | this.simpleFloat = simpleFloat;
62 | this.simpleDouble = simpleDouble;
63 | this.simpleString = simpleString;
64 | this.simpleByteArray = simpleByteArray;
65 | }
66 |
67 | public long getId() {
68 | return id;
69 | }
70 |
71 | public void setId(long id) {
72 | this.id = id;
73 | }
74 |
75 | public boolean getSimpleBoolean() {
76 | return simpleBoolean;
77 | }
78 |
79 | public void setSimpleBoolean(boolean simpleBoolean) {
80 | this.simpleBoolean = simpleBoolean;
81 | }
82 |
83 | public byte getSimpleByte() {
84 | return simpleByte;
85 | }
86 |
87 | public void setSimpleByte(byte simpleByte) {
88 | this.simpleByte = simpleByte;
89 | }
90 |
91 | public short getSimpleShort() {
92 | return simpleShort;
93 | }
94 |
95 | public void setSimpleShort(short simpleShort) {
96 | this.simpleShort = simpleShort;
97 | }
98 |
99 | public int getSimpleInt() {
100 | return simpleInt;
101 | }
102 |
103 | public void setSimpleInt(int simpleInt) {
104 | this.simpleInt = simpleInt;
105 | }
106 |
107 | public long getSimpleLong() {
108 | return simpleLong;
109 | }
110 |
111 | public void setSimpleLong(long simpleLong) {
112 | this.simpleLong = simpleLong;
113 | }
114 |
115 | public float getSimpleFloat() {
116 | return simpleFloat;
117 | }
118 |
119 | public void setSimpleFloat(float simpleFloat) {
120 | this.simpleFloat = simpleFloat;
121 | }
122 |
123 | public double getSimpleDouble() {
124 | return simpleDouble;
125 | }
126 |
127 | public void setSimpleDouble(double simpleDouble) {
128 | this.simpleDouble = simpleDouble;
129 | }
130 |
131 | public String getSimpleString() {
132 | return simpleString;
133 | }
134 |
135 | public void setSimpleString(String simpleString) {
136 | this.simpleString = simpleString;
137 | }
138 |
139 | public byte[] getSimpleByteArray() {
140 | return simpleByteArray;
141 | }
142 |
143 | public void setSimpleByteArray(byte[] simpleByteArray) {
144 | this.simpleByteArray = simpleByteArray;
145 | }
146 |
147 | }
148 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/greendao/SimpleEntityIndexedDao.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp.greendao;
18 |
19 | import android.database.Cursor;
20 | import android.database.sqlite.SQLiteStatement;
21 |
22 | import org.greenrobot.greendao.AbstractDao;
23 | import org.greenrobot.greendao.Property;
24 | import org.greenrobot.greendao.internal.DaoConfig;
25 | import org.greenrobot.greendao.database.Database;
26 | import org.greenrobot.greendao.database.DatabaseStatement;
27 |
28 | // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
29 | /**
30 | * DAO for table "SIMPLE_ENTITY_INDEXED".
31 | */
32 | public class SimpleEntityIndexedDao extends AbstractDao {
33 |
34 | public static final String TABLENAME = "SIMPLE_ENTITY_INDEXED";
35 |
36 | /**
37 | * Properties of entity SimpleEntityIndexed.
38 | * Can be used for QueryBuilder and for referencing column names.
39 | */
40 | public static class Properties {
41 | public final static Property Id = new Property(0, long.class, "id", true, "_id");
42 | public final static Property SimpleBoolean = new Property(1, boolean.class, "simpleBoolean", false, "SIMPLE_BOOLEAN");
43 | public final static Property SimpleByte = new Property(2, byte.class, "simpleByte", false, "SIMPLE_BYTE");
44 | public final static Property SimpleShort = new Property(3, short.class, "simpleShort", false, "SIMPLE_SHORT");
45 | public final static Property SimpleInt = new Property(4, int.class, "simpleInt", false, "SIMPLE_INT");
46 | public final static Property SimpleLong = new Property(5, long.class, "simpleLong", false, "SIMPLE_LONG");
47 | public final static Property SimpleFloat = new Property(6, float.class, "simpleFloat", false, "SIMPLE_FLOAT");
48 | public final static Property SimpleDouble = new Property(7, double.class, "simpleDouble", false, "SIMPLE_DOUBLE");
49 | public final static Property SimpleString = new Property(8, String.class, "simpleString", false, "SIMPLE_STRING");
50 | public final static Property SimpleByteArray = new Property(9, byte[].class, "simpleByteArray", false, "SIMPLE_BYTE_ARRAY");
51 | }
52 |
53 |
54 | public SimpleEntityIndexedDao(DaoConfig config) {
55 | super(config);
56 | }
57 |
58 | public SimpleEntityIndexedDao(DaoConfig config, DaoSession daoSession) {
59 | super(config, daoSession);
60 | }
61 |
62 | /** Creates the underlying database table. */
63 | public static void createTable(Database db, boolean ifNotExists) {
64 | String constraint = ifNotExists? "IF NOT EXISTS ": "";
65 | db.execSQL("CREATE TABLE " + constraint + "\"SIMPLE_ENTITY_INDEXED\" (" + //
66 | "\"_id\" INTEGER PRIMARY KEY NOT NULL ," + // 0: id
67 | "\"SIMPLE_BOOLEAN\" INTEGER NOT NULL ," + // 1: simpleBoolean
68 | "\"SIMPLE_BYTE\" INTEGER NOT NULL ," + // 2: simpleByte
69 | "\"SIMPLE_SHORT\" INTEGER NOT NULL ," + // 3: simpleShort
70 | "\"SIMPLE_INT\" INTEGER NOT NULL ," + // 4: simpleInt
71 | "\"SIMPLE_LONG\" INTEGER NOT NULL ," + // 5: simpleLong
72 | "\"SIMPLE_FLOAT\" REAL NOT NULL ," + // 6: simpleFloat
73 | "\"SIMPLE_DOUBLE\" REAL NOT NULL ," + // 7: simpleDouble
74 | "\"SIMPLE_STRING\" TEXT," + // 8: simpleString
75 | "\"SIMPLE_BYTE_ARRAY\" BLOB);"); // 9: simpleByteArray
76 | // Add Indexes
77 | db.execSQL("CREATE INDEX " + constraint + "IDX_SIMPLE_ENTITY_INDEXED_SIMPLE_INT ON \"SIMPLE_ENTITY_INDEXED\"" +
78 | " (\"SIMPLE_INT\");");
79 | db.execSQL("CREATE INDEX " + constraint + "IDX_SIMPLE_ENTITY_INDEXED_SIMPLE_STRING ON \"SIMPLE_ENTITY_INDEXED\"" +
80 | " (\"SIMPLE_STRING\");");
81 | }
82 |
83 | /** Drops the underlying database table. */
84 | public static void dropTable(Database db, boolean ifExists) {
85 | String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"SIMPLE_ENTITY_INDEXED\"";
86 | db.execSQL(sql);
87 | }
88 |
89 | @Override
90 | protected final void bindValues(DatabaseStatement stmt, SimpleEntityIndexed entity) {
91 | stmt.clearBindings();
92 | stmt.bindLong(1, entity.getId());
93 | stmt.bindLong(2, entity.getSimpleBoolean() ? 1L: 0L);
94 | stmt.bindLong(3, entity.getSimpleByte());
95 | stmt.bindLong(4, entity.getSimpleShort());
96 | stmt.bindLong(5, entity.getSimpleInt());
97 | stmt.bindLong(6, entity.getSimpleLong());
98 | stmt.bindDouble(7, entity.getSimpleFloat());
99 | stmt.bindDouble(8, entity.getSimpleDouble());
100 |
101 | String simpleString = entity.getSimpleString();
102 | if (simpleString != null) {
103 | stmt.bindString(9, simpleString);
104 | }
105 |
106 | byte[] simpleByteArray = entity.getSimpleByteArray();
107 | if (simpleByteArray != null) {
108 | stmt.bindBlob(10, simpleByteArray);
109 | }
110 | }
111 |
112 | @Override
113 | protected final void bindValues(SQLiteStatement stmt, SimpleEntityIndexed entity) {
114 | stmt.clearBindings();
115 | stmt.bindLong(1, entity.getId());
116 | stmt.bindLong(2, entity.getSimpleBoolean() ? 1L: 0L);
117 | stmt.bindLong(3, entity.getSimpleByte());
118 | stmt.bindLong(4, entity.getSimpleShort());
119 | stmt.bindLong(5, entity.getSimpleInt());
120 | stmt.bindLong(6, entity.getSimpleLong());
121 | stmt.bindDouble(7, entity.getSimpleFloat());
122 | stmt.bindDouble(8, entity.getSimpleDouble());
123 |
124 | String simpleString = entity.getSimpleString();
125 | if (simpleString != null) {
126 | stmt.bindString(9, simpleString);
127 | }
128 |
129 | byte[] simpleByteArray = entity.getSimpleByteArray();
130 | if (simpleByteArray != null) {
131 | stmt.bindBlob(10, simpleByteArray);
132 | }
133 | }
134 |
135 | @Override
136 | public Long readKey(Cursor cursor, int offset) {
137 | return cursor.getLong(offset + 0);
138 | }
139 |
140 | @Override
141 | public SimpleEntityIndexed readEntity(Cursor cursor, int offset) {
142 | SimpleEntityIndexed entity = new SimpleEntityIndexed( //
143 | cursor.getLong(offset + 0), // id
144 | cursor.getShort(offset + 1) != 0, // simpleBoolean
145 | (byte) cursor.getShort(offset + 2), // simpleByte
146 | cursor.getShort(offset + 3), // simpleShort
147 | cursor.getInt(offset + 4), // simpleInt
148 | cursor.getLong(offset + 5), // simpleLong
149 | cursor.getFloat(offset + 6), // simpleFloat
150 | cursor.getDouble(offset + 7), // simpleDouble
151 | cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8), // simpleString
152 | cursor.isNull(offset + 9) ? null : cursor.getBlob(offset + 9) // simpleByteArray
153 | );
154 | return entity;
155 | }
156 |
157 | @Override
158 | public void readEntity(Cursor cursor, SimpleEntityIndexed entity, int offset) {
159 | entity.setId(cursor.getLong(offset + 0));
160 | entity.setSimpleBoolean(cursor.getShort(offset + 1) != 0);
161 | entity.setSimpleByte((byte) cursor.getShort(offset + 2));
162 | entity.setSimpleShort(cursor.getShort(offset + 3));
163 | entity.setSimpleInt(cursor.getInt(offset + 4));
164 | entity.setSimpleLong(cursor.getLong(offset + 5));
165 | entity.setSimpleFloat(cursor.getFloat(offset + 6));
166 | entity.setSimpleDouble(cursor.getDouble(offset + 7));
167 | entity.setSimpleString(cursor.isNull(offset + 8) ? null : cursor.getString(offset + 8));
168 | entity.setSimpleByteArray(cursor.isNull(offset + 9) ? null : cursor.getBlob(offset + 9));
169 | }
170 |
171 | @Override
172 | protected final Long updateKeyAfterInsert(SimpleEntityIndexed entity, long rowId) {
173 | entity.setId(rowId);
174 | return rowId;
175 | }
176 |
177 | @Override
178 | public Long getKey(SimpleEntityIndexed entity) {
179 | if(entity != null) {
180 | return entity.getId();
181 | } else {
182 | return null;
183 | }
184 | }
185 |
186 | @Override
187 | public boolean hasKey(SimpleEntityIndexed entity) {
188 | throw new UnsupportedOperationException("Unsupported for entities with a non-null key");
189 | }
190 |
191 | @Override
192 | protected final boolean isEntityUpdateable() {
193 | return true;
194 | }
195 |
196 | }
197 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/objectbox/ObjectBoxPerfTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017-2024 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp.objectbox;
18 |
19 | import android.content.Context;
20 |
21 | import java.util.ArrayList;
22 | import java.util.List;
23 |
24 | import io.objectbox.Box;
25 | import io.objectbox.BoxStore;
26 | import io.objectbox.performanceapp.PerfTest;
27 | import io.objectbox.performanceapp.PerfTestRunner;
28 | import io.objectbox.performanceapp.TestType;
29 | import io.objectbox.query.Query;
30 |
31 | import static io.objectbox.query.QueryBuilder.StringOrder.CASE_SENSITIVE;
32 |
33 | public class ObjectBoxPerfTest extends PerfTest {
34 | private BoxStore store;
35 |
36 | private boolean versionLoggedOnce;
37 | private Box box;
38 | private Box boxIndexed;
39 |
40 | @Override
41 | public String name() {
42 | return "ObjectBox";
43 | }
44 |
45 | public void setUp(Context context, PerfTestRunner testRunner) {
46 | super.setUp(context, testRunner);
47 | store = MyObjectBox.builder().androidContext(context).build();
48 | store.close();
49 | store.deleteAllFiles();
50 | // 2 GB for DB to allow putting millions of objects
51 | store = MyObjectBox.builder().androidContext(context).maxSizeInKByte(2 * 1024 * 1024).build();
52 | box = store.boxFor(SimpleEntity.class);
53 | boxIndexed = store.boxFor(SimpleEntityIndexed.class);
54 |
55 | if (!versionLoggedOnce) {
56 | String versionNative = BoxStore.getVersionNative();
57 | String versionJava = BoxStore.getVersion();
58 | if (versionJava.equals(versionNative)) {
59 | log("ObjectBox " + versionNative);
60 | } else {
61 | log("ObjectBox " + versionNative + " (Java: " + versionJava + ")");
62 | }
63 | versionLoggedOnce = true;
64 | }
65 | }
66 |
67 | @Override
68 | public void run(TestType type) {
69 | switch (type.name) {
70 | case TestType.CRUD:
71 | runBatchPerfTest(false);
72 | break;
73 | case TestType.CRUD_SCALARS:
74 | runBatchPerfTest(true);
75 | break;
76 | case TestType.CRUD_INDEXED:
77 | runBatchPerfTestIndexed();
78 | break;
79 | case TestType.QUERY_STRING:
80 | runQueryByString();
81 | break;
82 | case TestType.QUERY_STRING_INDEXED:
83 | runQueryByStringIndexed();
84 | break;
85 | case TestType.QUERY_INTEGER:
86 | runQueryByInteger();
87 | break;
88 | case TestType.QUERY_INTEGER_INDEXED:
89 | runQueryByIntegerIndexed();
90 | break;
91 | case TestType.QUERY_ID:
92 | runQueryById(false);
93 | break;
94 | case TestType.QUERY_ID_RANDOM:
95 | runQueryById(true);
96 | break;
97 | }
98 | }
99 |
100 | public void runBatchPerfTest(boolean scalarsOnly) {
101 | List list = prepareAndPutEntities(scalarsOnly);
102 |
103 | for (SimpleEntity entity : list) {
104 | if (scalarsOnly) {
105 | setRandomScalars(entity);
106 | } else {
107 | setRandomValues(entity);
108 | }
109 | }
110 | startBenchmark("update");
111 | box.put(list);
112 | stopBenchmark();
113 |
114 | //noinspection UnusedAssignment
115 | list = null;
116 |
117 | startBenchmark("load");
118 | List reloaded = box.getAll();
119 | stopBenchmark();
120 |
121 | assertEntityCount(reloaded.size());
122 |
123 | startBenchmark("access");
124 | accessAll(reloaded);
125 | stopBenchmark();
126 |
127 | startBenchmark("delete");
128 | box.remove(reloaded);
129 | stopBenchmark();
130 | }
131 |
132 | protected void setRandomValues(SimpleEntity entity) {
133 | setRandomScalars(entity);
134 | entity.setSimpleString(randomString());
135 | entity.setSimpleByteArray(randomBytes());
136 | }
137 |
138 | private void setRandomScalars(SimpleEntity entity) {
139 | entity.setSimpleBoolean(random.nextBoolean());
140 | entity.setSimpleByte((byte) random.nextInt());
141 | entity.setSimpleShort((short) random.nextInt());
142 | entity.setSimpleInt(random.nextInt());
143 | entity.setSimpleLong(random.nextLong());
144 | entity.setSimpleDouble(random.nextDouble());
145 | entity.setSimpleFloat(random.nextFloat());
146 | }
147 |
148 | public SimpleEntity createEntity(boolean scalarsOnly) {
149 | SimpleEntity entity = new SimpleEntity();
150 | if (scalarsOnly) {
151 | setRandomScalars(entity);
152 | } else {
153 | setRandomValues(entity);
154 | }
155 | return entity;
156 | }
157 |
158 | @SuppressWarnings("ResultOfMethodCallIgnored")
159 | protected void accessAll(List list) {
160 | for (SimpleEntity entity : list) {
161 | entity.getId();
162 | entity.getSimpleBoolean();
163 | entity.getSimpleByte();
164 | entity.getSimpleShort();
165 | entity.getSimpleInt();
166 | entity.getSimpleLong();
167 | entity.getSimpleFloat();
168 | entity.getSimpleDouble();
169 | entity.getSimpleString();
170 | entity.getSimpleByteArray();
171 | }
172 | }
173 |
174 | public void runBatchPerfTestIndexed() {
175 | List list = prepareAndPutEntitiesIndexed();
176 |
177 | for (SimpleEntityIndexed entity : list) {
178 | setRandomValues(entity);
179 | }
180 | startBenchmark("update");
181 | boxIndexed.put(list);
182 | stopBenchmark();
183 |
184 | //noinspection UnusedAssignment
185 | list = null;
186 |
187 | startBenchmark("load");
188 | List reloaded = boxIndexed.getAll();
189 | stopBenchmark();
190 |
191 | startBenchmark("access");
192 | accessAllIndexed(reloaded);
193 | stopBenchmark();
194 |
195 | startBenchmark("delete");
196 | boxIndexed.remove(reloaded);
197 | stopBenchmark();
198 | }
199 |
200 | protected void setRandomValues(SimpleEntityIndexed entity) {
201 | entity.setSimpleBoolean(random.nextBoolean());
202 | entity.setSimpleByte((byte) random.nextInt());
203 | entity.setSimpleShort((short) random.nextInt());
204 | entity.setSimpleInt(random.nextInt());
205 | entity.setSimpleLong(random.nextLong());
206 | entity.setSimpleDouble(random.nextDouble());
207 | entity.setSimpleFloat(random.nextFloat());
208 | entity.setSimpleString(randomString());
209 | entity.setSimpleByteArray(randomBytes());
210 | }
211 |
212 | public SimpleEntityIndexed createEntityIndexed() {
213 | SimpleEntityIndexed entity = new SimpleEntityIndexed();
214 | setRandomValues(entity);
215 | return entity;
216 | }
217 |
218 | @SuppressWarnings("ResultOfMethodCallIgnored")
219 | protected void accessAllIndexed(List list) {
220 | for (SimpleEntityIndexed entity : list) {
221 | entity.getId();
222 | entity.getSimpleBoolean();
223 | entity.getSimpleByte();
224 | entity.getSimpleShort();
225 | entity.getSimpleInt();
226 | entity.getSimpleLong();
227 | entity.getSimpleFloat();
228 | entity.getSimpleDouble();
229 | entity.getSimpleString();
230 | entity.getSimpleByteArray();
231 | }
232 | }
233 |
234 | private void runQueryByString() {
235 | if (numberEntities > 10000) {
236 | log("Reduce number of entities to 10000 to avoid extremely long test runs");
237 | return;
238 | }
239 | List entities = prepareAndPutEntities(false);
240 |
241 | final String[] stringsToLookup = new String[numberEntities];
242 | for (int i = 0; i < stringsToLookup.length; i++) {
243 | String text = "";
244 | while (text.length() < 2) {
245 | text = entities.get(random.nextInt(numberEntities)).getSimpleString();
246 | }
247 | stringsToLookup[i] = text;
248 | }
249 |
250 | startBenchmark("query");
251 |
252 | long entitiesFound = 0;
253 | try (Query query = box.query(
254 | SimpleEntity_.simpleString
255 | .equal("", CASE_SENSITIVE)
256 | .alias("string")
257 | ).build()) {
258 | for (int i = 0; i < stringsToLookup.length; i++) {
259 | query.setParameter("string", stringsToLookup[i]);
260 | List result = query.find();
261 | accessAll(result);
262 | entitiesFound += result.size();
263 | }
264 | }
265 | stopBenchmark();
266 | log("Entities found: " + entitiesFound);
267 | }
268 |
269 | private void runQueryByInteger() {
270 | if (numberEntities > 10000) {
271 | log("Reduce number of entities to 10000 to avoid extremely long test runs");
272 | return;
273 | }
274 | List entities = prepareAndPutEntities(false);
275 | final int[] valuesToLookup = new int[numberEntities];
276 | for (int i = 0; i < numberEntities; i++) {
277 | valuesToLookup[i] = entities.get(random.nextInt(numberEntities)).getSimpleInt();
278 | }
279 |
280 | startBenchmark("query");
281 | long entitiesFound = 0;
282 | try (Query query = box.query(
283 | SimpleEntity_.simpleInt
284 | .equal(0)
285 | .alias("int")
286 | ).build()) {
287 | for (int i = 0; i < numberEntities; i++) {
288 | query.setParameter("int", valuesToLookup[i]);
289 | List result = query.find();
290 | accessAll(result);
291 | entitiesFound += result.size();
292 | }
293 | }
294 | stopBenchmark();
295 | log("Entities found: " + entitiesFound);
296 | assertGreaterOrEqualToNumberOfEntities(entitiesFound);
297 | }
298 |
299 | private List prepareAndPutEntities(boolean scalarsOnly) {
300 | List entities = new ArrayList<>(numberEntities);
301 | for (int i = 0; i < numberEntities; i++) {
302 | entities.add(createEntity(scalarsOnly));
303 | }
304 | log("Prepared test data: " + numberEntities + " objects");
305 |
306 | startBenchmark("insert");
307 | box.put(entities);
308 | stopBenchmark();
309 |
310 | assertEntityCount(box.count());
311 | return entities;
312 | }
313 |
314 | private void runQueryByStringIndexed() {
315 | List entities = prepareAndPutEntitiesIndexed();
316 |
317 | final String[] stringsToLookup = new String[numberEntities];
318 | for (int i = 0; i < stringsToLookup.length; i++) {
319 | String text = "";
320 | while (text.length() < 2) {
321 | text = entities.get(random.nextInt(numberEntities)).getSimpleString();
322 | }
323 | stringsToLookup[i] = text;
324 | }
325 |
326 | startBenchmark("query");
327 | long entitiesFound = 0;
328 | try (Query query = boxIndexed.query(
329 | SimpleEntityIndexed_.simpleString
330 | .equal("", CASE_SENSITIVE)
331 | .alias("string")
332 | ).build()) {
333 | for (int i = 0; i < stringsToLookup.length; i++) {
334 | query.setParameter("string", stringsToLookup[i]);
335 | List result = query.find();
336 | accessAllIndexed(result);
337 | entitiesFound += result.size();
338 | }
339 | }
340 | stopBenchmark();
341 | log("Entities found: " + entitiesFound);
342 | assertGreaterOrEqualToNumberOfEntities(entitiesFound);
343 | }
344 |
345 | private void runQueryByIntegerIndexed() {
346 | List entities = prepareAndPutEntitiesIndexed();
347 | final int[] valuesToLookup = new int[numberEntities];
348 | for (int i = 0; i < numberEntities; i++) {
349 | valuesToLookup[i] = entities.get(random.nextInt(numberEntities)).getSimpleInt();
350 | }
351 |
352 | startBenchmark("query");
353 | long entitiesFound = 0;
354 | try (Query query = boxIndexed.query(
355 | SimpleEntityIndexed_.simpleInt
356 | .equal(0)
357 | .alias("int")
358 | ).build()) {
359 | for (int i = 0; i < numberEntities; i++) {
360 | query.setParameter("int", valuesToLookup[i]);
361 | List result = query.find();
362 | accessAllIndexed(result);
363 | entitiesFound += result.size();
364 | }
365 | }
366 | stopBenchmark();
367 | log("Entities found: " + entitiesFound);
368 | assertGreaterOrEqualToNumberOfEntities(entitiesFound);
369 | }
370 |
371 | private List prepareAndPutEntitiesIndexed() {
372 | List entities = new ArrayList<>(numberEntities);
373 | for (int i = 0; i < numberEntities; i++) {
374 | entities.add(createEntityIndexed());
375 | }
376 |
377 | startBenchmark("insert");
378 | boxIndexed.put(entities);
379 | stopBenchmark();
380 |
381 | assertEntityCount(boxIndexed.count());
382 |
383 | return entities;
384 | }
385 |
386 | private void runQueryById(boolean randomIds) {
387 | prepareAndPutEntities(false);
388 |
389 | final long[] idsToLookup = new long[numberEntities];
390 | for (int i = 0; i < numberEntities; i++) {
391 | idsToLookup[i] = randomIds ? 1 + random.nextInt(numberEntities) : 1 + i;
392 | }
393 |
394 | benchmark("query", () -> {
395 | List results = box.get(idsToLookup);
396 | if (results.size() != idsToLookup.length) {
397 | throw new IllegalStateException("result count " + results.size()
398 | + " is not " + idsToLookup.length);
399 | }
400 | accessAll(results);
401 | });
402 | }
403 |
404 | @Override
405 | public void tearDown() {
406 | store.close();
407 | store.deleteAllFiles();
408 | }
409 |
410 | }
411 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/objectbox/SimpleEntity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp.objectbox;
18 |
19 | import io.objectbox.annotation.Entity;
20 | import io.objectbox.annotation.Id;
21 |
22 | @Entity
23 | public class SimpleEntity {
24 |
25 | @Id
26 | long id;
27 | boolean simpleBoolean;
28 | byte simpleByte;
29 | short simpleShort;
30 | int simpleInt;
31 | long simpleLong;
32 | float simpleFloat;
33 | double simpleDouble;
34 |
35 | String simpleString;
36 |
37 | byte[] simpleByteArray;
38 |
39 | public SimpleEntity() {
40 | }
41 |
42 | public SimpleEntity(long id) {
43 | this.id = id;
44 | }
45 |
46 | public SimpleEntity(long id, boolean simpleBoolean, byte simpleByte, short simpleShort, int simpleInt, long simpleLong, float simpleFloat, double simpleDouble, String simpleString, byte[] simpleByteArray) {
47 | this.id = id;
48 | this.simpleBoolean = simpleBoolean;
49 | this.simpleByte = simpleByte;
50 | this.simpleShort = simpleShort;
51 | this.simpleInt = simpleInt;
52 | this.simpleLong = simpleLong;
53 | this.simpleFloat = simpleFloat;
54 | this.simpleDouble = simpleDouble;
55 | this.simpleString = simpleString;
56 | this.simpleByteArray = simpleByteArray;
57 | }
58 |
59 | public long getId() {
60 | return id;
61 | }
62 |
63 | public void setId(long id) {
64 | this.id = id;
65 | }
66 |
67 | public boolean getSimpleBoolean() {
68 | return simpleBoolean;
69 | }
70 |
71 | public void setSimpleBoolean(boolean simpleBoolean) {
72 | this.simpleBoolean = simpleBoolean;
73 | }
74 |
75 | public byte getSimpleByte() {
76 | return simpleByte;
77 | }
78 |
79 | public void setSimpleByte(byte simpleByte) {
80 | this.simpleByte = simpleByte;
81 | }
82 |
83 | public short getSimpleShort() {
84 | return simpleShort;
85 | }
86 |
87 | public void setSimpleShort(short simpleShort) {
88 | this.simpleShort = simpleShort;
89 | }
90 |
91 | public int getSimpleInt() {
92 | return simpleInt;
93 | }
94 |
95 | public void setSimpleInt(int simpleInt) {
96 | this.simpleInt = simpleInt;
97 | }
98 |
99 | public long getSimpleLong() {
100 | return simpleLong;
101 | }
102 |
103 | public void setSimpleLong(long simpleLong) {
104 | this.simpleLong = simpleLong;
105 | }
106 |
107 | public float getSimpleFloat() {
108 | return simpleFloat;
109 | }
110 |
111 | public void setSimpleFloat(float simpleFloat) {
112 | this.simpleFloat = simpleFloat;
113 | }
114 |
115 | public double getSimpleDouble() {
116 | return simpleDouble;
117 | }
118 |
119 | public void setSimpleDouble(double simpleDouble) {
120 | this.simpleDouble = simpleDouble;
121 | }
122 |
123 | public String getSimpleString() {
124 | return simpleString;
125 | }
126 |
127 | public void setSimpleString(String simpleString) {
128 | this.simpleString = simpleString;
129 | }
130 |
131 | public byte[] getSimpleByteArray() {
132 | return simpleByteArray;
133 | }
134 |
135 | public void setSimpleByteArray(byte[] simpleByteArray) {
136 | this.simpleByteArray = simpleByteArray;
137 | }
138 |
139 | }
140 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/objectbox/SimpleEntityIndexed.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp.objectbox;
18 |
19 | import io.objectbox.annotation.Entity;
20 | import io.objectbox.annotation.Id;
21 | import io.objectbox.annotation.Index;
22 |
23 | @Entity
24 | public class SimpleEntityIndexed {
25 |
26 | @Id
27 | private long id;
28 | private boolean simpleBoolean;
29 | private byte simpleByte;
30 | private short simpleShort;
31 | @Index
32 | private int simpleInt;
33 | private long simpleLong;
34 | private float simpleFloat;
35 | private double simpleDouble;
36 |
37 | @Index
38 | private String simpleString;
39 |
40 | private byte[] simpleByteArray;
41 |
42 | public SimpleEntityIndexed() {
43 | }
44 |
45 | public SimpleEntityIndexed(long id) {
46 | this.id = id;
47 | }
48 |
49 | public SimpleEntityIndexed(long id, boolean simpleBoolean, byte simpleByte, short simpleShort, int simpleInt, long simpleLong, float simpleFloat, double simpleDouble, String simpleString, byte[] simpleByteArray) {
50 | this.id = id;
51 | this.simpleBoolean = simpleBoolean;
52 | this.simpleByte = simpleByte;
53 | this.simpleShort = simpleShort;
54 | this.simpleInt = simpleInt;
55 | this.simpleLong = simpleLong;
56 | this.simpleFloat = simpleFloat;
57 | this.simpleDouble = simpleDouble;
58 | this.simpleString = simpleString;
59 | this.simpleByteArray = simpleByteArray;
60 | }
61 |
62 | public long getId() {
63 | return id;
64 | }
65 |
66 | public void setId(long id) {
67 | this.id = id;
68 | }
69 |
70 | public boolean getSimpleBoolean() {
71 | return simpleBoolean;
72 | }
73 |
74 | public void setSimpleBoolean(boolean simpleBoolean) {
75 | this.simpleBoolean = simpleBoolean;
76 | }
77 |
78 | public byte getSimpleByte() {
79 | return simpleByte;
80 | }
81 |
82 | public void setSimpleByte(byte simpleByte) {
83 | this.simpleByte = simpleByte;
84 | }
85 |
86 | public short getSimpleShort() {
87 | return simpleShort;
88 | }
89 |
90 | public void setSimpleShort(short simpleShort) {
91 | this.simpleShort = simpleShort;
92 | }
93 |
94 | public int getSimpleInt() {
95 | return simpleInt;
96 | }
97 |
98 | public void setSimpleInt(int simpleInt) {
99 | this.simpleInt = simpleInt;
100 | }
101 |
102 | public long getSimpleLong() {
103 | return simpleLong;
104 | }
105 |
106 | public void setSimpleLong(long simpleLong) {
107 | this.simpleLong = simpleLong;
108 | }
109 |
110 | public float getSimpleFloat() {
111 | return simpleFloat;
112 | }
113 |
114 | public void setSimpleFloat(float simpleFloat) {
115 | this.simpleFloat = simpleFloat;
116 | }
117 |
118 | public double getSimpleDouble() {
119 | return simpleDouble;
120 | }
121 |
122 | public void setSimpleDouble(double simpleDouble) {
123 | this.simpleDouble = simpleDouble;
124 | }
125 |
126 | public String getSimpleString() {
127 | return simpleString;
128 | }
129 |
130 | public void setSimpleString(String simpleString) {
131 | this.simpleString = simpleString;
132 | }
133 |
134 | public byte[] getSimpleByteArray() {
135 | return simpleByteArray;
136 | }
137 |
138 | public void setSimpleByteArray(byte[] simpleByteArray) {
139 | this.simpleByteArray = simpleByteArray;
140 | }
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/realm/RealmPerfTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp.realm;
18 |
19 | import android.content.Context;
20 |
21 | import java.util.ArrayList;
22 | import java.util.List;
23 |
24 | import io.objectbox.performanceapp.PerfTest;
25 | import io.objectbox.performanceapp.PerfTestRunner;
26 | import io.objectbox.performanceapp.TestType;
27 | import io.realm.Realm;
28 | import io.realm.RealmConfiguration;
29 | import io.realm.RealmResults;
30 |
31 | public class RealmPerfTest extends PerfTest {
32 |
33 | private boolean versionLoggedOnce;
34 | private Realm realm;
35 |
36 | @Override
37 | public String name() {
38 | return "Realm";
39 | }
40 |
41 | public void setUp(Context context, PerfTestRunner testRunner) {
42 | super.setUp(context, testRunner);
43 | Realm.init(context);
44 | realm = Realm.getDefaultInstance();
45 |
46 | RealmConfiguration configuration = realm.getConfiguration();
47 | realm.close();
48 | Realm.deleteRealm(configuration);
49 | realm = Realm.getDefaultInstance();
50 |
51 | if (!versionLoggedOnce) {
52 | //log("Realm " + ??);
53 | versionLoggedOnce = true;
54 | }
55 | }
56 |
57 | @Override
58 | public void run(TestType type) {
59 | switch (type.name) {
60 | case TestType.CRUD:
61 | runBatchPerfTest(false);
62 | break;
63 | case TestType.CRUD_SCALARS:
64 | runBatchPerfTest(true);
65 | break;
66 | case TestType.CRUD_INDEXED:
67 | runBatchPerfTestIndexed();
68 | break;
69 | case TestType.QUERY_STRING:
70 | runQueryByString();
71 | break;
72 | case TestType.QUERY_STRING_INDEXED:
73 | runQueryByStringIndexed();
74 | break;
75 | case TestType.QUERY_ID:
76 | runQueryById(false);
77 | break;
78 | case TestType.QUERY_ID_RANDOM:
79 | runQueryById(true);
80 | break;
81 | }
82 | }
83 |
84 | public void runBatchPerfTest(boolean scalarsOnly) {
85 | List list = new ArrayList<>(numberEntities);
86 | for (int i = 0; i < numberEntities; i++) {
87 | list.add(createEntity(i, scalarsOnly));
88 | }
89 | startBenchmark("insert");
90 | realm.beginTransaction();
91 | realm.insert(list);
92 | realm.commitTransaction();
93 | stopBenchmark();
94 |
95 | for (SimpleEntity entity : list) {
96 | if (scalarsOnly) {
97 | setRandomScalars(entity);
98 | } else {
99 | setRandomValues(entity);
100 | }
101 | }
102 | startBenchmark("update");
103 | realm.beginTransaction();
104 | realm.insertOrUpdate(list);
105 | realm.commitTransaction();
106 | stopBenchmark();
107 |
108 | //noinspection UnusedAssignment
109 | list = null;
110 |
111 | startBenchmark("load");
112 | RealmResults reloaded = realm.where(SimpleEntity.class).findAll();
113 | stopBenchmark();
114 |
115 | startBenchmark("access");
116 | accessAll(reloaded);
117 | stopBenchmark();
118 |
119 | startBenchmark("delete");
120 | realm.beginTransaction();
121 | reloaded.deleteAllFromRealm();
122 | realm.commitTransaction();
123 | stopBenchmark();
124 | }
125 |
126 | protected void setRandomValues(SimpleEntity entity) {
127 | setRandomScalars(entity);
128 | entity.setSimpleString(randomString());
129 | entity.setSimpleByteArray(randomBytes());
130 | }
131 |
132 | private void setRandomScalars(SimpleEntity entity) {
133 | entity.setSimpleBoolean(random.nextBoolean());
134 | entity.setSimpleByte((byte) random.nextInt());
135 | entity.setSimpleShort((short) random.nextInt());
136 | entity.setSimpleInt(random.nextInt());
137 | entity.setSimpleLong(random.nextLong());
138 | entity.setSimpleDouble(random.nextDouble());
139 | entity.setSimpleFloat(random.nextFloat());
140 | }
141 |
142 | public SimpleEntity createEntity(long id, boolean scalarsOnly) {
143 | SimpleEntity entity = new SimpleEntity();
144 | entity.setId(id);
145 | if (scalarsOnly) {
146 | setRandomScalars(entity);
147 | } else {
148 | setRandomValues(entity);
149 | }
150 | return entity;
151 | }
152 |
153 | @SuppressWarnings("ResultOfMethodCallIgnored")
154 | protected void accessAll(List list) {
155 | for (SimpleEntity entity : list) {
156 | entity.getId();
157 | entity.getSimpleBoolean();
158 | entity.getSimpleByte();
159 | entity.getSimpleShort();
160 | entity.getSimpleInt();
161 | entity.getSimpleLong();
162 | entity.getSimpleFloat();
163 | entity.getSimpleDouble();
164 | entity.getSimpleString();
165 | entity.getSimpleByteArray();
166 | }
167 | }
168 |
169 | public void runBatchPerfTestIndexed() {
170 | List list = new ArrayList<>(numberEntities);
171 | for (int i = 0; i < numberEntities; i++) {
172 | list.add(createEntityIndexed(i));
173 | }
174 | startBenchmark("insert");
175 | realm.beginTransaction();
176 | realm.insert(list);
177 | realm.commitTransaction();
178 | stopBenchmark();
179 |
180 | for (SimpleEntityIndexed entity : list) {
181 | setRandomValues(entity);
182 | }
183 | startBenchmark("update");
184 | realm.beginTransaction();
185 | realm.insertOrUpdate(list);
186 | realm.commitTransaction();
187 | stopBenchmark();
188 |
189 | //noinspection UnusedAssignment
190 | list = null;
191 |
192 | startBenchmark("load");
193 | RealmResults reloaded = realm.where(SimpleEntityIndexed.class).findAll();
194 | stopBenchmark();
195 |
196 | startBenchmark("access");
197 | accessAllIndexed(reloaded);
198 | stopBenchmark();
199 |
200 | startBenchmark("delete");
201 | realm.beginTransaction();
202 | reloaded.deleteAllFromRealm();
203 | realm.commitTransaction();
204 | stopBenchmark();
205 | }
206 |
207 | protected void setRandomValues(SimpleEntityIndexed entity) {
208 | entity.setSimpleBoolean(random.nextBoolean());
209 | entity.setSimpleByte((byte) random.nextInt());
210 | entity.setSimpleShort((short) random.nextInt());
211 | entity.setSimpleInt(random.nextInt());
212 | entity.setSimpleLong(random.nextLong());
213 | entity.setSimpleDouble(random.nextDouble());
214 | entity.setSimpleFloat(random.nextFloat());
215 | entity.setSimpleString(randomString());
216 | entity.setSimpleByteArray(randomBytes());
217 | }
218 |
219 | public SimpleEntityIndexed createEntityIndexed(long id) {
220 | SimpleEntityIndexed entity = new SimpleEntityIndexed();
221 | entity.setId(id);
222 | setRandomValues(entity);
223 | return entity;
224 | }
225 |
226 | @SuppressWarnings("ResultOfMethodCallIgnored")
227 | protected void accessAllIndexed(List list) {
228 | for (SimpleEntityIndexed entity : list) {
229 | entity.getId();
230 | entity.getSimpleBoolean();
231 | entity.getSimpleByte();
232 | entity.getSimpleShort();
233 | entity.getSimpleInt();
234 | entity.getSimpleLong();
235 | entity.getSimpleFloat();
236 | entity.getSimpleDouble();
237 | entity.getSimpleString();
238 | entity.getSimpleByteArray();
239 | }
240 | }
241 |
242 | private void runQueryByString() {
243 | if (numberEntities > 10000) {
244 | log("Reduce number of entities to 10000 to avoid extremely long test runs");
245 | return;
246 | }
247 | List entities = new ArrayList<>(numberEntities);
248 | for (int i = 0; i < numberEntities; i++) {
249 | entities.add(createEntity(i, false));
250 | }
251 |
252 | startBenchmark("insert");
253 | realm.beginTransaction();
254 | realm.insert(entities);
255 | realm.commitTransaction();
256 | stopBenchmark();
257 |
258 | final String[] stringsToLookup = new String[numberEntities];
259 | for (int i = 0; i < numberEntities; i++) {
260 | String text = "";
261 | while (text.length() < 2) {
262 | text = entities.get(random.nextInt(numberEntities)).getSimpleString();
263 | }
264 | stringsToLookup[i] = text;
265 | }
266 |
267 | startBenchmark("query");
268 | long entitiesFound = 0;
269 | for (int i = 0; i < numberEntities; i++) {
270 | List result = realm.where(SimpleEntity.class).equalTo("simpleString", stringsToLookup[i]).findAll();
271 | accessAll(result);
272 | entitiesFound += result.size();
273 | }
274 | stopBenchmark();
275 | log("Entities found: " + entitiesFound);
276 | }
277 |
278 | private void runQueryByStringIndexed() {
279 | List entities = new ArrayList<>(numberEntities);
280 | for (int i = 0; i < numberEntities; i++) {
281 | entities.add(createEntityIndexed(i));
282 | }
283 |
284 | startBenchmark("insert");
285 | realm.beginTransaction();
286 | realm.insert(entities);
287 | realm.commitTransaction();
288 | stopBenchmark();
289 |
290 | final String[] stringsToLookup = new String[numberEntities];
291 | for (int i = 0; i < numberEntities; i++) {
292 | String text = "";
293 | while (text.length() < 2) {
294 | text = entities.get(random.nextInt(numberEntities)).getSimpleString();
295 | }
296 | stringsToLookup[i] = text;
297 | }
298 |
299 | startBenchmark("query");
300 | long entitiesFound = 0;
301 | for (int i = 0; i < numberEntities; i++) {
302 | List result = realm.where(SimpleEntityIndexed.class).equalTo("simpleString", stringsToLookup[i]).findAll();
303 | accessAllIndexed(result);
304 | entitiesFound += result.size();
305 | }
306 | stopBenchmark();
307 | log("Entities found: " + entitiesFound);
308 | }
309 |
310 | private void runQueryById(boolean randomIds) {
311 | List entities = new ArrayList<>(numberEntities);
312 | for (int i = 0; i < numberEntities; i++) {
313 | entities.add(createEntity((long) i, false));
314 | }
315 |
316 | startBenchmark("insert");
317 | realm.beginTransaction();
318 | realm.insert(entities);
319 | realm.commitTransaction();
320 | stopBenchmark();
321 |
322 | long[] idsToLookup = new long[numberEntities];
323 | for (int i = 0; i < numberEntities; i++) {
324 | idsToLookup[i] = randomIds ? random.nextInt(numberEntities) : i;
325 | }
326 |
327 | startBenchmark("query");
328 | for (int i = 0; i < numberEntities; i++) {
329 | SimpleEntity entity = realm.where(SimpleEntity.class).equalTo("id", idsToLookup[i]).findFirst();
330 | accessAll(entity);
331 | }
332 | stopBenchmark();
333 | }
334 |
335 | @SuppressWarnings("ResultOfMethodCallIgnored")
336 | private void accessAll(SimpleEntity entity) {
337 | entity.getId();
338 | entity.getSimpleBoolean();
339 | entity.getSimpleByte();
340 | entity.getSimpleShort();
341 | entity.getSimpleInt();
342 | entity.getSimpleLong();
343 | entity.getSimpleFloat();
344 | entity.getSimpleDouble();
345 | entity.getSimpleString();
346 | entity.getSimpleByteArray();
347 | }
348 |
349 | @Override
350 | public void tearDown() {
351 | RealmConfiguration configuration = realm.getConfiguration();
352 | realm.close();
353 | Realm.deleteRealm(configuration);
354 | }
355 |
356 | }
357 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/realm/SimpleEntity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp.realm;
18 |
19 | import io.realm.RealmObject;
20 | import io.realm.annotations.PrimaryKey;
21 |
22 | public class SimpleEntity extends RealmObject {
23 |
24 | @PrimaryKey
25 | private long id;
26 | private boolean simpleBoolean;
27 | private byte simpleByte;
28 | private short simpleShort;
29 | private int simpleInt;
30 | private long simpleLong;
31 | private float simpleFloat;
32 | private double simpleDouble;
33 |
34 | private String simpleString;
35 |
36 | private byte[] simpleByteArray;
37 |
38 | public SimpleEntity() {
39 | }
40 |
41 | public long getId() {
42 | return id;
43 | }
44 |
45 | public void setId(long id) {
46 | this.id = id;
47 | }
48 |
49 | public boolean getSimpleBoolean() {
50 | return simpleBoolean;
51 | }
52 |
53 | public void setSimpleBoolean(boolean simpleBoolean) {
54 | this.simpleBoolean = simpleBoolean;
55 | }
56 |
57 | public byte getSimpleByte() {
58 | return simpleByte;
59 | }
60 |
61 | public void setSimpleByte(byte simpleByte) {
62 | this.simpleByte = simpleByte;
63 | }
64 |
65 | public short getSimpleShort() {
66 | return simpleShort;
67 | }
68 |
69 | public void setSimpleShort(short simpleShort) {
70 | this.simpleShort = simpleShort;
71 | }
72 |
73 | public int getSimpleInt() {
74 | return simpleInt;
75 | }
76 |
77 | public void setSimpleInt(int simpleInt) {
78 | this.simpleInt = simpleInt;
79 | }
80 |
81 | public long getSimpleLong() {
82 | return simpleLong;
83 | }
84 |
85 | public void setSimpleLong(long simpleLong) {
86 | this.simpleLong = simpleLong;
87 | }
88 |
89 | public float getSimpleFloat() {
90 | return simpleFloat;
91 | }
92 |
93 | public void setSimpleFloat(float simpleFloat) {
94 | this.simpleFloat = simpleFloat;
95 | }
96 |
97 | public double getSimpleDouble() {
98 | return simpleDouble;
99 | }
100 |
101 | public void setSimpleDouble(double simpleDouble) {
102 | this.simpleDouble = simpleDouble;
103 | }
104 |
105 | public String getSimpleString() {
106 | return simpleString;
107 | }
108 |
109 | public void setSimpleString(String simpleString) {
110 | this.simpleString = simpleString;
111 | }
112 |
113 |
114 | public byte[] getSimpleByteArray() {
115 | return simpleByteArray;
116 | }
117 |
118 | public void setSimpleByteArray(byte[] simpleByteArray) {
119 | this.simpleByteArray = simpleByteArray;
120 | }
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/realm/SimpleEntityIndexed.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.objectbox.performanceapp.realm;
18 |
19 | import io.realm.RealmObject;
20 | import io.realm.annotations.Index;
21 | import io.realm.annotations.PrimaryKey;
22 |
23 | public class SimpleEntityIndexed extends RealmObject {
24 |
25 | @PrimaryKey
26 | private long id;
27 | private boolean simpleBoolean;
28 | private byte simpleByte;
29 | private short simpleShort;
30 |
31 | @Index
32 | private int simpleInt;
33 | private long simpleLong;
34 | private float simpleFloat;
35 | private double simpleDouble;
36 |
37 | @Index
38 | private String simpleString;
39 |
40 | private byte[] simpleByteArray;
41 |
42 | public SimpleEntityIndexed() {
43 | }
44 |
45 | public SimpleEntityIndexed(long id) {
46 | this.id = id;
47 | }
48 |
49 | public SimpleEntityIndexed(long id, boolean simpleBoolean, byte simpleByte, short simpleShort, int simpleInt, long simpleLong, float simpleFloat, double simpleDouble, String simpleString, byte[] simpleByteArray) {
50 | this.id = id;
51 | this.simpleBoolean = simpleBoolean;
52 | this.simpleByte = simpleByte;
53 | this.simpleShort = simpleShort;
54 | this.simpleInt = simpleInt;
55 | this.simpleLong = simpleLong;
56 | this.simpleFloat = simpleFloat;
57 | this.simpleDouble = simpleDouble;
58 | this.simpleString = simpleString;
59 | this.simpleByteArray = simpleByteArray;
60 | }
61 |
62 | public long getId() {
63 | return id;
64 | }
65 |
66 | public void setId(long id) {
67 | this.id = id;
68 | }
69 |
70 | public boolean getSimpleBoolean() {
71 | return simpleBoolean;
72 | }
73 |
74 | public void setSimpleBoolean(boolean simpleBoolean) {
75 | this.simpleBoolean = simpleBoolean;
76 | }
77 |
78 | public byte getSimpleByte() {
79 | return simpleByte;
80 | }
81 |
82 | public void setSimpleByte(byte simpleByte) {
83 | this.simpleByte = simpleByte;
84 | }
85 |
86 | public short getSimpleShort() {
87 | return simpleShort;
88 | }
89 |
90 | public void setSimpleShort(short simpleShort) {
91 | this.simpleShort = simpleShort;
92 | }
93 |
94 | public int getSimpleInt() {
95 | return simpleInt;
96 | }
97 |
98 | public void setSimpleInt(int simpleInt) {
99 | this.simpleInt = simpleInt;
100 | }
101 |
102 | public long getSimpleLong() {
103 | return simpleLong;
104 | }
105 |
106 | public void setSimpleLong(long simpleLong) {
107 | this.simpleLong = simpleLong;
108 | }
109 |
110 | public float getSimpleFloat() {
111 | return simpleFloat;
112 | }
113 |
114 | public void setSimpleFloat(float simpleFloat) {
115 | this.simpleFloat = simpleFloat;
116 | }
117 |
118 | public double getSimpleDouble() {
119 | return simpleDouble;
120 | }
121 |
122 | public void setSimpleDouble(double simpleDouble) {
123 | this.simpleDouble = simpleDouble;
124 | }
125 |
126 | public String getSimpleString() {
127 | return simpleString;
128 | }
129 |
130 | public void setSimpleString(String simpleString) {
131 | this.simpleString = simpleString;
132 | }
133 |
134 | public byte[] getSimpleByteArray() {
135 | return simpleByteArray;
136 | }
137 |
138 | public void setSimpleByteArray(byte[] simpleByteArray) {
139 | this.simpleByteArray = simpleByteArray;
140 | }
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/room/AppDatabase.java:
--------------------------------------------------------------------------------
1 | package io.objectbox.performanceapp.room;
2 |
3 | import androidx.room.Database;
4 | import androidx.room.RoomDatabase;
5 |
6 | @Database(entities = {SimpleEntity.class, SimpleEntityIndexed.class}, version = 1)
7 | public abstract class AppDatabase extends RoomDatabase {
8 |
9 | public abstract SimpleEntityDao simpleEntityDao();
10 |
11 | public abstract SimpleEntityIndexedDao simpleEntityIndexedDao();
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/room/RoomPerfTest.java:
--------------------------------------------------------------------------------
1 | package io.objectbox.performanceapp.room;
2 |
3 | import androidx.room.Room;
4 | import android.content.Context;
5 | import android.database.Cursor;
6 |
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | import io.objectbox.performanceapp.PerfTest;
11 | import io.objectbox.performanceapp.PerfTestRunner;
12 | import io.objectbox.performanceapp.TestType;
13 |
14 | public class RoomPerfTest extends PerfTest {
15 |
16 | public static final String DB_NAME = "sqlite-room";
17 |
18 | private boolean versionLoggedOnce;
19 | private AppDatabase db;
20 | private SimpleEntityDao dao;
21 | private SimpleEntityIndexedDao daoIndexed;
22 |
23 | @Override
24 | public String name() {
25 | return "Room";
26 | }
27 |
28 | @Override
29 | public void setUp(Context context, PerfTestRunner testRunner) {
30 | super.setUp(context, testRunner);
31 | boolean deleted = context.deleteDatabase(DB_NAME);
32 | if (deleted) {
33 | log("DB existed before start - deleted");
34 | }
35 | db = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DB_NAME)
36 | .build();
37 | dao = db.simpleEntityDao();
38 | daoIndexed = db.simpleEntityIndexedDao();
39 |
40 | if (!versionLoggedOnce) {
41 | try (Cursor cursor = db.query("select sqlite_version() AS sqlite_version", null)) {
42 | if (cursor.moveToFirst()) {
43 | log("SQLite version " + cursor.getString(0));
44 | }
45 | }
46 | versionLoggedOnce = true;
47 | }
48 | }
49 |
50 | @Override
51 | public void run(TestType type) {
52 | switch (type.name) {
53 | case TestType.CRUD:
54 | runBatchPerfTest(false);
55 | break;
56 | case TestType.CRUD_SCALARS:
57 | runBatchPerfTest(true);
58 | break;
59 | case TestType.CRUD_INDEXED:
60 | runBatchPerfTestIndexed();
61 | break;
62 | case TestType.QUERY_STRING:
63 | runQueryByString();
64 | break;
65 | case TestType.QUERY_STRING_INDEXED:
66 | runQueryByStringIndexed();
67 | break;
68 | case TestType.QUERY_INTEGER:
69 | runQueryByInteger();
70 | break;
71 | case TestType.QUERY_INTEGER_INDEXED:
72 | runQueryByIntegerIndexed();
73 | break;
74 | case TestType.QUERY_ID:
75 | runQueryById(false);
76 | break;
77 | case TestType.QUERY_ID_RANDOM:
78 | runQueryById(true);
79 | break;
80 | }
81 | }
82 |
83 | private void runBatchPerfTest(boolean scalarsOnly) {
84 | List list = new ArrayList<>(numberEntities);
85 | for (int i = 0; i < numberEntities; i++) {
86 | list.add(createEntity((long) i, scalarsOnly));
87 | }
88 | startBenchmark("insert");
89 | dao.insertInTx(list);
90 | stopBenchmark();
91 |
92 | for (SimpleEntity entity : list) {
93 | if (scalarsOnly) {
94 | setRandomScalars(entity);
95 | } else {
96 | setRandomValues(entity);
97 | }
98 | }
99 | startBenchmark("update");
100 | dao.updateInTx(list);
101 | stopBenchmark();
102 |
103 | //noinspection UnusedAssignment
104 | list = null;
105 |
106 | startBenchmark("load");
107 | List reloaded = dao.loadAll();
108 | stopBenchmark();
109 |
110 | startBenchmark("access");
111 | accessAll(reloaded);
112 | stopBenchmark();
113 |
114 | startBenchmark("delete");
115 | dao.deleteInTx(reloaded);
116 | stopBenchmark();
117 | }
118 |
119 | private void runBatchPerfTestIndexed() {
120 | List list = new ArrayList<>(numberEntities);
121 | for (int i = 0; i < numberEntities; i++) {
122 | list.add(createEntityIndexed((long) i));
123 | }
124 | startBenchmark("insert");
125 | daoIndexed.insertInTx(list);
126 | stopBenchmark();
127 |
128 | for (SimpleEntityIndexed entity : list) {
129 | setRandomValues(entity);
130 | }
131 | startBenchmark("update");
132 | daoIndexed.updateInTx(list);
133 | stopBenchmark();
134 |
135 | //noinspection UnusedAssignment
136 | list = null;
137 |
138 | startBenchmark("load");
139 | List reloaded = daoIndexed.loadAll();
140 | stopBenchmark();
141 |
142 | startBenchmark("access");
143 | accessAllIndexed(reloaded);
144 | stopBenchmark();
145 |
146 | startBenchmark("delete");
147 | daoIndexed.deleteInTx(reloaded);
148 | stopBenchmark();
149 | }
150 |
151 | private void runQueryByString() {
152 | if (numberEntities > 10000) {
153 | log("Reduce number of entities to 10000 to avoid extremely long test runs");
154 | return;
155 | }
156 | List entities = new ArrayList<>(numberEntities);
157 | for (int i = 0; i < numberEntities; i++) {
158 | entities.add(createEntity((long) i, false));
159 | }
160 |
161 | startBenchmark("insert");
162 | dao.insertInTx(entities);
163 | stopBenchmark();
164 |
165 | String[] stringsToLookup = new String[numberEntities];
166 | for (int i = 0; i < numberEntities; i++) {
167 | String text = "";
168 | while (text.length() < 2) {
169 | text = entities.get(random.nextInt(numberEntities)).getSimpleString();
170 | }
171 | stringsToLookup[i] = text;
172 | }
173 |
174 | startBenchmark("query");
175 | long entitiesFound = db.runInTransaction(() -> {
176 | long found = 0;
177 | for (int i = 0; i < numberEntities; i++) {
178 | List result = dao.whereSimpleStringEq(stringsToLookup[i]);
179 | accessAll(result);
180 | found += result.size();
181 | }
182 | return found;
183 | });
184 | stopBenchmark();
185 | log("Entities found: " + entitiesFound);
186 | }
187 |
188 | private void runQueryByStringIndexed() {
189 | List entities = new ArrayList<>(numberEntities);
190 | for (int i = 0; i < numberEntities; i++) {
191 | entities.add(createEntityIndexed((long) i));
192 | }
193 |
194 | startBenchmark("insert");
195 | daoIndexed.insertInTx(entities);
196 | stopBenchmark();
197 |
198 | String[] stringsToLookup = new String[numberEntities];
199 | for (int i = 0; i < numberEntities; i++) {
200 | String text = "";
201 | while (text.length() < 2) {
202 | text = entities.get(random.nextInt(numberEntities)).getSimpleString();
203 | }
204 | stringsToLookup[i] = text;
205 | }
206 |
207 | startBenchmark("query");
208 | long entitiesFound = db.runInTransaction(() -> {
209 | long found = 0;
210 | for (int i = 0; i < numberEntities; i++) {
211 | List result = daoIndexed.whereSimpleStringEq(stringsToLookup[i]);
212 | accessAllIndexed(result);
213 | found += result.size();
214 | }
215 | return found;
216 | });
217 | stopBenchmark();
218 | log("Entities found: " + entitiesFound);
219 | }
220 |
221 | private void runQueryByInteger() {
222 | if (numberEntities > 10000) {
223 | log("Reduce number of entities to 10000 to avoid extremely long test runs");
224 | return;
225 | }
226 | List entities = new ArrayList<>(numberEntities);
227 | for (int i = 0; i < numberEntities; i++) {
228 | entities.add(createEntity((long) i, false));
229 | }
230 |
231 | startBenchmark("insert");
232 | dao.insertInTx(entities);
233 | stopBenchmark();
234 |
235 | final int[] valuesToLookup = new int[numberEntities];
236 | for (int i = 0; i < numberEntities; i++) {
237 | valuesToLookup[i] = entities.get(random.nextInt(numberEntities)).getSimpleInt();
238 | }
239 |
240 | startBenchmark("query");
241 | long entitiesFound = 0;
242 | for (int i = 0; i < numberEntities; i++) {
243 | List result = dao.whereSimpleIntEq(valuesToLookup[i]);
244 | accessAll(result);
245 | entitiesFound += result.size();
246 | }
247 | stopBenchmark();
248 | log("Entities found: " + entitiesFound);
249 | assertGreaterOrEqualToNumberOfEntities(entitiesFound);
250 | }
251 |
252 | private void runQueryByIntegerIndexed() {
253 | List entities = new ArrayList<>(numberEntities);
254 | for (int i = 0; i < numberEntities; i++) {
255 | entities.add(createEntityIndexed((long) i));
256 | }
257 |
258 | startBenchmark("insert");
259 | daoIndexed.insertInTx(entities);
260 | stopBenchmark();
261 |
262 | final int[] valuesToLookup = new int[numberEntities];
263 | for (int i = 0; i < numberEntities; i++) {
264 | valuesToLookup[i] = entities.get(random.nextInt(numberEntities)).getSimpleInt();
265 | }
266 |
267 | startBenchmark("query");
268 | long entitiesFound = 0;
269 | for (int i = 0; i < numberEntities; i++) {
270 | List result = daoIndexed.whereSimpleIntEq(valuesToLookup[i]);
271 | accessAllIndexed(result);
272 | entitiesFound += result.size();
273 | }
274 | stopBenchmark();
275 | log("Entities found: " + entitiesFound);
276 | assertGreaterOrEqualToNumberOfEntities(entitiesFound);
277 | }
278 |
279 | private void runQueryById(boolean randomIds) {
280 | List entities = new ArrayList<>(numberEntities);
281 | for (int i = 0; i < numberEntities; i++) {
282 | entities.add(createEntity((long) i, false));
283 | }
284 |
285 | startBenchmark("insert");
286 | dao.insertInTx(entities);
287 | stopBenchmark();
288 |
289 | assertEntityCount(dao.count());
290 |
291 | long[] idsToLookup = new long[numberEntities];
292 | for (int i = 0; i < numberEntities; i++) {
293 | idsToLookup[i] = randomIds ? random.nextInt(numberEntities) : i;
294 | }
295 |
296 | startBenchmark("query");
297 | for (int i = 0; i < numberEntities; i++) {
298 | SimpleEntity entity = dao.load(idsToLookup[i]);
299 | accessAll(entity);
300 | }
301 | stopBenchmark();
302 | }
303 |
304 | @Override
305 | public void tearDown() {
306 | super.tearDown();
307 | db.close();
308 | boolean deleted = context.deleteDatabase(DB_NAME);
309 | log("DB deleted: " + deleted);
310 | }
311 |
312 | @SuppressWarnings("ResultOfMethodCallIgnored")
313 | private void accessAll(SimpleEntity entity) {
314 | entity.getId();
315 | entity.getSimpleBoolean();
316 | entity.getSimpleByte();
317 | entity.getSimpleShort();
318 | entity.getSimpleInt();
319 | entity.getSimpleLong();
320 | entity.getSimpleFloat();
321 | entity.getSimpleDouble();
322 | entity.getSimpleString();
323 | entity.getSimpleByteArray();
324 | }
325 |
326 | @SuppressWarnings("ResultOfMethodCallIgnored")
327 | protected void accessAll(List list) {
328 | for (SimpleEntity entity : list) {
329 | entity.getId();
330 | entity.getSimpleBoolean();
331 | entity.getSimpleByte();
332 | entity.getSimpleShort();
333 | entity.getSimpleInt();
334 | entity.getSimpleLong();
335 | entity.getSimpleFloat();
336 | entity.getSimpleDouble();
337 | entity.getSimpleString();
338 | entity.getSimpleByteArray();
339 | }
340 | }
341 |
342 | @SuppressWarnings("ResultOfMethodCallIgnored")
343 | protected void accessAllIndexed(List list) {
344 | for (SimpleEntityIndexed entity : list) {
345 | entity.getId();
346 | entity.getSimpleBoolean();
347 | entity.getSimpleByte();
348 | entity.getSimpleShort();
349 | entity.getSimpleInt();
350 | entity.getSimpleLong();
351 | entity.getSimpleFloat();
352 | entity.getSimpleDouble();
353 | entity.getSimpleString();
354 | entity.getSimpleByteArray();
355 | }
356 | }
357 |
358 | private void setRandomValues(SimpleEntity entity) {
359 | setRandomScalars(entity);
360 | entity.setSimpleString(randomString());
361 | entity.setSimpleByteArray(randomBytes());
362 | }
363 |
364 | protected void setRandomValues(SimpleEntityIndexed entity) {
365 | entity.setSimpleBoolean(random.nextBoolean());
366 | entity.setSimpleByte((byte) random.nextInt());
367 | entity.setSimpleShort((short) random.nextInt());
368 | entity.setSimpleInt(random.nextInt());
369 | entity.setSimpleLong(random.nextLong());
370 | entity.setSimpleDouble(random.nextDouble());
371 | entity.setSimpleFloat(random.nextFloat());
372 | entity.setSimpleString(randomString());
373 | entity.setSimpleByteArray(randomBytes());
374 | }
375 |
376 | private void setRandomScalars(SimpleEntity entity) {
377 | entity.setSimpleBoolean(random.nextBoolean());
378 | entity.setSimpleByte((byte) random.nextInt());
379 | entity.setSimpleShort((short) random.nextInt());
380 | entity.setSimpleInt(random.nextInt());
381 | entity.setSimpleLong(random.nextLong());
382 | entity.setSimpleDouble(random.nextDouble());
383 | entity.setSimpleFloat(random.nextFloat());
384 | }
385 |
386 | private SimpleEntity createEntity(Long key, boolean scalarsOnly) {
387 | SimpleEntity entity = new SimpleEntity();
388 | if (key != null) {
389 | entity.setId(key);
390 | }
391 | if (scalarsOnly) {
392 | setRandomScalars(entity);
393 | } else {
394 | setRandomValues(entity);
395 | }
396 | return entity;
397 | }
398 |
399 | public SimpleEntityIndexed createEntityIndexed(Long key) {
400 | SimpleEntityIndexed entity = new SimpleEntityIndexed();
401 | if (key != null) {
402 | entity.setId(key);
403 | }
404 | setRandomValues(entity);
405 | return entity;
406 | }
407 | }
408 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/room/SimpleEntity.java:
--------------------------------------------------------------------------------
1 | package io.objectbox.performanceapp.room;
2 |
3 | import androidx.room.Entity;
4 | import androidx.room.PrimaryKey;
5 |
6 | @Entity
7 | public class SimpleEntity {
8 |
9 | @PrimaryKey
10 | long id;
11 | boolean simpleBoolean;
12 | byte simpleByte;
13 | short simpleShort;
14 | int simpleInt;
15 | long simpleLong;
16 | float simpleFloat;
17 | double simpleDouble;
18 |
19 | String simpleString;
20 |
21 | byte[] simpleByteArray;
22 |
23 | public long getId() {
24 | return id;
25 | }
26 |
27 | public void setId(long id) {
28 | this.id = id;
29 | }
30 |
31 | public boolean getSimpleBoolean() {
32 | return simpleBoolean;
33 | }
34 |
35 | public void setSimpleBoolean(boolean simpleBoolean) {
36 | this.simpleBoolean = simpleBoolean;
37 | }
38 |
39 | public byte getSimpleByte() {
40 | return simpleByte;
41 | }
42 |
43 | public void setSimpleByte(byte simpleByte) {
44 | this.simpleByte = simpleByte;
45 | }
46 |
47 | public short getSimpleShort() {
48 | return simpleShort;
49 | }
50 |
51 | public void setSimpleShort(short simpleShort) {
52 | this.simpleShort = simpleShort;
53 | }
54 |
55 | public int getSimpleInt() {
56 | return simpleInt;
57 | }
58 |
59 | public void setSimpleInt(int simpleInt) {
60 | this.simpleInt = simpleInt;
61 | }
62 |
63 | public long getSimpleLong() {
64 | return simpleLong;
65 | }
66 |
67 | public void setSimpleLong(long simpleLong) {
68 | this.simpleLong = simpleLong;
69 | }
70 |
71 | public float getSimpleFloat() {
72 | return simpleFloat;
73 | }
74 |
75 | public void setSimpleFloat(float simpleFloat) {
76 | this.simpleFloat = simpleFloat;
77 | }
78 |
79 | public double getSimpleDouble() {
80 | return simpleDouble;
81 | }
82 |
83 | public void setSimpleDouble(double simpleDouble) {
84 | this.simpleDouble = simpleDouble;
85 | }
86 |
87 | public String getSimpleString() {
88 | return simpleString;
89 | }
90 |
91 | public void setSimpleString(String simpleString) {
92 | this.simpleString = simpleString;
93 | }
94 |
95 | public byte[] getSimpleByteArray() {
96 | return simpleByteArray;
97 | }
98 |
99 | public void setSimpleByteArray(byte[] simpleByteArray) {
100 | this.simpleByteArray = simpleByteArray;
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/room/SimpleEntityDao.java:
--------------------------------------------------------------------------------
1 | package io.objectbox.performanceapp.room;
2 |
3 | import androidx.room.Dao;
4 | import androidx.room.Delete;
5 | import androidx.room.Insert;
6 | import androidx.room.Query;
7 | import androidx.room.Update;
8 |
9 | import java.util.List;
10 |
11 | @Dao
12 | public interface SimpleEntityDao {
13 |
14 | @Insert
15 | void insertInTx(List entities);
16 |
17 | @Query("SELECT * from simpleentity where id = :id LIMIT 1")
18 | SimpleEntity load(long id);
19 |
20 | @Query("SELECT * FROM simpleentity")
21 | List loadAll();
22 |
23 | @Update
24 | void updateInTx(List entities);
25 |
26 | @Delete
27 | void deleteInTx(List entities);
28 |
29 | @Query("SELECT * from simpleentity where simpleInt = :value")
30 | List whereSimpleIntEq(int value);
31 |
32 | @Query("SELECT * FROM simpleentity WHERE simpleString = :value")
33 | List whereSimpleStringEq(String value);
34 |
35 | @Query("SELECT COUNT(*) from simpleentity")
36 | int count();
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/room/SimpleEntityIndexed.java:
--------------------------------------------------------------------------------
1 | package io.objectbox.performanceapp.room;
2 |
3 | import androidx.room.Entity;
4 | import androidx.room.Index;
5 | import androidx.room.PrimaryKey;
6 |
7 | @Entity(indices = {@Index("simpleInt"), @Index("simpleString")})
8 | public class SimpleEntityIndexed {
9 |
10 | @PrimaryKey
11 | private long id;
12 | private boolean simpleBoolean;
13 | private byte simpleByte;
14 | private short simpleShort;
15 | // index
16 | private int simpleInt;
17 | private long simpleLong;
18 | private float simpleFloat;
19 | private double simpleDouble;
20 |
21 | // index
22 | private String simpleString;
23 |
24 | private byte[] simpleByteArray;
25 |
26 | public long getId() {
27 | return id;
28 | }
29 |
30 | public void setId(long id) {
31 | this.id = id;
32 | }
33 |
34 | public boolean getSimpleBoolean() {
35 | return simpleBoolean;
36 | }
37 |
38 | public void setSimpleBoolean(boolean simpleBoolean) {
39 | this.simpleBoolean = simpleBoolean;
40 | }
41 |
42 | public byte getSimpleByte() {
43 | return simpleByte;
44 | }
45 |
46 | public void setSimpleByte(byte simpleByte) {
47 | this.simpleByte = simpleByte;
48 | }
49 |
50 | public short getSimpleShort() {
51 | return simpleShort;
52 | }
53 |
54 | public void setSimpleShort(short simpleShort) {
55 | this.simpleShort = simpleShort;
56 | }
57 |
58 | public int getSimpleInt() {
59 | return simpleInt;
60 | }
61 |
62 | public void setSimpleInt(int simpleInt) {
63 | this.simpleInt = simpleInt;
64 | }
65 |
66 | public long getSimpleLong() {
67 | return simpleLong;
68 | }
69 |
70 | public void setSimpleLong(long simpleLong) {
71 | this.simpleLong = simpleLong;
72 | }
73 |
74 | public float getSimpleFloat() {
75 | return simpleFloat;
76 | }
77 |
78 | public void setSimpleFloat(float simpleFloat) {
79 | this.simpleFloat = simpleFloat;
80 | }
81 |
82 | public double getSimpleDouble() {
83 | return simpleDouble;
84 | }
85 |
86 | public void setSimpleDouble(double simpleDouble) {
87 | this.simpleDouble = simpleDouble;
88 | }
89 |
90 | public String getSimpleString() {
91 | return simpleString;
92 | }
93 |
94 | public void setSimpleString(String simpleString) {
95 | this.simpleString = simpleString;
96 | }
97 |
98 | public byte[] getSimpleByteArray() {
99 | return simpleByteArray;
100 | }
101 |
102 | public void setSimpleByteArray(byte[] simpleByteArray) {
103 | this.simpleByteArray = simpleByteArray;
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/app/src/main/java/io/objectbox/performanceapp/room/SimpleEntityIndexedDao.java:
--------------------------------------------------------------------------------
1 | package io.objectbox.performanceapp.room;
2 |
3 | import androidx.room.Dao;
4 | import androidx.room.Delete;
5 | import androidx.room.Insert;
6 | import androidx.room.Query;
7 | import androidx.room.Update;
8 |
9 | import java.util.List;
10 |
11 | @Dao
12 | public interface SimpleEntityIndexedDao {
13 |
14 | @Insert
15 | void insertInTx(List entities);
16 |
17 | @Query("SELECT * FROM simpleentityindexed")
18 | List loadAll();
19 |
20 | @Update
21 | void updateInTx(List entities);
22 |
23 | @Delete
24 | void deleteInTx(List entities);
25 |
26 | @Query("SELECT * from simpleentityindexed where simpleInt = :value")
27 | List whereSimpleIntEq(int value);
28 |
29 | @Query("SELECT * FROM simpleentityindexed WHERE simpleString = :value")
30 | List whereSimpleStringEq(String value);
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
25 |
26 |
36 |
37 |
45 |
46 |
56 |
57 |
66 |
67 |
75 |
76 |
77 |
86 |
87 |
88 |
98 |
99 |
100 |
112 |
113 |
124 |
125 |
126 |
134 |
135 |
141 |
142 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-performance/e8d3c35b2ab4fc774c04284489b7c275499f5470/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-performance/e8d3c35b2ab4fc774c04284489b7c275499f5470/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-performance/e8d3c35b2ab4fc774c04284489b7c275499f5470/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-performance/e8d3c35b2ab4fc774c04284489b7c275499f5470/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-performance/e8d3c35b2ab4fc774c04284489b7c275499f5470/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ObjectBox Performance
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | // https://github.com/ben-manes/gradle-versions-plugin/releases
4 | id("com.github.ben-manes.versions") version "0.51.0"
5 | }
6 |
7 | buildscript {
8 | val objectboxVersion by extra("4.0.0")
9 | repositories {
10 | google()
11 | mavenCentral()
12 | }
13 | dependencies {
14 | classpath("com.android.tools.build:gradle:8.4.1")
15 | classpath("io.objectbox:objectbox-gradle-plugin:$objectboxVersion")
16 | classpath("io.realm:realm-gradle-plugin:10.18.0")
17 |
18 | // NOTE: Do not place your application dependencies here; they belong
19 | // in the individual module build.gradle files
20 | }
21 | }
22 |
23 | allprojects {
24 | repositories {
25 | google()
26 | mavenCentral()
27 | }
28 | }
29 |
30 | // Reject preview releases for dependencyUpdates task
31 | fun isNonStable(version: String): Boolean {
32 | val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase().contains(it) }
33 | val regex = "^[0-9,.v-]+(-r)?$".toRegex()
34 | val isStable = stableKeyword || regex.matches(version)
35 | return isStable.not()
36 | }
37 | tasks.withType {
38 | rejectVersionIf {
39 | isNonStable(candidate.version)
40 | }
41 | }
42 |
43 | tasks.wrapper {
44 | distributionType = Wrapper.DistributionType.ALL
45 | }
46 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
19 | org.gradle.daemon=true
20 | android.useAndroidX=true
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objectbox/objectbox-performance/e8d3c35b2ab4fc774c04284489b7c275499f5470/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/greenDAO-generator/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "java-library"
2 |
3 | java {
4 | sourceCompatibility = JavaVersion.VERSION_1_8
5 | targetCompatibility = JavaVersion.VERSION_1_8
6 | }
7 |
8 | sourceSets {
9 | main {
10 | java {
11 | srcDir 'src'
12 | }
13 | }
14 | }
15 |
16 | dependencies {
17 | implementation("org.greenrobot:greendao-generator:3.3.0")
18 | }
19 |
--------------------------------------------------------------------------------
/greenDAO-generator/src/io/objectbox/performanceapp/generator/TestDaoGenerator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2017 ObjectBox Ltd. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package io.objectbox.performanceapp.generator;
17 |
18 | import org.greenrobot.greendao.generator.DaoGenerator;
19 | import org.greenrobot.greendao.generator.Entity;
20 | import org.greenrobot.greendao.generator.Property.PropertyBuilder;
21 | import org.greenrobot.greendao.generator.Schema;
22 |
23 | /**
24 | * Generates test entities for test project DaoTest.
25 | *
26 | * @author Markus
27 | */
28 | public class TestDaoGenerator {
29 |
30 | public static void main(String[] args) throws Exception {
31 | TestDaoGenerator testDaoGenerator = new TestDaoGenerator();
32 | testDaoGenerator.generate();
33 | }
34 |
35 | private final Schema schema;
36 |
37 | public TestDaoGenerator() {
38 | schema = new Schema(1, "io.objectbox.performanceapp.greendao");
39 | createSimple("SimpleEntity", false);
40 | createSimple("SimpleEntityIndexed", true);
41 | }
42 |
43 | public void generate() throws Exception {
44 | DaoGenerator daoGenerator = new DaoGenerator();
45 | String src = "../app/src/main/java/";
46 | daoGenerator.generateAll(schema, src);
47 | }
48 |
49 | protected void createSimple(String name, boolean indexed) {
50 | Entity notNull = schema.addEntity(name);
51 | notNull.addIdProperty().notNull();
52 | notNull.addBooleanProperty("simpleBoolean").notNull();
53 | notNull.addByteProperty("simpleByte").notNull();
54 | notNull.addShortProperty("simpleShort").notNull();
55 | PropertyBuilder simpleInt = notNull.addIntProperty("simpleInt").notNull();
56 | if (indexed) {
57 | simpleInt.index();
58 | }
59 | notNull.addLongProperty("simpleLong").notNull();
60 | notNull.addFloatProperty("simpleFloat").notNull();
61 | notNull.addDoubleProperty("simpleDouble").notNull();
62 | PropertyBuilder simpleString = notNull.addStringProperty("simpleString");
63 | if(indexed) {
64 | simpleString.index();
65 | }
66 | notNull.addByteArrayProperty("simpleByteArray");
67 | }
68 |
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | include ':greenDAO-generator'
3 |
--------------------------------------------------------------------------------