SlimORM

A lightweight Java ORM library

License

License

Categories

Categories

ORM Data
GroupId

GroupId

eu.miltema
ArtifactId

ArtifactId

slimorm
Last Version

Last Version

1.3.2
Release Date

Release Date

Type

Type

jar
Description

Description

SlimORM
A lightweight Java ORM library
Project URL

Project URL

https://github.com/tehnomaan/slimorm
Source Code Management

Source Code Management

https://github.com/tehnomaan/slimorm

Download slimorm

How to add to project

<!-- https://jarcasting.com/artifacts/eu.miltema/slimorm/ -->
<dependency>
    <groupId>eu.miltema</groupId>
    <artifactId>slimorm</artifactId>
    <version>1.3.2</version>
</dependency>
// https://jarcasting.com/artifacts/eu.miltema/slimorm/
implementation 'eu.miltema:slimorm:1.3.2'
// https://jarcasting.com/artifacts/eu.miltema/slimorm/
implementation ("eu.miltema:slimorm:1.3.2")
'eu.miltema:slimorm:jar:1.3.2'
<dependency org="eu.miltema" name="slimorm" rev="1.3.2">
  <artifact name="slimorm" type="jar" />
</dependency>
@Grapes(
@Grab(group='eu.miltema', module='slimorm', version='1.3.2')
)
libraryDependencies += "eu.miltema" % "slimorm" % "1.3.2"
[eu.miltema/slimorm "1.3.2"]

Dependencies

compile (1)

Group / Artifact Type Version
javax.persistence : persistence-api jar 1.0.2

runtime (1)

Group / Artifact Type Version
com.google.code.gson : gson jar 2.8.5

test (2)

Group / Artifact Type Version
junit : junit jar 4.12
org.postgresql : postgresql jar 42.2.5

Project Modules

There are no modules declared in this project.

SlimORM

SlimORM is a lightweight Java ORM library, based on JDBC.

Why yet another ORM library, when we have popular ORM libraries already? Because sometimes, You just don't want the complexity of those popular libraries. Instead, You need basic CRUD operations, which would be simple to set up, simple to use and have minimal dependencies. For large and complex enterprise projects, SlimORM probably lacks features and flexibility.

Basic Usage

Establish database link:

Database db = new Database("org.postgresql.Driver", "jdbc:postgresql://localhost:5432/demoDB", "demouser", "password");

Insert a new Employee-record:

Employee employee = new Employee();
employee.name = "John Smith";
employee.dateOfBirth = LocalDate.of(1992, 3, 27);
db.insert(employee);

Update an existing record:

employee.dateOfBirth = LocalDate.of(1993, 5, 14);
db.update(employee);

Delete the record (based on id):

db.delete(Employee.class, employee.id);

Get a specific record from database (based on id):

Employee employee = db.getById(Employee.class, id);

Get a list of records with SQL filter:

Collection<Employee> employees = db.where("name LIKE ?", "A%").list(Employee.class);

Dependencies

Add SlimORM dependency into build.gradle:

dependencies {
    implementation 'eu.miltema:slimorm:1.3.2'
}

or alternatively, if using Maven, then into pom.xml:

<dependencies>
  <dependency>
    <groupId>eu.miltema</groupId>
    <artifactId>slimorm</artifactId>
    <version>1.0.0</version>
  </dependency>
</dependencies>

In addition, build.gradle or pom.xml must refer to database driver. For example, when using PostgreSQL, build.gradle contains:

dependencies {
    implementation 'eu.miltema:slimorm:1.2.1'
    runtime 'org.postgresql:postgresql:42.2.5'
}

SlimORM itself depends on 2 libraries: javax.persistence and com.google.code.gson:gson. These are resolved by build system automatically.

Annotations

SlimORM supports these javax.persistence annotations when declaring entities:

  • @Table (name) - without this annotation, SlimORM uses snake-case class name as table name. For example, class EmployeeDetails would be stored into table employee_details
  • @Column (name, updatable, insertable) - without this annotation, SlimORM uses snake-case field name as column name. For example, field dateOfBirth would be stored into column date_of_birth
  • @Transient - annotation @Transient and Java modifier transient have the same effect: SlimORM will not read/write this field to the database
  • @Id - declares a primary key field. Only single-field primary keys are supported - composite primary keys are not
  • @GeneratedValue - database generates the value for this field. INSERT & UPDATE will not modify this field
  • ManyToOne - defines a many-to-one relationship. In database, this field must be a foreign key field to target entity table. When writing, only the foreign key is persisted (the referenced entity has to be persisted expicitly beforehand). When reading, only referenced entity id is filled.
  • @JSon - declares that this field will be stored as a JSon object. This is not a javax.persistence annotation, but SlimORM annotation

For example:

@Table(name="employees")
public class Employee {
	@Id
	@GeneratedValue
	int id;

	String name;

	@Column(name = "dob")
	LocalDate dateOfBirth;

	@Transient
	boolean isDirty;

	transient boolean isDirty2;

