Hibernate get() and load() are the two Session methods that read an entity by its primary key, and get() runs a SELECT at once and returns null when the row is missing, whereas load() returns a placeholder object without any SQL and queries the database only when we read a field. The placeholder is a proxy, a subclass of the entity that Hibernate generates at runtime and that knows only the id. In Hibernate 7, Session.load() is removed and Session.get() is deprecated, so new code uses their Jakarta Persistence equivalents find() and getReference(), which behave the same way.
We use get() (find() in Hibernate 7) to read the data of a parcel, and load() (getReference() in Hibernate 7) when we need the parcel only as a foreign key, for example to save a scan event.
The following example reads a Parcel with the Hibernate 7 methods, with the old names in the comments.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String trackingCode;
private String status;
private double weightKg;
Parcel parcel = session.find(Parcel.class, 1L); // was get(): select ... where p1_0.id=?
Parcel ref = session.getReference(Parcel.class, 1L); // was load(): no SQL, ref is a proxy
String status = ref.getStatus(); // select ... where p1_0.id=?, "in transit"
Parcel missing = session.find(Parcel.class, 99L); // null
String gone = session.getReference(Parcel.class, 99L).getStatus(); // EntityNotFoundException
Notice that the missing parcel 99 gives null from find(), whereas getReference() throws only at the first getter.
Next, we look at the Hibernate 7 replacements for both methods and run every old call next to its replacement in a parcel tracking app.
1. What Did get() and load() Do?
Both methods take the entity class and an id. They differ in when Hibernate sends the SELECT to the database.
- The method get() sends the SELECT inside the call and returns a fully loaded Parcel.
- The method load() sends nothing. It returns a Parcel$HibernateProxy whose id is set, and runs the SELECT when we call a getter other than getId().
Their Hibernate 7 replacements keep the same timing, as the lookup of parcel 1 shows with both pairs of names.

