Simple but powerful SQL library for tiny projects

Related tags

Database KiORM
Overview

KiORM - Simple and powerful MySQL Java library for tiny projects

Notice: KiORM is still in SNAPSHOT state. The code is not tested, there is no Javadoc, the API may change unexpectedly. If that doesn't bother you, welcome! 😎

KiORM is simple MySQL library built on top of JDBC, that was originally created to build Minecraft plugins, but can be used anywhere. It provides some basic data serialization/deserialization functionality that makes it easy to map classes to SQL tables and backwards. Note, that it does not cover SQL relations mapping. KiORM was build to make database interactions require as less code as possible, so you don't need to worry about drivers, etc. Just make sure, that mysql-connector-java is included to your runtime and enjoy coding 🧑‍💻

Java 8 Open issues

💼 This readme contains full library documentation/tutorial!

Install

SNAPSHOT! Not for production use!

Gradle:

repositories {
    maven {
        name = 'rikonardo-repository-snapshots'
        url = 'https://maven.rikonardo.com/snapshots/'
    }
}

dependencies {
    implementation 'com.rikonardo.kiorm:KiORM:1.0.0-SNAPSHOT'
}

Maven:

<project>
    <repositories>
        <repository>
            <id>rikonardo-repository-snapshots</id>
            <name>Rikonardo's repo (snapshots)</name>
            <url>https://maven.rikonardo.com/snapshots/</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>com.rikonardo.kiorm</groupId>
            <artifactId>KiORM</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

If you are using it in Minecraft plugin, you can just shade this library into your jar using gradle Shadow plugin

Documentation

Content
1. Basic usage
2. SQL clauses
3. Computed fields
4. Serializer classes
5. Exceptions
6. Creating tables
7. Raw SQL queries
8. Fixed types and binary/text primary keys
9. Table/Field name modifiers
10. Schema caching
11. Usage with Lombok

Basic usage

Interact with database is as easy as write:

// Specify table name here
@Document("players")
class Player {
    // String, passed to @Field will be used as field name in database
    @Field("name") public String name;
    @Field("score") public int score;
}

class Main {
    public static void main(String[] args) {
        // Connecting to database
        KiORM database = new KiORM("jdbc:mysql://[email protected]:3306/dbname");
        // Create table based on class structure
        database.createTableIfNotExist(Player.class);
        // Create player object
        Player p = new Player();
        p.name = "SatoshiNakamoto";
        p.score = 100;
        // Save it do database
        database.insert(p).exec();
    }
}

There is another way to connect to the database:

class Main {
    public static void main(String[] args) {
        KiORM database = new KiORM();
        database.connect("jdbc:mysql://[email protected]:3306/dbname");
    }
}

In the most time, we would want to have unique ID for every object in our database. It is pretty easy to do, just add numeric field with two additional annotations:

@Document("players")
class Player {
    @Field("id") @PrimaryKey @AutoIncrement public long id;
    @Field("name") public String name;
    @Field("score") public int score;
}

class Main {
    public static void main(String[] args) {
        KiORM database = new KiORM("jdbc:mysql://[email protected]:3306/dbname");
        database.createTableIfNotExist(Player.class);
        Player p = new Player();
        p.name = "SatoshiNakamoto";
        p.score = 100;
        database.insert(p).exec();
        // KiORM would automatically fill ID field after insert operation
        System.out.println(p.id);
        p.score += 200;
        // Documents with ID fields (or just with primary keys) can be used in update and delete queries
        database.update(p).exec();
    }
}

Notice, that @PrimaryKey can be used without @AutoIncrement, but @AutoIncrement requires @PrimaryKey to be set on field. You can also use a composite primary key by specifying @PrimaryKey on multiple fields.

List of supported field types and their SQL names: String (TEXT), boolean (BOOLEAN), byte (TINYINT), short (SMALLINT), int (INT), long (BIGINT), float (FLOAT), double (DOUBLE), byte[] (BLOB). There are also two fixed types, but more on that later.

Important: KiORM only serializes fields marked with @Field. Even if field is private, KiORM will access it, so you don't need to think about how access modifiers affects data mapping.

Important: KiORM requires document class to have public no-args constructor in order to be used in SELECT queries. By default, java implicitly creates it for you, but when you create your own constructor, the implicit one disappears, and you need to add it manually by writing public ClassName() {}.

SQL clauses

KiORM provides pretty simple java api for Where, OrderBy, etc. clauses. Here is an example:

