Anko SQLite, a library to simplify working with SQLite on Android
kotlindevelopment.com
kotlindevelopment.com
https://developer.android.com/topic/libraries/architecture/r...
It's in their list, at half the size and method count of Anko (with no need to pull in the Kotlin runtime if you're not using Kotlin). It offers all the same features, really awesome migration testing, and RxJava integration.
For me these days the options that make sense in Android's world of infinite DBs/Orms/Wrappers are down to SQLBrite+SQLDelight, Realm, or Room depending on what you want
You can reach a point where it's not feasible to have everything done by joins.
EDIT: I mean in relational DBs in general. In this case, 400+ columns probably mean SQLite may not be the engine to use, regardless of schema :)
Personally I don't like it and I am luckily not in IT, but as an analyst I have seen many weird database structures. One can only imagine the business requirements that gave birth to some of them.
To quote a solutions architect to the head of marketing in a previous job: "You asked for a monster, you got a monster."
Note that this is very much in line with their recommendations from the docs (http://sqlite.org/whentouse.html):
>>SQLite is not directly comparable to client/server SQL database engines such as MySQL, Oracle, PostgreSQL, or SQL Server since SQLite is trying to solve a different problem.
Client/server SQL database engines strive to implement a shared repository of enterprise data. They emphasize scalability, concurrency, centralization, and control. SQLite strives to provide local data storage for individual applications and devices. SQLite emphasizes economy, efficiency, reliability, independence, and simplicity.
SQLite does not compete with client/server databases. SQLite competes with fopen().<<
It's worth noting that when considered as competition to fopen(), SQLite is quite good.
I've always wondered why the creator of SQLite (who's also the creator of Fossil SCM) will state that SQLite is not meant for client/server scenarios ... yet his Fossil SCM (which is client/server) uses SQLite.
It places no restrictions whatsoever on the applications that uses it. That will be pointless. Fossil-SCM uses SQLite and is itself client server.
It is fine to use SQLite as the local storage in the implementation of some custom server like Fossil, as there is a separate server (Fossil) that sits in between the client and the data. In fact, SQLite excels at this and usually works better than traditional client/server databases. The scenario you want to avoid is where the client attempts to access SQLite data directly across the network, with no intermediary server. The proverb is: "Always send your query to the data, not the data to the query."
It will work to access an SQLite database across a network filesystem. Just remember that the amount of information that flows between the SQL engine and the storage medium is much greater than the amount of information that flows between the SQL engine and the application. So if the storage is separated by from the application by a relatively slow network, you want to position that network on the low-bandwidth link to obtain the best performance. That means that the SQL engine needs to be on the same side of the network as the storage - hence a client/server database. If you use SQLite to access a network file, the SQL engine will be on the application side of the network and a lot more content will need to traverse the relatively slow network link.
And so, while SQLite will work in a client/server situation, you might be disappointed by the resulting network load and/or performance of the system.
In the case of Fossil, Fossil itself is the "client" for SQLite and it is on the same machine as the data, which is exactly what you want with SQLite. The client of the Fossil server might well be across a network, but that does not matter to SQLite.
So how is what you described above architecturally different than a web application architecture?
Which is a use case I thought you did not recommend.
But, our desire for both simplicity and obscenely low latency means that SQLite is still the better choice for us at the moment. In the future, though, I can see robust concurrency support being important even in single application use-cases.
Not only would you get rid of network latency, you would also vastly reduce your dependencies.
I have a side project to try to see if I can build a minimal micro-blogging type server application on sqlite and try to test how many requests it can handle per second (reads and writes).
I have a hunch it will be able to handle quite a lot. Specially if the server code is written in a fast language that compiles to native code (e.g. D) with some caching of the most commonly requested items in memory.
Nonetheless I think it's a great idea to build your own single-threaded layer over SQLite and scale that, using a message bus or whatever.
But it's _not_ a drop-in replacement for MySQL based apps.
Compile a static site from SQLite when someone enters new data, push it somewhere to be accessible and be done with it.
I want no dependencies .. and no dependency manager.
> An Unix socket has something like 1ns.
That seems implausible, given that you're lucky to get a context switch down to 1µs if the cache gods smile on you.https://www.reddit.com/r/PHP/comments/59na74/sqlite_as_the_o...
In the app, for each relation (e.g., a "workout set"), I have 2 tables: a master and a scratchpad. When a user saves a set, a row is written to the scratchpad table. When the user syncs it with the server, a row is written to the master table and deleted from the scratchpad table. When the user wants to edit the record, I first copy it down from the master table to the scratchpad table. All local editing impacts the scratchpad row. When the user wants to sync, only if a 200 response is returned will I copy-up the scratchpad row to the master row. If the set was edited on another device and the local copy is out-of-sync, the server would have responded with a 409 (http conflict code), and the body would contain the server copy, which is then written to the master table. The user can then figure how they want to merge the scratchpad row and the master row.
Anyway...trying to do all this with CoreData would have been a pain, so I use SQLite directly, and works great.
Or to summarize, I handle offline mode, syncing and conflict detection using "updated_at" timestamp columns along with logic in my REST API to returned appropriate HTTP status codes, interpret "if-unmodified-since" headers, etc.
https://itunes.apple.com/us/app/riker/id1196920730?mt=8
Riker on Android is currently in-progress...
But yes, you're right overall - full offline mode w/syncing, etc is a big pain :)
The performance is high!
With ORM packages, the degree of I/O becomes proportional to the app's feature growth, and the UI eventually stutters. I've been on several successful app teams, and everyone that started with ORMs had to throw them away to solve stutter, and since the data and threading models of the apps were written around ORMs, they had to be mostly rewritten. Going through this, I also noticed that the before and after ORM code was about the same in line numbers, and it didn't save time to use the ORM (because ORMs have lots of negatives that required time to work around, e.g., distancing you from control over the use of indices).
For a great example of successful UI code, see Chromium, which explicitly outlaws blocking I/O on the UI thread.
This doesn't have to mean avoiding ORMs; it means separating frontend from backend with an explicit, narrow interface between the two, but that's no reason not to use an ORM in the backend piece.
> I've been on several successful app teams, and everyone that started with ORMs had to throw them away to solve stutter, and since the data and threading models of the apps were written around ORMs, they had to be mostly rewritten.
Even in that kind of case (which doesn't match my experience) that doesn't mean the ORM was a mistake; 90% of apps fail, so if you can save time on getting to the point where you can verify product/market fit one way or another, that's well worth doing even if it leads to more work in the cases where you do want to develop the app further.
> Going through this, I also noticed that the before and after ORM code was about the same in line numbers, and it didn't save time to use the ORM (because ORMs have lots of negatives that required time to work around, e.g., distancing you from control over the use of indices).
Not my experience. Or rather, that matches my experience on teams that tried to maintain manual control over the database while using an ORM, but teams that were willing to embrace the ORM and use the database in an ORM-first way (i.e. the ORM is the source of truth about what the schema looks like, and the DDL is generated from that) have been able to save a significant amount of code and have a lower defect rate.
>For a great example of successful UI code, see Chromium, which explicitly outlaws blocking I/O on the UI thread.
Android explicitly outlaws network I/O on the UI thread by default, and can be configured to block disk I/O too via StrictMode
ORM's are still a great solution for a wide majority of scenarios.
https://developer.android.com/topic/libraries/architecture/r...
Leaving some technicalities aside, SQLite is SQL. So I guess we're not talking the same here.
SQL scripts in assets/ folder would be not cool or sexy, but everybody knows what are they.
Instead what we really need is a way to map a row from an sql query to a struct (or similar).
Luckily, the SQLite helpers from Anko do provide this ability, and it's pretty much the only part I use.
data class UserRow(val id: Long, val name: String);
// .... open db .. etc
var users = db.query<UserRow>("select id, name from users where ....."); // where clause content omitted
// now users is a list of structs (as close to structs as you can get in Kotlin).
Where I have an extension method `query`: inline fun <reified T : Any> SQLiteDatabase.query(sql: String, vararg args: String): List<T> {
this.rawQuery(sql, args).parseList(classParser<T>())
} database.use {
insert(Book.TABLE_NAME, Book.COLUMN_ID to 1, Book.COLUMN_TITLE to "2666", Book.COLUMN_AUTHOR to "Roberto Bolano")
}
That's pretty bad design: you're throwing out type safety with this API.