What is ORM

Objects vs Tables

Java programs work with objects: a Product with a name, a price and a stock count. Relational databases (MySQL, PostgreSQL, Oracle) store rows in tables. Moving data between the two worlds by hand means writing SQL, reading columns one by one and copying them into fields — for every class.

ORM (Object-Relational Mapping) is a technique where a library does this copying for you. You describe once how a class maps to a table, and the ORM generates the SQL, runs it and builds objects from the results.

Java world                          Database world
 ----------                          --------------
 class Product          <--ORM-->    table product
 object (one product)   <--ORM-->    row
 field  price           <--ORM-->    column price
 reference to Category  <--ORM-->    foreign key category_id

The Object-Relational Mismatch

Objects and tables do not fit perfectly. An ORM exists to hide these differences:

Java objectsRelational tablesProblem
Identity with == / equalsIdentity by primary keyTwo objects may represent the same row
References (order.getUser())Foreign keys and JOINsNavigating objects must become SQL joins
Inheritance (class Book extends Product)No inheritanceNeeds a mapping strategy
Collections (List<OrderItem>)Rows in another tableLoading and saving the whole list
Java types (BigDecimal, LocalDate, enums)SQL types (DECIMAL, DATE, VARCHAR)Type conversion

A Tiny ORM in Plain Java

Example

Hibernate reads annotations on your classes with reflection. This toy version does the same: it builds an INSERT from an object and builds an object from a database row (a Map here).

import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        Product p = new Product(1L, "Boat Rockerz 450", new BigDecimal("1499.00"), 120);
        System.out.println(MiniOrm.insertSql(p));

        Map<String, Object> row = new LinkedHashMap<>();   // pretend this came from a SELECT
        row.put("id", 2L);
        row.put("product_name", "Noise ColorFit Pro");
        row.put("price", new BigDecimal("2299.00"));
        row.put("stock", 40);
        System.out.println(MiniOrm.fromRow(Product.class, row));
    }
}

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface Table { String value(); }
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @interface Column { String value(); }

@Table("product")
class Product {
    @Column("id") Long id;
    @Column("product_name") String name;
    @Column("price") BigDecimal price;
    @Column("stock") Integer stock;

    Product() {}
    Product(Long id, String name, BigDecimal price, Integer stock) {
        this.id = id; this.name = name; this.price = price; this.stock = stock;
    }
    public String toString() {
        return "Product{id=" + id + ", name=" + name + ", price=" + price + ", stock=" + stock + "}";
    }
}

class MiniOrm {
    static String insertSql(Object entity) throws IllegalAccessException {
        Class<?> type = entity.getClass();
        List<String> cols = new ArrayList<>(), vals = new ArrayList<>();
        for (Field f : type.getDeclaredFields()) {
            Column c = f.getAnnotation(Column.class);
            if (c == null) continue;
            cols.add(c.value());
            Object v = f.get(entity);
            vals.add(v instanceof String ? "'" + v + "'" : String.valueOf(v));
        }
        return "insert into " + type.getAnnotation(Table.class).value()
            + " (" + String.join(", ", cols) + ") values (" + String.join(", ", vals) + ")";
    }

    static <T> T fromRow(Class<T> type, Map<String, Object> row) throws Exception {
        T obj = type.getDeclaredConstructor().newInstance();
        for (Field f : type.getDeclaredFields()) {
            Column c = f.getAnnotation(Column.class);
            if (c != null) f.set(obj, row.get(c.value()));
        }
        return obj;
    }
}

Output

insert into product (id, product_name, price, stock) values (1, 'Boat Rockerz 450', 1499.00, 120)
Product{id=2, name=Noise ColorFit Pro, price=2299.00, stock=40}

Note: Real Hibernate uses PreparedStatement parameters (?) instead of pasting values into SQL, caches the mapping metadata, and handles relationships, types and transactions. The idea is the same.