class Main {
    public static void main(String[] args) {
        KiORM database = new KiORM("jdbc:mysql://[email protected]:3306/dbname");
        // Getting list of players, which score is >= 100
        List<Player> players = database
            .select(Player.class)
            .where(Where.gte("score", 100))
            .exec();
    }
}

It can also do more complicated queries. For SELECT there are .where(), .order() and .limit() clauses available.

class Main {
    public static void main(String[] args) {
        KiORM database = new KiORM("jdbc:mysql://[email protected]:3306/dbname");
        List<Player> players = database
            .select(Player.class)
            .where(
                Where.and(
                    Where.eq("alive", true),
                    Where.gte("score", 100),
                    Where.or(
                        Where.between("health", 10, 50),
                        Where.neq("type", "zombie")
                    )
                )
            )
            .order(
                Order.asc("score"),
                Order.desc("health")
            )
            .limit(10)
            .exec();
    }
}

INSERT, UPDATE and DELETE queries does not support any clauses. There also COUNT query, which supports .where() clause and can be used to count documents in table:

class Main {
    public static void main(String[] args) {
        KiORM database = new KiORM("jdbc:mysql://[email protected]:3306/dbname");
        long players = database
            .count(Player.class)
            .where(Where.gte("score", 100))
            .exec();
        System.out.println(players);
    }
}

All clauses are optional, you can run query like this to get all players:

class Main {
    public static void main(String[] args) {
        KiORM database = new KiORM("jdbc:mysql://[email protected]:3306/dbname");
        List<Player> players = database.select(Player.class).exec();
    }
}

Important: KiORM currently does not serialize values, passed to .where() clause. It also does not apply field name modifier to field names, passed to .where() and .order() clauses. You need to serialize values and apply name modifiers by yourself.

Computed fields

Sometimes we need to go beyond primitive types and store something more complicated, like JSON or player UUID. KiORM provides two different way to achieve this, and computed fields is the first one.

@Document("items")
class Item {
    @Field("id") @PrimaryKey @AutoIncrement public long id;
    @Field("type") public String type;
    @Field("durability") public int durability;

    public Player owner;

    @Field("owner")
    private String getPlayerId() {
        return this.owner.getUUID();
    }

    @Field("owner")
    private void setPlayerId(String uuid) {
        this.owner = Player.fromUUID(uuid);
    }
}

This code allows you to interact only with real Player instance, while it stored as uuid string. getPlayerId and setPlayerId are private, so they won't distract you during development.

Computed fields always consists of two methods - a getter and a setter, returning/accepting one of the values, supported by the database (unless you use @Serializer along with a computed field, but more on that later).

Computed fields can also be primary key and even auto increment:

@Document("items")
class Item {
    public CoolIdClass id;

    @PrimaryKey
    @AutoIncrement
    @Field("id")
    private long getPlayerId() {
        return this.id.getAsLong();
    }

    @Field("id")
    private void setPlayerId(long id) {
        this.id = new CoolIdClass(id);
    }
}

You may notice, that we specified @PrimaryKey and @AutoIncrement at getter, but in fact it does not matter. You can specify them both at setter, or even specify one annotation at getter and another at setter. KiORM would merge them.

Serializer classes

Remember I said about two ways of complicated data serialization? Here it is! Computed fields are good when you have one or two of them, but if you have two fields of the same type, then you would want to write serialization/deserialization code separately and reuse it at multiple fields.

This is where the FieldSerializer<R, S> interface comes. It has two generic types (R and S), where R means the real type and S is the storage type. Just create a class that implements this interface and pass it in the @Serializer annotation on the desired field:

class CoolIdSerializer implements FieldSerializer<CoolIdClass, Long> {
    @Override
    public Long serialize(CoolIdClass id) {
        return id.getAsLong();
    }

    @Override
    public CoolIdClass deserialize(Long id) {
        return new CoolIdClass(id);
    }
}

@Document("items")
class Item {
    @Serializer(CoolIdSerializer.class)
    @PrimaryKey @AutoIncrement @Field("id") public CoolIdClass id;
}

As you see, we are using Long instead of long here. KiORM would automatically care about object <-> primitive converting.

Exceptions

KiORM provides three exceptions: InvalidDocumentClassException, InvalidQueryException and RuntimeSQLException.
All of them extends RuntimeException.
InvalidDocumentClassException indicates an incorrectly defined document class. With a valid document class declaration, you won't encounter this exception. The most common reason for this exception is the use of a data type not supported by the library as a storage type.
InvalidQueryException indicates an invalid query. Can often be caused by passing an unsupported data type to the .where() clause. Note that even if you use a computed field or @Serializer, the values passed to the .where() clause will not be serialized, you need to pass them in the same type as they are stored in the database.
RuntimeSQLException is the only exception that can happen even when the code is correct. Basically, it can occur when there are problems connecting to the database, or when an incorrect request is formed (for example, when passing the name of a non-existent field to the .where() clause).

