Add hibernate-jpa module: 40 runnable Hibernate 7 / JPA interview answers on PostgreSQL 16

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cfo2K4u4rF18QsPXPFnytf
This commit is contained in:
2026-10-24 17:33:29 +00:00
co-authored by Claude Sonnet 5.5
parent a5fbf45971
commit d03962ea9c
87 changed files with 2387 additions and 1 deletions
@@ -0,0 +1,4 @@
Session is an EntityManager : true
SessionFactory is an EMF : true
em.unwrap(Session.class) == em : true
Hibernate-only API reachable : s.getStatistics().isStatisticsEnabled() = true
@@ -0,0 +1,5 @@
new Employee() : managed=false id=null
after persist() : managed=true id assigned=true
after detach() : managed=false
after merge() : original managed=false, returned copy managed=true, same object=false
after remove() : managed=false
@@ -0,0 +1,3 @@
persist(detached) -> jakarta.persistence.EntityExistsException: Detached entity passed to persist: lab.hibernate.model.Employee
merge(detached) -> returns a different instance: true
salary changed on the original after merge is stored: false (db has 100)
@@ -0,0 +1,2 @@
methods present on org.hibernate.Session : [find, get, getReference, load, merge, persist, refresh, remove]
methods NOT present : [delete, save, saveOrUpdate, update]
@@ -0,0 +1,7 @@
getReference(): SQL statements so far = 0, initialized = false
ref.name (field) -> null; SQL statements now = 0, initialized = false
ref.getName() -> Chen; SQL statements now = 1, initialized = true
SQL> select e1_0.id,e1_0.city,e1_0.zip,e1_0.department_id,e1_0.level,e1_0.name,e1_0.salary from Employee e1_0 where e1_0.id=?
find(missing id) -> null
getReference(missing) -> proxy returned without error: true
first access -> org.hibernate.ObjectNotFoundException: No row with the given identifier exists for entity [lab.hibernate.model.Employee with id '99999']
@@ -0,0 +1,8 @@
change a managed entity, no save call:
SQL> select e1_0.id,e1_0.city,e1_0.zip,e1_0.department_id,e1_0.level,e1_0.name,e1_0.salary from Employee e1_0 where e1_0.id=?
SQL> update Employee set city=?,zip=?,department_id=?,level=?,name=?,salary=? where id=?
count visible inside the transaction (AUTO flush before the query): 2
persist then query:
SQL> select nextval('Employee_SEQ')
SQL> insert into Employee (city,zip,department_id,level,name,salary,id) values (?,?,?,?,?,?,?)
SQL> select count(e1_0.id) from Employee e1_0
@@ -0,0 +1,2 @@
refresh(): pending in-memory change thrown away, salary = 100
merge(detached with salary 500) -> database now has 500
@@ -0,0 +1,5 @@
two find() calls, same session: SQL selects = 1, same instance = true
after clear(): SQL selects = 2, same instance as before = false
SQL> select e1_0.id,e1_0.city,e1_0.zip,e1_0.department_id,e1_0.level,e1_0.name,e1_0.salary from Employee e1_0 where e1_0.id=?
SQL> select e1_0.id,e1_0.city,e1_0.zip,e1_0.department_id,e1_0.level,e1_0.name,e1_0.salary from Employee e1_0 where e1_0.id=?
a different session: SQL selects = 1 (the first-level cache is per session)
@@ -0,0 +1,4 @@
SessionFactory started with an entity that has no no-arg constructor: true
persist(): ok
find() -> org.hibernate.InstantiationException: No default constructor for entity 'lab.hibernate.model.BadEntity'
root cause -> org.hibernate.InstantiationException: No default constructor for entity 'lab.hibernate.model.BadEntity'
@@ -0,0 +1,2 @@
SEQUENCE: 25 entities -> 1 prepared insert statement(s) with jdbc.batch_size=10, 2 sequence selects
IDENTITY: 25 entities -> 25 prepared insert statements (one per row)
@@ -0,0 +1 @@
columns of table employee: [city, department_id, id, level, name, salary, zip]
@@ -0,0 +1,2 @@
@Enumerated(STRING) stores LEAD as: LEAD
@Enumerated (default) stores LEAD as: 2 (its position in the enum: [JUNIOR, SENIOR, LEAD])
@@ -0,0 +1 @@
tables: [comment, post, post_tags]
@@ -0,0 +1,5 @@
tables: [bankpayment, cardpayment, payment, shape]
JOINED 'from Payment' returned 2 rows
SINGLE_TABLE 'from Shape' returned 2 rows
SQL> select p1_0.id,case when p1_2.id is not null then 1 when p1_1.id is not null then 2 end,p1_0.amount,p1_1.iban,p1_2.last4 from Payment p1_0 left join BankPayment p1_1 on p1_0.id=p1_1.id left join CardPayment p1_2 on p1_0.id=p1_2.id
SQL> select s1_0.id,s1_0.kind,s1_0.label,s1_0.radius,s1_0.side from Shape s1_0
@@ -0,0 +1,3 @@
tables: [note]
select from the entity Note: 1 row(s), created_by column inherited = system
select from the @MappedSuperclass -> org.hibernate.query.sqm.UnknownEntityException: Could not resolve root entity 'Auditable'
@@ -0,0 +1,2 @@
before persist: id=null, set.contains(item)=true
after persist : id assigned=true, hashCode changed, set.contains(item)=false
@@ -0,0 +1,7 @@
default of fetch() on each JPA annotation, read from the API jar:
@Basic -> EAGER
@ManyToOne -> EAGER
@OneToOne -> EAGER
@OneToMany -> LAZY
@ManyToMany -> LAZY
@ElementCollection -> LAZY
+2
View File
@@ -0,0 +1,2 @@
employee.department_id after d.employees.add(e) only: null
after setting employee.department too : 1
+7
View File
@@ -0,0 +1,7 @@
naive loop over 3 departments: 4 SQL statements (1 + 3)
SQL> select d1_0.id,d1_0.name from Department d1_0
SQL> select e1_0.department_id,e1_0.id,e1_0.city,e1_0.zip,e1_0.level,e1_0.name,e1_0.salary from Employee e1_0 where e1_0.department_id=?
SQL> select e1_0.department_id,e1_0.id,e1_0.city,e1_0.zip,e1_0.level,e1_0.name,e1_0.salary from Employee e1_0 where e1_0.department_id=?
SQL> select e1_0.department_id,e1_0.id,e1_0.city,e1_0.zip,e1_0.level,e1_0.name,e1_0.salary from Employee e1_0 where e1_0.department_id=?
with join fetch: 1 SQL statement
SQL> select distinct d1_0.id,e1_0.department_id,e1_0.id,e1_0.city,e1_0.zip,e1_0.level,e1_0.name,e1_0.salary,d1_0.name from Department d1_0 join Employee e1_0 on d1_0.id=e1_0.department_id
@@ -0,0 +1,4 @@
join fetch : department initialized = true
SQL> select e1_0.id,e1_0.city,e1_0.zip,d1_0.id,d1_0.name,e1_0.level,e1_0.name,e1_0.salary from Employee e1_0 join Department d1_0 on d1_0.id=e1_0.department_id
entity graph : department initialized = true
SQL> select e1_0.id,e1_0.city,e1_0.zip,d1_0.id,d1_0.name,e1_0.level,e1_0.name,e1_0.salary from Employee e1_0 left join Department d1_0 on d1_0.id=e1_0.department_id
@@ -0,0 +1,4 @@
default_batch_fetch_size=2, 3 departments: 3 SQL statements (1 + ceil(3/2))
SQL> select d1_0.id,d1_0.name from Department d1_0
SQL> select e1_0.department_id,e1_0.id,e1_0.city,e1_0.zip,e1_0.level,e1_0.name,e1_0.salary from Employee e1_0 where e1_0.department_id = any (?)
SQL> select e1_0.department_id,e1_0.id,e1_0.city,e1_0.zip,e1_0.level,e1_0.name,e1_0.salary from Employee e1_0 where e1_0.department_id=?
@@ -0,0 +1,3 @@
read a lazy collection after the session closed -> org.hibernate.LazyInitializationException: Cannot lazily initialize collection of role 'lab.hibernate.model.Department.employees' with key '1' (no session)
cure 1: fetch it inside the query -> 3 employees, 1 SQL statement
cure 2: ask for exactly the columns you need -> [E11, E12, E13], 1 SQL statement
@@ -0,0 +1,2 @@
two List collections in one join fetch -> java.lang.IllegalArgumentException: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags: [lab.hibernate.model.Post.comments, lab.hibernate.model.Post.tags]
fix: one collection per query -> comments=2 tags=2, 2 SQL statements
@@ -0,0 +1,6 @@
join fetch of a collection + setMaxResults(2) -> 2 departments
SQL contains a row limit: true
SQL> select d1_0.id,e1_0.department_id,e1_0.id,e1_0.city,e1_0.zip,e1_0.level,e1_0.name,e1_0.salary,d1_0.name from (select d1_0.id,d1_0.name from Department d1_0 where exists(select 1 from Employee e1_0 where d1_0.id=e1_0.department_id) order by d1_0.id offset ? rows fetch first ? rows only) d1_0(id,name) join Employee e1_0 on d1_0.id=e1_0.department_id order by d1_0.id
two-step fix -> 2 departments
SQL> select d1_0.id from Department d1_0 order by d1_0.id fetch first ? rows only
SQL> select distinct d1_0.id,e1_0.department_id,e1_0.id,e1_0.city,e1_0.zip,e1_0.level,e1_0.name,e1_0.salary,d1_0.name from Department d1_0 join Employee e1_0 on d1_0.id=e1_0.department_id where d1_0.id in (?,?) order by d1_0.id
@@ -0,0 +1,2 @@
persist Employee that points at an unsaved Department -> java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Persistent instance of 'lab.hibernate.model.Employee' references an unsaved transient instance of 'lab.hibernate.model.Department' (persist the transient instance before flushing) [lab.hibernate.model.Employee.department -> lab.hibernate.model.Department]
Post -> comments has CascadeType.ALL: comments stored = 1
@@ -0,0 +1,9 @@
removed a line from the collection only:
SQL> select po1_0.id,po1_0.customer from PurchaseOrder po1_0 where po1_0.id=?
SQL> select l1_0.order_id,l1_0.id,l1_0.sku from OrderLine l1_0 where l1_0.order_id=?
SQL> delete from OrderLine where id=?
removed the order (cascade ALL):
SQL> select po1_0.id,po1_0.customer from PurchaseOrder po1_0 where po1_0.id=?
SQL> select l1_0.order_id,l1_0.id,l1_0.sku from OrderLine l1_0 where l1_0.order_id=?
SQL> delete from OrderLine where id=?
SQL> delete from PurchaseOrder where id=?
@@ -0,0 +1,6 @@
'from EagerEmployee' with the default @ManyToOne: 4 SQL statements for 9 employees in 3 departments
SQL> select ee1_0.id,ee1_0.department_id,ee1_0.name from EagerEmployee ee1_0
SQL> select d1_0.id,d1_0.name from Department d1_0 where d1_0.id=?
SQL> select d1_0.id,d1_0.name from Department d1_0 where d1_0.id=?
SQL> select d1_0.id,d1_0.name from Department d1_0 where d1_0.id=?
'from Employee' with @ManyToOne(fetch = LAZY): 1 SQL statement
@@ -0,0 +1,6 @@
right after persist() the row is already in L2 (READ_WRITE caches on insert): SQL selects = 0, hits = 1
after evictAllRegions(), three find() calls, three different sessions:
SQL selects : 1
L2 cache hits : 2
L2 cache misses : 1
L2 cache puts : 1
+5
View File
@@ -0,0 +1,5 @@
run 1: query cache hits=0 misses=1 puts=1
run 2: query cache hits=1 misses=1 puts=1
SQL selects for Product after 2 runs: 1
run 3 after inserting a Product: query cache hits=1 misses=2
SQL selects for Product after 3 runs: 3
@@ -0,0 +1,3 @@
after an HQL bulk update to 11 : find() sees 11 (L2 hits=0 misses=1)
after a native bulk update to 12 : find() sees 12 (L2 hits=0 misses=1)
after raw JDBC update to 13 : find() sees 12 (L2 hits=1 misses=0) <- stale: Hibernate was not told
@@ -0,0 +1,6 @@
both sessions read balance=100 version=0
session A commits +50 -> version is now 1
session B commits +25 -> jakarta.persistence.OptimisticLockException: Unexpected row count (expected row count 1 but was 0) [update Account set balance=?,owner=?,version=? where id=? and version=?] for entity [lab.hibernate.model.Account with id '1']
cause: org.hibernate.StaleObjectStateException: Unexpected row count (expected row count 1 but was 0) [update Account set balance=?,owner=?,version=? where id=? and version=?] for entity [lab.hibernate.model.Account with id '1']
SQL> update Account set balance=?,owner=?,version=? where id=? and version=?
final balance: 150 (B's stale write was rejected, A's +50 kept)
@@ -0,0 +1,3 @@
session 1: find(..., PESSIMISTIC_WRITE) ran: select a1_0.id,a1_0.balance,a1_0.owner,a1_0.version from Account a1_0 where a1_0.id=? for no key update of a1_0
session 2 with lock.timeout=0 -> jakarta.persistence.PessimisticLockException: JDBC exception executing SQL [ERROR: could not obtain lock on row in relation "account"] [select a1_0.id,a1_0.balance,a1_0.owner,a1_0.version from Account a1_0 where a1_0.id=? for no key update of a1_0 nowait]
cause: org.hibernate.exception.LockTimeoutException: JDBC exception executing SQL [ERROR: could not obtain lock on row in relation "account"] [select a1_0.id,a1_0.balance,a1_0.owner,a1_0.version from Account a1_0 where a1_0.id=? for no key update of a1_0 nowait]
+2
View File
@@ -0,0 +1,2 @@
SQL> update PlainAccount set balance=?,owner=? where id=?
two deposits (+50, +25) on 100 -> final balance 125, expected 175: A's deposit silently lost
@@ -0,0 +1,3 @@
PostgreSQL default isolation level: read committed
read committed : first read 100, another transaction commits 150, second read 150 -> non-repeatable read
repeatable read : first read 100, another transaction commits 150, second read 100 -> repeatable
@@ -0,0 +1,2 @@
Wallet (owns a unidirectional @OneToMany): add a Coin -> version 0 -> 1
Folder (inverse, mappedBy @OneToMany) : add a Doc -> version 0 -> 0
@@ -0,0 +1,6 @@
default entity, change one column:
SQL> select p1_0.id,p1_0.a,p1_0.b,p1_0.c from Plain p1_0 where p1_0.id=?
SQL> update Plain set a=?,b=?,c=? where id=?
@DynamicUpdate entity, change one column:
SQL> select w1_0.id,w1_0.a,w1_0.b,w1_0.c from Wide w1_0 where w1_0.id=?
SQL> update Wide set a=? where id=?
@@ -0,0 +1,6 @@
HQL -> [Cy, Ana]
Criteria -> [Cy, Ana]
Native -> [Cy, Ana]
SQL> select e1_0.id,e1_0.city,e1_0.zip,e1_0.department_id,e1_0.level,e1_0.name,e1_0.salary from Employee e1_0 where e1_0.salary>? order by e1_0.salary
SQL> select e1_0.id,e1_0.city,e1_0.zip,e1_0.department_id,e1_0.level,e1_0.name,e1_0.salary from Employee e1_0 where e1_0.salary>? order by e1_0.salary
SQL> select * from employee where salary > 150 order by salary
@@ -0,0 +1,4 @@
record projection -> [EmployeeRow[name=Bo, salary=100], EmployeeRow[name=Cy, salary=200], EmployeeRow[name=Ana, salary=300]]
entities loaded: 0
SQL> select e1_0.name,e1_0.salary from Employee e1_0 order by e1_0.salary
full entity query loads 3 entities
@@ -0,0 +1,3 @@
HQL bulk update touched 3 rows
the entity already loaded still says salary = 100
after refresh() it says salary = 999
@@ -0,0 +1,3 @@
buildSessionFactory() failed with org.hibernate.query.NamedQueryValidationException:
Errors in named queries:
[1] Error in query named 'BrokenQuery.byTitle': Could not resolve attribute 'titel' of 'lab.hibernate.model.BrokenQuery' [select b from BrokenQuery b where b.titel = :t]