Hibernate & JPA Study Material
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 objects | Relational tables | Problem |
|---|---|---|
Identity with == / equals | Identity by primary key | Two objects may represent the same row |
References (order.getUser()) | Foreign keys and JOINs | Navigating objects must become SQL joins |
Inheritance (class Book extends Product) | No inheritance | Needs a mapping strategy |
Collections (List<OrderItem>) | Rows in another table | Loading 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.