	@JSon
	Contract[] contracts;

	@ManyToOne
	Department department;
}

NB! Since most database designs have an auto-generated primary key with name id, then SlimORM has a special shorthand: a field with name id and without @Id is still treated as if both annotations were present. If this is not what You need, declare Your primary key with a different name or add @Id annotation to a different field.

@ManyToOne and Foreign Keys

In the Employee case above, table employees must contain column department, which is a foreign key field, referencing table department.

  • When writing Employee-entity to database, only the foreign key is persisted (the referenced entity itself has to be persisted expicitly beforehand)
  • When reading Employee-entity from database, only referenced entity id is filled inside Department-object (for performance reasons)
  • To load Employee entities with fully initialized Department-entites, use method referencedColumns
		db.where("id=?", id).referencedColumns("*").list(Employee.class);//load all columns for all referenced entities
		db.where("id=?", id).
			referencedColumns(Department.class, "*").//load all columns for Department references
			referencedColumns("id, name").//load id, name columns for all other references
			list(Employee.class);

Transactions

In the "Basic Usage" chapter, each example statement was executed as individual transaction. Here is an example with a transaction including multiple statements. When all statements succeed, SlimORM automatically commits the transaction. When any of the statements throws an Exception, SlimORM automatically rolls back the entire transaction.

Employee[] entities = db.transaction((db, connection) -> {

	Employee e1 = new Employee();
	e1.name = "John Smith";
	db.insert(e1);

	Employee e2 = new Employee();
	e2.name = "Jane Doe";
	db.insert(e2);
	return new Employee[] {e1, e2};
});

SlimORM manages the connection itself - no need to close, commit or rollback the connection. It is possible to return an object of any type from the transaction.

SQL Dialects

By default, SlimORM uses PostgreSQL dialect. If that dialect is causing problems, You must implement a custom dialect and a superclass of Database:

public class MySqlDialect extends PgDialect {
	... // override any methods that cause problems
}
public class MySqlDatabase extends Database {
	@Override
	public Dialect getDialect() {
		return MySqlDialect();
	}
}

And when establishing database link, don't forget to use Your custom database class instead of Database:

Database db = new MySqlDatabase(dbDriver, dbUrl, dbUser, dbPassword);

Data Types

The developer is responsible of selecting Java type and corresponding SQL data byte, which must match. By default, SlimORM supports these Java types by default: String, byte, Byte, short, Short, int, Integer, long, Long, float, Float, double, Double, BigDecimal, byte[], Timestamp, Instant, Date, LocalDate, LocalDateTime, ZonedDateTime.

Be aware that PostgreSQL does not store timezone id into record (even when data type is with time zone). Therefore, all time-related columns store correct instant in time, but have lost the original timezone id.

For data types not listed above, one must superclass PgDialect and provide custom saveBinder and loadBinder.

Logging

To keep the amount of dependencies low, SlimORM is not logging automatically. To add logging to SlimORM (System.out, log4j, slf etc), do this:

Database db = new Database(...).setLogger(message -> System.out.println(message));

Authorization and Record-Level Restrictions

To inject restrictions into all database-related queries (for example restrict to a specific account only), extend and use a custom database class (for example SecureDatabase) like this:

public class SecureDatabase extends Database {

	private int accountId;

	public SecureDatabase(String jndiName, int accountId) {
		super(jndiName);
		this.accountId = accountId;
	}

	// inject additional account-id filter into every query
	@Override
	protected String injectIntoWhereExpression(Class<?> entityClass, String whereExpression) {
		return (whereExpression == null ? "account_id=?" : "(" + whereExpression + ") AND account_id=?");
	}

	// inject account-id filtering parameter into every query
	@Override
	protected Object[] injectWhereParameters(Class<?> entityClass, Object[] whereParameters) {
		if (whereParameters != null) {
			whereParameters = Arrays.copyOf(whereParameters, whereParameters.length + 1);
			whereParameters[whereParameters.length - 1] = accountId;
			return whereParameters;
		}
		else return new Object[] {accountId};
	}

	@Override
	protected void authorize(Object entity) throws UnauthorizedException {
		if (!/* entity validation logic */)
			throw new UnauthorizedException();
	}
}

Database db = new SecureDatabase("java:comp/env/jdbc/demodb", accountId);

History

I first started with a custom ORM library probably around 2003, mostly for PostgreSQL. Then at some point I found https://github.com/dieselpoint/norm, which had pretty similar logic and API to my own library. Norm was written for MySQL and worked rather well with PostgreSQL, although multiple issues emerged. Some of those issues I was able to handle and I used Norm in many projects. But a number of issues still remained unsolved. Finally I decided, that it is easier to continue with my own library - SlimORM, which now has some Norm influences.

Versions

Version
1.3.2
1.3.1
1.3.0
1.2.1
1.2.0
1.1.1
1.1.0
1.0.1
1.0.0
0.0.32
0.0.31
0.0.30
0.0.28
0.0.27
0.0.22