Creating tables

As was shown earlier, you can easily create new table by calling database.createTableIfNotExist(YourDocumentHere.class), but it is actually just combination of two other methods, which can be used separately: database.checkIfTableExists(YourDocumentHere.class) and database.createTable(YourDocumentHere.class). Currently, there are no table schema verification, if your code created mysql table, and then you changed its schema in the code, you may get errors.

Raw SQL queries

While using all advantages of KiORM, you still can use raw SQL queries for things, that KiORM can't do. All you need to get JDBC connection and execute any queries is write database.getConnection()

Fixed types and binary/text primary keys

By default, you can't use text or binary fields as primary key, because MySQL can't index fields of dynamic length. KiORM has fixed types to achieve this.

On fields, which storage type is String or byte[], you can add @Fixed(n) annotation, where n is int, that represents string length for String and array length for byte[]. Under the hood KiORM would use CHAR(n) and BINARY(n) sql types to store this fields.

Here is an example:

@Document("players")
class Player {
    @Field("id") @Fixed(36) @PrimaryKey public String id;
    @Field("score") public int score;
}

class Main {
    public static void main(String[] args) {
        KiORM database = new KiORM("jdbc:mysql://[email protected]:3306/dbname");
        database.createTableIfNotExist(Player.class);
        Player p = new Player();
        p.id = "45d01b53-cfc4-4a57-b795-d9cd2a91104c"; // UUID with dashes is always 36 chars long
        p.score = 100;
        database.insert(p).exec();
        p.score += 200;
        // Since the document has a primary key, it can be used in update and delete queries
        database.update(p).exec();
    }
}

Fixed types can be used with computed fields and serializers as well.

Table/Field name modifiers

Sometimes we need to specify table name in runtime (for example get it from configuration). Here name modifiers comes:

class Main {
    public static void main(String[] args) {
        Map<Class<?>, String> tablesPrefixes = new HashMap<>();
        tablesPrefixes.put(Item.class, "new_");

        KiORM database = new KiORM("jdbc:mysql://[email protected]:3306/dbname");
        database.setTableNameModifier((name, type) -> tablesPrefixes.get(type) + name);
        database.createTableIfNotExist(Items.class); // Will create table with name "new_items"
    }
}

There is absolutely same way for modifying field names (database.setFieldNameModifier((name, type) -> ...)).

Important: Due to schema caching, this lambda will be invoked only once. If you need to update table names, you can clear cache by calling KiORM.clearSchemaCache()

Schema caching

By default, KiORM caches schemas after first parsing. The cache key consists of three things - the document class, the tableNameModifier lambda, and the fieldNameModifier lambda.

You can completely disable schema cache by adding -Dkiorm.disableDocumentSchemaCache="true" to Java launch args, or by calling System.setProperty("kiorm.disableDocumentSchemaCache", "true") in runtime.

You also can clear all cache by calling KiORM.clearSchemaCache(), or even access whole cache list by calling DocumentParser.getCache()

Usage with Lombok

KiORM was designed to be used with Lombok. You don't have to use it, but it greatly simplifies the writing of models, and allows you to get such beautiful schemas:

@NoArgsConstructor // Creating no-args constructor for KiORM
@AllArgsConstructor // Creating constructor with all fields
@Document("items")
class Item {
    @Getter @PrimaryKey @AutoIncrement @Field("id") private long id; // ID without setter

    @Getter @Setter @Field("durability") private float durability;

    @Getter @Setter @Field("count") private int count;

    @Getter @Setter @Field("rarity") private int rarity;

    @Getter @Setter @Field("name") private String name;

    @Serializer(PlayerIdSerializer.class)
    @Getter @Setter @Field("owner") private Player owner;
}

Epilogue

I'm developing this project during using it in other personal/work projects, so it will be maintained, and I hope, will grow up into full alternative of big and complicated ORMs for tiny/average projects.

I would be very pleased if you supported my work financially. You can do it on the LiberaPay or using Monero cryptocurrency

LibraPay

XMR: 42d4gWUUk9c79Vkg9CnU7z4VJci7UebTRf3B3FMNnPADFbKUqFZYMJ1S5xeK9vSV2b7gmsmZisS2XJQpW3pVwifrKc9PSN5

You might also like...

