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:
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
employee.department_id after d.employees.add(e) only: null
|
||||
after setting employee.department too : 1
|
||||
@@ -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
|
||||
@@ -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]
|
||||
@@ -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]
|
||||
Reference in New Issue
Block a user