A lightweight, annotation-based ORM framework for Java with zero dependencies (excluding JDBC drivers).
Overview • Key Features • Quick Start • Supported Annotations • Advanced Features • Supported Databases • Examples • Performance • Roadmap • Contributing • License • Contact • Acknowledgments
Skinned Rat ORM is a lightweight, annotation-based ORM framework for Java with zero external dependencies (excluding JDBC drivers).
I developed this package because my instructor explicitly told me not to use Hibernate or any external ORM libraries and instead work directly with JDBC even if it meant implementing the ORM manually :). This project demonstrates how to build a simple, type-safe, and flexible ORM from scratch while still supporting essential features like CRUD operations, entity relationships, and schema generation.
Note
Some parts of this project developed with the assistance of AI tools: GitHub Copilot CLI + Claude sonnet 4.5 While AI helped accelerate development, a big part of the core ORM logic, design decisions, and implementation were authored and reviewed manually (+youtube & stackoverflow & .. :)) to ensure correctness and learning outcomes.
- Simple & Lightweight - No complex configuration, just annotations
- Fast - Direct JDBC with minimal overhead
- Flexible - Works with any JDBC-compatible database
- Zero Dependencies - Only requires a JDBC driver
- Type-Safe - Full compile-time type checking
- Production-Ready - Tested with MariaDB, MySQL, PostgreSQL
Option 1: JitPack (Easiest)
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
implementation 'com.github.zouari-oss:skinned-rat-orm:v1.0.0'
// Add your JDBC driver
implementation 'org.mariadb.jdbc:mariadb-java-client:3.3.2'
}Option 2: Maven Local (for testing)
git clone https://github.com/zouari-oss/skinned-rat-orm.git
cd skinned-rat-orm/project
./gradlew publishToMavenLocalrepositories {
mavenLocal()
}
dependencies {
implementation 'org.zouarioss:skinned-rat-orm:1.0.0'
}import org.zouarioss.skinnedratorm.annotations.*;
@Entity
@Table(name = "users")
public class User extends TimestampedEntity {
@Column(nullable = false, unique = true, length = 150)
private String email;
@Column(nullable = false)
private String passwordHash;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private UserRole role;
@PrePersist
protected void onCreate() {
if (this.role == null) {
this.role = UserRole.USER;
}
}
// Getters and setters...
}import org.zouarioss.skinnedratorm.core.EntityManager;
import org.zouarioss.skinnedratorm.engine.*;
import java.sql.*;
// Setup
Connection connection = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/mydb",
"user",
"password"
);
EntityManager em = new EntityManager(connection);
// Create table (first time only)
SchemaGenerator generator = new SchemaGenerator(connection, SQLDialect.MYSQL);
generator.createTable(User.class);
// CREATE
User user = new User();
user.setEmail("john@example.com");
user.setPasswordHash("hashed_password");
user.setRole(UserRole.ADMIN);
em.persist(user);
// READ
User found = em.findById(User.class, user.getId());
// UPDATE
found.setRole(UserRole.MODERATOR);
em.update(found);
// DELETE
em.delete(found);
// QUERY
List<User> admins = em.createQuery(User.class)
.where("role", "ADMIN")
.orderBy("email")
.limit(10)
.getResultList();@Entity- Mark class as an entity@Table(name = "...")- Specify table name@MappedSuperclass- Inherit fields without creating table
@Id- Primary key field@Column(name, nullable, unique, length, updatable)- Column configuration@GeneratedValue(strategy = UUID)- Auto-generate IDs@Enumerated(STRING|ORDINAL)- Enum mapping strategy
@CreationTimestamp- Auto-set on creation@UpdateTimestamp- Auto-update on modification
@OneToOne- One-to-one relationship@ManyToOne- Many-to-one relationship@JoinColumn- Foreign key configuration
@PrePersist- Called before entity is persisted
@Index(name, columnList)- Create indexes@UniqueConstraint(name, columnNames)- Unique constraints
List<User> users = em.createQuery(User.class)
.where("role", "=", UserRole.ADMIN)
.where("email", "LIKE", "%@example.com")
.orderBy("createdAt", "DESC")
.limit(20)
.offset(10)
.getResultList();
long count = em.createQuery(User.class)
.where("role", "ADMIN")
.count();SchemaGenerator generator = new SchemaGenerator(connection, SQLDialect.MYSQL);
// Create tables
generator.createTable(User.class);
generator.createTable(Profile.class);
// Drop tables
generator.dropTable(User.class);@MappedSuperclass
public abstract class IdentifiableEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
protected UUID id;
}
@MappedSuperclass
public abstract class TimestampedEntity extends IdentifiableEntity {
@CreationTimestamp
@Column(nullable = false, updatable = false)
protected Instant createdAt;
@UpdateTimestamp
@Column(nullable = false)
protected Instant updatedAt;
}
@Entity
@Table(name = "users")
public class User extends TimestampedEntity {
// Automatically inherits id, createdAt, updatedAt
}- MariaDB / MySQL
- PostgreSQL
- H2
- SQLite
- Any JDBC-compatible database
See the /lib/src/test/java/org/zouarioss/skinnedratorm/ directory for complete examples:
- User Entity - Basic CRUD operations
- Profile Entity - One-to-one relationships
- AuditLog Entity - Timestamps and pre-persist callbacks
- Integration Tests - Full CRUD test suite with MariaDB
Skinned Rat ORM is designed for simplicity and performance:
- Direct JDBC - No reflection overhead at runtime
- Prepared Statements - SQL injection protection
- Lazy Initialization - Metadata cached after first use
- No Proxy Objects - Work with real POJOs
- Lazy loading for relationships
- Batch operations
- Criteria API
- Connection pooling integration
- Query caching
- Multi-database support in one application
Contributions are welcome! Please feel free to submit a Pull Request.
This repository is licensed under the GPL-3.0 License. You are free to use, modify, and distribute the content. See the LICENSE file for details.
For questions or suggestions, feel free to reach out:
- GitHub: ZouariOmar
- Email: zouariomar20@gmail.com
- LinkedIn: Zouari Omar
Built with ❤️ for the Java community.
After my haircut, I feel like a skinned rat... ⭐ Star this repo if you find it useful! and save me from this look!