jdbi is designed to provide convenient tabular data access in Java; including templated SQL, parameterized and strongly typed queries, and Streams integration

The Jdbi library provides convenient, idiomatic access to relational databases in Java. Jdbi is built on top of JDBC. If your database has a JDBC driv

Dec 27, 2022

Java code generator for calling PL/SQL.

OBridge OBridge provides a simple Java source code generator for calling Oracle PL/SQL package procedures. Supported input, output parameters and retu

Oct 7, 2022

Persistent priority queue over sql

queue-over-sql This projects implement a persistent priority queue (or a worker queue) (like SQS, RabbitMQ and others) over sql. Why? There are some c

Aug 15, 2022

SQL tasarım komutları ve Backend yazıldı. Projeye yıldız Vermeyi Unutmayın 🚀 Teşekkürler! ❤️

HumanResourcesManagementSystem-HRMS SQL tasarım komutları ve Backend yazıldı. Projeye yıldız Vermeyi Unutmayın 🚀 Teşekkürler! ❤️ insan kaynakları yön

Nov 6, 2022

The public release repository for SUSTech SQL (CS307) course project 2.

CS307 Spring 2021 Database Project 2 1. Source code Download link: For java: https://github.com/NewbieOrange/SUSTech-SQL-Project2-Public For python: h

Dec 26, 2022

SQL made uagliò.

SQL made uagliò.

GomorraSQL is an easy and straightforward interpreted SQL dialect that allows you to write simpler and more understandable queries in Neapolitan Langu

Dec 22, 2022

Multi-DBMS SQL Benchmarking Framework via JDBC

BenchBase BenchBase (formerly OLTPBench) is a Multi-DBMS SQL Benchmarking Framework via JDBC. Table of Contents Quickstart Description Usage Guide Con

Dec 29, 2022

HasorDB is a Full-featured database access tool, Providing object mapping,Richer type handling than Mybatis, Dynamic SQL

HasorDB is a Full-featured database access tool, Providing object mapping,Richer type handling than Mybatis, Dynamic SQL

HasorDB is a Full-featured database access tool, Providing object mapping,Richer type handling than Mybatis, Dynamic SQL, stored procedures, more dialect 20+, nested transactions, multiple data sources, conditional constructors, INSERT strategies, multiple statements/multiple results. And compatible with Spring and MyBatis usage.

Oct 27, 2022
Owner
Rikonardo
Rikonardo
🚀flink-sql-submit is a custom SQL submission client

??flink-sql-submit is a custom SQL submission client This is a customizable extension of the client, unlike flink's official default client.

ccinn 3 Mar 28, 2022
CrateDB is a distributed SQL database that makes it simple to store and analyze massive amounts of machine data in real-time.

About CrateDB is a distributed SQL database that makes it simple to store and analyze massive amounts of machine data in real-time. CrateDB offers the

Crate.io 3.6k Jan 2, 2023
A Java library to query pictures with SQL-like language

PicSQL A Java library to query pictures with SQL-like language. Features : Select and manipulate pixels of pictures in your disk with SQL-like dialect

Olivier Cavadenti 16 Dec 25, 2022
A Java library to query pictures with SQL-like language.

PicSQL A Java library to query pictures with SQL-like language. Features : Select and manipulate pixels of pictures in your disk with SQL-like dialect

null 16 Dec 25, 2022
esProc SPL is a scripting language for data processing, with well-designed rich library functions and powerful syntax, which can be executed in a Java program through JDBC interface and computing independently.

esProc esProc is the unique name for esProc SPL package. esProc SPL is an open-source programming language for data processing, which can perform comp

null 990 Dec 27, 2022
Official repository of Trino, the distributed SQL query engine for big data, formerly known as PrestoSQL (https://trino.io)

Trino is a fast distributed SQL query engine for big data analytics. See the User Manual for deployment instructions and end user documentation. Devel

Trino 6.9k Dec 31, 2022
requery - modern SQL based query & persistence for Java / Kotlin / Android

A light but powerful object mapping and SQL generator for Java/Kotlin/Android with RxJava and Java 8 support. Easily map to or create databases, perfo

requery 3.1k Jan 5, 2023
The official home of the Presto distributed SQL query engine for big data

Presto Presto is a distributed SQL query engine for big data. See the User Manual for deployment instructions and end user documentation. Requirements

Presto 14.3k Dec 30, 2022
jOOQ is the best way to write SQL in Java

jOOQ's reason for being - compared to JPA Java and SQL have come a long way. SQL is an "ancient", yet established and well-understood technology. Java

jOOQ Object Oriented Querying 5.3k Jan 4, 2023