The SQL is identical in both cases, and only the moment differs. The moment of the SELECT decides the other differences.
| get(), replaced by find() | load(), replaced by getReference() | |
|---|---|---|
| SQL at the call | One SELECT | None |
| Returned object | Parcel | Parcel$HibernateProxy (extends Parcel) |
| Row does not exist | Returns null | Proxy; EntityNotFoundException on the first getter |
| Read after the Session closes | Works | LazyInitializationException |
| Typical use | Show or change the parcel | Set a foreign key to the parcel |
2. What Replaced get() and load() in Hibernate 7?
Hibernate 7.0 removed Hibernate-specific methods that have a direct Jakarta Persistence replacement. Hibernate 7.0 removed load() “in favor of Session#getReference which have the same semantic”, and deprecated get() in favor of find() instead of removing it, because get() had not been deprecated before. Hibernate 7.1 also deprecated the byId() loader. The Class overloads of find() and getReference() are standard EntityManager methods, so the same code works in plain Jakarta Persistence. The entity-name overloads exist only on Session.
| Hibernate 5/6 call | Status in 7.4 | Hibernate 7 call |
|---|---|---|
| session.get(Parcel.class, id) | Deprecated (7.0), for removal | session.find(Parcel.class, id) |
| session.get(entityName, id) | Deprecated (7.0), for removal | session.find(entityName, id) |
| session.get(Parcel.class, id, LockMode.PESSIMISTIC_WRITE) | Deprecated (7.0), for removal | session.find(Parcel.class, id, LockModeType.PESSIMISTIC_WRITE) |
| session.load(Parcel.class, id) | Removed (7.0) | session.getReference(Parcel.class, id) |
| session.load(entityName, id) | Removed (7.0) | session.getReference(entityName, id) |
| session.load(parcel, id) | Still available | No change |
| session.byId(Parcel.class).load(id) | Deprecated (7.1), for removal | session.find(Parcel.class, id) |
| session.byId(Parcel.class).getReference(id) | Deprecated (7.1), for removal | session.getReference(Parcel.class, id) |
| catch (ObjectNotFoundException e) | No longer thrown here | catch (EntityNotFoundException e) |
The deprecated methods still compile with a removal warning, and they send the same SQL as their replacements. Code that calls load(Parcel.class, id) no longer compiles, because the only load() left on Session is the one that fills an object we pass in.
3. get() vs load() Example
The following example is a parcel tracking app that runs on Hibernate 7.4.11, Java 25 and an in-memory H2 database, and it runs every old call next to its replacement.
3.1. Parcel Tracking Model
A Parcel has a tracking code, a status and a weight. A ScanEvent records that a depot scanned a parcel. Its parcel field is a lazy @ManyToOne, so the ScanEvent table stores only the parcel’s id in parcel_id.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String location;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parcel_id")
private Parcel parcel;
The data is one parcel with id 1, tracking code “lisbon-1”, status “in transit” and a weight of 2.5 kg. We get a Session from the SessionFactory with sf.inTransaction(session -> …), which opens the session, starts a transaction and commits it at the end.
3.2. Loading a Parcel With find() Instead of get()
The method find() is the one to call where old code called get(). The deprecated get() still works in 7.4 and prints the same statement.
Parcel parcel = session.get(Parcel.class, id); // deprecated
// select p1_0.id,p1_0.status,p1_0.trackingCode,p1_0.weightKg from Parcel p1_0 where p1_0.id=?
Parcel parcel = session.find(Parcel.class, id);
// select p1_0.id,p1_0.status,p1_0.trackingCode,p1_0.weightKg from Parcel p1_0 where p1_0.id=?
Class<?> type = parcel.getClass(); // class Parcel
String status = parcel.getStatus(); // "in transit", no SQL
The object is the real Parcel class with every column already read, so we can use it after the session closes.
3.3. Getting a Proxy With getReference() Instead of load()
The method getReference() replaces load(Parcel.class, id). The call returns at once, and the SELECT appears only at getStatus().
Parcel parcel = session.getReference(Parcel.class, id); // no SQL
String className = parcel.getClass().getName(); // com.howtodoinjava.hibernate.getload.Parcel$HibernateProxy
Long parcelId = parcel.getId(); // 1, no SQL
boolean loaded = Hibernate.isInitialized(parcel); // false
String status = parcel.getStatus(); // select p1_0.id,p1_0.status,p1_0.trackingCode,p1_0.weightKg from Parcel p1_0 where p1_0.id=?
// "in transit"
boolean loadedAfter = Hibernate.isInitialized(parcel); // true
The method Hibernate.isInitialized() tells us whether the proxy has loaded its row yet. A proxy can load only while its Session is open. Read after the transaction, the proxy throws an exception.
Parcel parcel = sf.fromTransaction(session -> session.getReference(Parcel.class, id));
String status = parcel.getStatus(); // LazyInitializationException
org.hibernate.LazyInitializationException: Could not initialize proxy [com.howtodoinjava.hibernate.getload.Parcel#1] - no session
The exception is the same LazyInitializationException that lazy associations throw. We can load a proxy on purpose with Hibernate.initialize(), and Jakarta Persistence 3.2 adds new options to find() and getReference().
3.4. What Happens When the Parcel Does Not Exist
The missing row is the difference most developers search for. The methods get() and find() return null, whereas load() and getReference() never check the row at the call, so the error comes later.

Parcel viaGet = session.get(Parcel.class, 99L); // select ... where p1_0.id=? null
Parcel viaFind = session.find(Parcel.class, 99L); // select ... where p1_0.id=? null
Parcel viaById = session.byId(Parcel.class).load(99L); // select ... where p1_0.id=? null
Parcel parcel = session.getReference(Parcel.class, 99L); // no SQL, no error
Long parcelId = parcel.getId(); // 99
String status = parcel.getStatus(); // select ... where p1_0.id=?, EntityNotFoundException
jakarta.persistence.EntityNotFoundException: No row with the given identifier exists for entity [com.howtodoinjava.hibernate.getload.Parcel with id '99']
The exception type changed between versions. In Hibernate 6.6, load() and getReference() threw org.hibernate.ObjectNotFoundException with the message “No row with the given identifier exists”. Hibernate 7.4 throws EntityNotFoundException from jakarta.persistence, and the two classes are unrelated, so an old catch (ObjectNotFoundException e) block no longer catches it.
3.5. Recording a Scan Without Loading the Parcel
Recording a scan is the reason load() existed. To save a scan, Hibernate needs only the parcel’s id for the parcel_id column. With getReference(), saving the scan is one statement.
session.persist(new ScanEvent("Lisbon depot", session.getReference(Parcel.class, id)));
// insert into ScanEvent (location,parcel_id,id) values (?,?,default)
With find(), Hibernate first reads a parcel we never use.
session.persist(new ScanEvent("Porto depot", session.find(Parcel.class, id)));
// select p1_0.id,p1_0.status,p1_0.trackingCode,p1_0.weightKg from Parcel p1_0 where p1_0.id=?
// insert into ScanEvent (location,parcel_id,id) values (?,?,default)
With getReference(), every scan saves one SELECT, which adds up when a depot records thousands of scans. When the id comes from user input, we keep in mind that getReference() does not check it, so the foreign key on parcel_id is the only check.
3.6. Filling an Existing Object With load(parcel, id)
The third load() signature from Hibernate 5 is still on Session in 7.4 and is not deprecated. It reads the row into an object we created.
Parcel parcel = new Parcel();
session.load(parcel, id); // select p1_0.id,p1_0.status,p1_0.trackingCode,p1_0.weightKg from Parcel p1_0 where p1_0.id=?
String code = parcel.getTrackingCode(); // "lisbon-1"
session.load(new Parcel(), 99L); // EntityNotFoundException
Unlike the old load(Class, id), this one runs the SELECT at the call. We rarely need it, because find() returns a new object and is the standard way.
The complete project on GitHub contains every old and new call from this section, prints the SQL and includes 23 JUnit tests (mvn -q compile exec:java, mvn test).
4. get() vs load() FAQs
4.1. When Should I Use get() and When load()?
We use get() when the code reads the parcel’s data, and load() when the code needs the parcel only as a reference by id. In Hibernate 7, the same rule applies to the new names.
| Task | Hibernate 7 method (old name) |
|---|---|
| Show the parcel’s status on a tracking page | find() (get()) |
| Check whether a tracking id exists | find() and compare with null (get()) |
| Change the status to “delivered” | find() (get()) |
| Save a scan that points to the parcel | getReference() (load()) |
| Return the parcel from a service after the transaction | find() (get()) |
When in doubt, use find(). A null check is easier to handle than an exception that appears later at a getter call.
4.2. Does get() Always Hit the Database?
No. Both get() and find() first look in the persistence context, the entities the current Session already holds. A second lookup of the same id in the same session runs no SQL.
Parcel first = session.find(Parcel.class, id); // select ... where p1_0.id=?
Parcel second = session.get(Parcel.class, id); // no SQL, second == first
The same applies across the two method families. After getReference(), a find() for that id returns the proxy itself, which is loaded by then.
Parcel ref = session.getReference(Parcel.class, id); // no SQL
Parcel found = session.find(Parcel.class, id); // select ... where p1_0.id=?
boolean same = found == ref; // true, found is the proxy
boolean loaded = Hibernate.isInitialized(found); // true
4.3. How Do I Load a Parcel With a Lock?
Old code passed a LockMode to get(). The method find() takes the standard LockModeType, and Hibernate’s own LockMode also works, because in Hibernate 7 it implements FindOption. Both lines print the same statement.
Parcel locked = session.get(Parcel.class, id, LockMode.PESSIMISTIC_WRITE); // deprecated
Parcel lockedToo = session.find(Parcel.class, id, LockModeType.PESSIMISTIC_WRITE);
// select p1_0.id,p1_0.status,p1_0.trackingCode,p1_0.weightKg from Parcel p1_0 where p1_0.id=? for update
4.4. What Entity Name Do get(String, id) and load(String, id) Expect?
The default entity name is the fully qualified class name, not the simple name. The methods find(String, id) and getReference(String, id) take the same value.
Object parcel = session.find("com.howtodoinjava.hibernate.getload.Parcel", id); // the parcel
Object unknown = session.find("Parcel", id); // UnknownEntityTypeException: Unknown entity type 'Parcel'
With a normal @Entity class, the Class overload is the better choice, because the compiler checks the class name and the return type.
5. Conclusion
The old get() read the row at once and returned null for a missing id, whereas load() returned a proxy and read the row on first use, throwing an exception if the row was missing. In Hibernate 7, find() replaces get() and getReference() replaces the removed load(), with the same SQL timing, and a missing row raises jakarta.persistence.EntityNotFoundException instead of ObjectNotFoundException. We use find() to read or change a parcel, and getReference() when we need the parcel only as a foreign key.
6. References
- Hibernate ORM 7.0 Migration Guide: Session#load
- Session JavaDoc (Hibernate ORM 7.4)
- Hibernate ORM 7.4 User Guide: Obtain an entity with its data initialized
- Hibernate ORM 7.4 User Guide: Obtain an entity reference without initializing its data
- EntityManager JavaDoc (Jakarta Persistence 3.2)
- EntityNotFoundException JavaDoc (Jakarta Persistence 3.2)
Happy Learning !!
Hey Have u made tutorial on lock mode of Hibernate load() method.
Hii how can i get your souce code reply back in my mail id
Hi Lokesh,
Many online tutorials say “get() method always hits the db”.
but i dont see in the console the query triggered again if use get() twice.
how far is this statement true?
Probably, they are talking about when you hit call the method “first time”. When you call the method second time, hibernate “always” look at cache fist.
Thanks for the information, Sir.
Indeed Very Helpful
short code | Stuatus | A(Sample2) | B(Sample2) | C(Sample2) | DSample2) | E (Sample2) | A(All Sample3) |
11111 pending New New New New New New
22222 under process New – New New –
In the above Sample format it may helps for you (A,B,C under Sample1)(C,D are under Sample2),(A is the Sample3.The Sample3 have some status but here we are showing only on Carrier Staus that is the Default Status and some where i mentioned (‘-‘) that means the status we didn’t selected and observe for same alsi Sample3(‘-‘) but here i already select some status(b,c,d..) status except “A” But still it is showing the blank value.My question is inplace of bydefault status i want to display the status whatever we selected.I hope you understand.
Hi,
I created one report.In reports there are three samples.sample1,sample2,sample3.When I run the report it is generated the XML.In that XML it will display all the Sample reports Status’s.But my question is in Sample1,Sample2,Sample3 there are some carriers list with check boxes .For example assume in Sample3 have some Carrier status with the names of A,B,C and so on.. with check boxes.First Carrier Status is by default i.e (“A”) and it is coming from DB remaining all are optional. When I am selecting the first Carrier Status in the generated XML the Carrier status is showing properly which we are selected that is fine.But the problem is when I selected all the Carrier(“b”,”c”..so on) except first Carrier (“A”) in the XML it is not showing the selected Carrier Status..Can you please tell me how to resolve this.Here is the Sample code.here Sample3CarrierNameList is Sample3 list i.e(a,b,c..) ,carrier means single carrier name(“a”…)
for (Object resultRow : returnList) {
Object[] rowResultSet = (Object[]) resultRow;
Map rowMap = new LinkedHashMap();
// rowMap.put(“reqProvisioningId”, rowResultSet[0].toString());
rowMap.put(“reqDescription”, rowResultSet[0].toString());
rowMap.put(“reqShortCode”, (String) rowResultSet[1]);
rowMap.put(“reqStatusText”, (String) rowResultSet[2]);
rowMap.put(“reqAssignedToInternal”, (String) rowResultSet[3]);
int index = 4;
//CMP 1.2 Upgrade. Fix for “Carrier Status of Requests For Customer” report
//not displaying all tier 3 carrier status when carrier’s are renamed.
boolean isTier3Populated = false;
for (String carrier : carrierNameList) {
// Now add the first Sample 3 Carrier as a representative of all
// other Carriers
if (isSample3Populated == false
&& Sample3CarrierNameList.size() > 0
&& Sample3CarrierNameList.contains(carrier)) {
rowMap.put(Constants.ALL_TIER_3_CARRIER,
(String) rowResultSet[index]);
index++;
isTier3Populated = true;
} else if (!Sample3CarrierNameList.contains(carrier)) { // Exclude all Tier 3 Carriers Except the First One
rowMap.put(carrier, (String) rowResultSet[index]);
index++;
}
}
carrierWiseList.add(rowMap);
}
logger.debug(“Carrier wise List:” + carrierWiseList);
return carrierWiseList;
}
Thanks
Sri.K
Hi LOkesh, good tutorial, can you help with this little problem?
I hava a CategoryDao class and it has a getCategory() method to fetch a Category by ID. (This method works)
@Override public Category getById(int id) { return (Category) sessionFactory.getCurrentSession().get(Category.class, id); }And I need a same method to fetch a Category by its name, so I create a getByName() method but I have the following error:
org.hibernate.TypeMismatchException: Provided id of the wrong type for class com.sedae.model.Category. Expected: class java.lang.Integer, got class java.lang.String.
My method to fetch by name is as follow:
@Override public Category getByName(String name) { return (Category) sessionFactory.getCurrentSession().get(Category.class, name); }Can you show me where is my error? Thanks in advance…
You need to use HQL here i.e. Named queries. Write similar code as written for “GET_DEPARTMENT_BY_ID_QUERY” in this named query tutorial.
You can’t use any other attribute except primary key… Whether you wan’t to use another attribute you should have to use ‘HQL’.
Also look at @naturalId…
Something like
sql query = “SELECT * FROM Category c where c.name= :name”
and set the parameter like
setParameter(“name”,parameterName)
and get the result with getResultList or getSingleResult (if you are sure that query returns only one record)