Does anyone know why getById and GetReferenceById work differently?
I'm using JpaRepository and method GetReferenceById doesn't throw EntityNotFoundException but getById throwing this exception when object doesn't exists
getReferenceById returns a proxy and doesn't do a database call therefore no exception is called as JPA doesn't know if the entity with this ID exists.
getReferenceById executes EntityManager.getReference and the docs says:
T getReference(java.lang.Class entityClass, java.lang.Object
primaryKey)
Get an instance, whose state may be lazily fetched. If the
requested instance does not exist in the database, the
EntityNotFoundException is thrown when the instance state is first
accessed. (The persistence provider runtime is permitted to throw the
EntityNotFoundException when getReference is called.) The application
should not expect that the instance state will be available upon
detachment, unless it was accessed by the application while the entity
manager was open.
Source: https://jakarta.ee/specifications/persistence/2.2/apidocs/javax/persistence/entitymanager
Sometimes when using only getById as a way to fetch the entity makes some major issues. Usually we prefer to use getReferenceById method instead to save some extra calls, as if we were doing such using getById there is an extra call to database.
Let’s consider we have a Category child entity that is associated with the parent Product entity via the Product reference in the Category entity. Means each product has multiple categories. The code block that adds a new category to our product using productId as reference is below:
public Category addNewCategory(String categoryName, Long productId) {
Category category = new Category()
.setName(categoryName)
.setProduct(productRepository.findById(productId)
.orElseThrow(
()-> new EntityNotFoundException(
String.format(
"Product with id [%d] was not found!",
productId
)
)
)
);
categoryRepository.save(category);
return category;
}
If you want to add a category to a product, you first need to call findProductById each time you insert a new Category. When you got the Product then you insert it as a reference to the Category entity.
In this case Spring Data JPA would generate the following sql:
SELECT
product0_.id AS id1_0_0_,
product0_.slug AS name2_0_0_,
product0_.title AS title3_0_0_
FROM
product product0_
WHERE
product0_.id = 1
SELECT nextval ('hibernate_sequence')
INSERT INTO category (
product_id,
name,
id
)
VALUES (
1,
'book',
1
)
This query was generated by the findById method call, which is meant to load the entity in the current Persistence Context. However, in our case, we don’t need that. We just want to save a new Category entity and set the product_id Foreign Key column to a value that we already know.
But, since the only way to set the underlying product_id column value is to provide a Product entity reference, that’s why many developers end up calling the findById method.
In our case, running this SQL query is unnecessary because we don’t need to fetch the parent Product entity. But how can we get rid of this extra SQL query?
We can use getReferenceById method instead.
public PostComment addNewCategory(String name, Long productId) {
Category category = new Category()
.setName(name)
.setProduct(productRepository.getReferenceById(productId));
categoryRepository.save(category);
return category;
}
When calling the same addNewCategory method now, we see that the Product entity is no longer fetched as it will be fetched from a cached location where JPA holds entity Proxy. This way we can optimize our Spring applications that uses database intensively.
What you are saying seems a bit odd, as the implementation of the deprecated org.springframework.data.jpa.repository.JpaRepository#getById just delegates to it's replacement, org.springframework.data.jpa.repository.JpaRepository#getReferenceById.
As you can see in the implementation of that method (org.springframework.data.jpa.repository.support.SimpleJpaRepository#getReferenceById) it is directly using the EntityManager#getReference method.
When using Hibernate this normally only creates a Proxy, and just when you access one of the fields the Proxy fetches the real values from the DB - or throwing an EntityNotFoundException in case it does not exist.
Could it be that you changed something in your code, or you are being tricked by your debugger trying to display the toString of your method?
Related
I'm doing some junit tests to improve my application. One of these, tests the deletion of a single raw (indicated by an id as primary key) if present, and this works fine. Now I'm testing how my application behave if I want to delete an id Not present in my database.
What I expect is that my test passes with 0 rows affected, but he doesn't pass giving me this error:
No class com.package1.package2.package3.entities.className entity with id 326L exists!
Some advice?
deleteById() from CrudRepository firstly tries to find entity by Id.
In case no entity is found it throws exception; You can have your own repository and declare deleteAllByIdIn() method which takes collection of ids as argument and ORM will create its implementation for you.
This way you should not get any exceptions even if entities with such ids were not present. Or you can always make a native SQL query that deletes the row in DB by id.
SOLUTION:
The method should be :
#Query (your query, nativeQuery=true)
#Modifying
#Transactional
void DeleteById(#Param(id) Long id)
I wonder is it livable to associate an entity with a child entity by using not a proxy object but by creating a new object and setting Id manually? Like this?
#Transactional
public void save(#NonNull String name, #NonNull Long roleId) {
User user = new User();
user.setName(name);
Role role = new Role(); role.setRoleId(roleId);
// Instead of:
// roleRepository.getOne(roleId);
user.setRole(role);
userRepository.save(user);
}
I know that the accepted and well-documented way to do it is by calling smth. like:
em.getReference(Role.class, roleId) ;
or if use Spring Data
roleRepository.getOne(roleId);
or Hibernetish way:
session.load(Role.class, roleId)
So the question is, what bad consequences can one face if he does this trick by cheating the JPA provider and using this new object with set Id? Note, the only reason to do getOne() is to associate a newly created entity with an existing one. Yet the Role mock object is not managed, no fear of loosing any data. It simply does its job for connecting two entities.
From the Hibernate documentation:
getReference() obtains a reference to the entity. The state may or may
not be initialized. If the entity is already associated with the
current running Session, that reference (loaded or not) is returned.
If the entity is not loaded in the current Session and the entity
supports proxy generation, an uninitialized proxy is generated and
returned, otherwise the entity is loaded from the database and
returned.
So after testing I found that it basically does not even hit the database to check the presence of ID and save() would fail at commit if FK constraint is violated. It just requires additional dependency to auto-wire (RoleRepository).
So why should I have this proxy fetched by invoking getOne() instead of this mock object created with new if my case is as simple as this one? What and when may go wrong with this approach?
Thank you for clarifying things.
EDIT:
Hibernate/JPA, save a new entity while only setting id on #OneToOne association
This related topic doesn't answer the question. I am asking why calling JPA's API getReference() is better and what wrong may happen to me if I adopt this practice of creating a new "mock" objects with a given Id with new operator?
When an object is written to database and the primary identifier (id) is known, it can be retrieved by the code below:
MyObject myObject = session.get(Class<MyObject>, id);
It seems, there is another way similar to get() method:
IdentifierLoadAccess<MyObject> ila = session.byId(Class<MyObject>);
MyObject myObject = ila.load(id);
I'm looking for a scenario which clarifies differences between them and describes the reason for having two similar methods for the same job in API.
same question can be asked about session.load() and session.byId().getReference().
Edit 1:
According to API documentation:
session.get() and session.byId().load() return persistent instance with given identifier, or null if there is no such persistent instance.
session.load() and session.byId().getReference() might return a proxied instance that is initialized in demand.
IdentifierLoadAccess allows you to specify:
LockOptions
CacheMode
even specifying both of them at once:
Post post = session
.byId( Post.class )
.with( new LockOptions( LockMode.OPTIMISTIC_FORCE_INCREMENT) )
.with( CacheMode.GET )
.load( id );
The same for getting a Proxy reference via getReference(id).
So, they are more flexible than the standard get or load which only take the entity identifier.
The similarity between
MyObject myObject = session.get(Class<MyObject>, id);
and
IdentifierLoadAccess<MyObject> ila = session.byId(Class<MyObject>);
MyObject myObject = ila.load(id);
is that both uses the copncept of hibernate cache mechanism but difference comes in fetching the data from database i.e
When we use session.get(Class,id) data from database comes in cache and you can make changes on that data and will be reflected back in database, as hibernate internally maintains a time stamp cache. This time stamp cache records the time at which a particular Hibernate managed table got modified and before returning the data from entity cache it validate whether the result cache are older with respect to table modification time.
But in case of session.byId().getReference() hibernate uses the concept of natural id in which data from database comes in cache but only onces.If you do any changes on that data using session.save(entity object) approach hibernate will throw an exception and if you do manually modification of table(insert,update,delete) it will not be reflected back when you fetch the data again as it always get the data from cache without checking whether that table for that entity has been modified again or not.
In case of session.get() and session.load() if there is any change in database like (insert,delete,update) of record it will get reflected either in the form of record or null pointer exception if record gets deleted.But in case of session.byId().load() and session.byId().getReference() it will first get the record from database when you try to fetch first time then it will save those record in session and will be shown to user from session only if any (insertion,deletion,updation) occurs then it will not be reflected back
It's mostly used in polymorphic association/queries. assume you have an entity named User with the BillingDetails association. If BillingDetails was mapped with
lazy="true" (which is the default), Hibernate would proxy the association target. In this case, you wouldn’t be able to perform a type-cast to the concrete class CreditCard (which is a subclass of BillingDetails) at runtime, and even the instanceof operator would behave strangely:
User user = (User) session.get(User.class, userid);
BillingDetails bd = user.getDefaultBillingDetails();
System.out.println( bd instanceof CreditCard ); // Prints "false"
CreditCard cc = (CreditCard) bd; // ClassCastException!
To perform a proxy-safe typecast, use load()
User user = (User) session.get(User.class, userId);
BillingDetails bd = user.getDefaultBillingDetails();
// Narrow the proxy to the subclass, doesn't hit the database
CreditCard cc =
(CreditCard) session.load( CreditCard.class, bd.getId() );
expiryDate = cc.getExpiryDate();
Note that you can avoid these issues by avoiding lazy fetching, as in the follow-ing code, using an eager fetch query
User user = (User)session.createCriteria(User.class)
.add(Restrictions.eq("id", uid) )
.setFetchMode("defaultBillingDetails", FetchMode.JOIN)
.uniqueResult();
// The users defaultBillingDetails have been fetched eagerly
CreditCard cc = (CreditCard) user.getDefaultBillingDetails();
expiryDate = cc.getExpiryDate();
Truly object-oriented code shouldn’t use instanceof or numerous typecasts. If you find yourself running into problems with proxies, you should question your design, asking whether there is a more polymorphic approach.
The key difference between get() and load() method is that load() will throw an exception if an object with id passed to them is not found, but get() will return null. Another important difference is that load can return proxy without hitting the database unless required (when you access any attribute other than id) but get() always go to the database, so sometimes using load() can be faster than the get() method. It makes sense to use the load() method if you know the object exists but get() method if you are not sure about object's existence.
I have a object A which maps to table A in DB
class A {
Integer id;
String field2,field2;field3 ,... fieldN;
//lots of other attribute
}
Now i want to write a DAO api that just updates a single field.One approach is that i can first load the object then changes the attribute i need and then use merge api
//start transcation
A a = session.load(A.class, id);
A.setfieldP(newValue)
session.merge(A)
//commit transcation
Now if i use following code
//start transcation
A a = new A();
a.setId(id); //set a id by which object A exists in DB
A.setfieldP(newValue)
session.merge(A)
//commit transaction
Now second approach all fields except id and fieldP are set to null
1)Now is there any other approach?
2)Can i use update instead of merge ?
If you need to update lots of entities at once the most efficient way is to use a query:
Query query = session.createQuery("update EntityName set fieldP = 'newValue' "
+ "where id IN (75, 76)");
query.executeUpdate();
This allows you to change field values without loading the entity or entities into memory.
It is best practice is to use named queries and named parameters - the above implementation is just an example.
I usually prefer session.get vs session.load, as session.get will return null as opposed to throwing an exception, but it depends on the behavior you want.
loading the object, setting your field, and calling either
session.merge(myObject)
is the standard way, although you can also use
session.saveOrUpdate(myObject)
as long as the object hasn't been detached, which in your case, it won't have been detached. Here is a good article explaining the differences in merge and saveOrUpdate.
In your second example, you are editing the primary key of the object? This is generally bad form, you should delete and insert instead of changing the primary key.
Using JPA you can do it this way.
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaUpdate<User> criteria = builder.createCriteriaUpdate(User.class);
Root<User> root = criteria.from(User.class);
criteria.set(root.get("fname"), user.getName());
criteria.set(root.get("lname"), user.getlastName());
criteria.where(builder.equal(root.get("id"), user.getId()));
session.createQuery(criteria).executeUpdate();
One more optimization here could be using dynamic-update set to true for the entity. This will make sure that whenever there is an update, only field(s) which are changed only gets updated.
I am new to Hibernate.
I want to create a public Object getById(Class class,long id) function. I tried creating query like: from :nameEntity where id= :id but I'm facing problems when setting parameter for nameEntity. Hibernate doesn't recognize it as a parameter.
I used session.createQuery function. Could you please help me?
You will either have to do String concatenation to achieve this:
session.createQuery("from " + clazz.getName() " where id=:id").
setParameter("id", id).
uniqueResult();
or use the Criteria API:
session.createCriteria(clazz).
add(Expression.eq("id", id).
uniqueResult()
I want to create a public Object getById(Class class, long id) function. I tried creating query like:" from :nameEntity where id=:id " but I'm facing problems when setting parameter for 'nameEntity'. Hibernate doesn't recognize it as a parameter.
Don't build a query, use the load() or get() methods from the Session. They are actually heavily overloaded, allowing to pass the class either as Class or String, the persistent object identifier and, if required, a lock option (for pessimistic locking):
get(Class clazz, Serializable id)
load(Class theClass, Serializable id)
get(Class clazz, Serializable id, LockOptions lockOptions)
load(Class theClass, Serializable id, LockOptions lockOptions)
get(String entityName, Serializable id)
load(String entityName, Serializable id)
get(String entityName, Serializable id, LockOptions lockOptions)
load(String entityName, Serializable id, LockOptions lockOptions)
What are the differences between get() and load()? This is explained in the Reference Documentation:
10.3. Loading an object
The load() methods of Session
provide a way of retrieving a
persistent instance if you know its
identifier. load() takes a class
object and loads the state into a
newly instantiated instance of that
class in a persistent state.
Cat fritz = (Cat) sess.load(Cat.class, generatedId);
// you need to wrap primitive identifiers
long id = 1234;
DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new Long(id) );
Alternatively, you can load state into
a given instance:
Cat cat = new DomesticCat();
// load pk's state into cat
sess.load( cat, new Long(pkId) );
Set kittens = cat.getKittens();
Be aware that load() will throw an
unrecoverable exception if there is no
matching database row. If the class is
mapped with a proxy, load() just
returns an uninitialized proxy and
does not actually hit the database
until you invoke a method of the
proxy. This is useful if you wish to
create an association to an object
without actually loading it from the
database. It also allows multiple
instances to be loaded as a batch if
batch-size is defined for the class
mapping.
If you are not certain that a matching
row exists, you should use the get()
method which hits the database
immediately and returns null if
there is no matching row.
Cat cat = (Cat) sess.get(Cat.class, id);
if (cat==null) {
cat = new Cat();
sess.save(cat, id);
}
return cat;
You can even load an object using an
SQL SELECT ... FOR UPDATE, using a
LockMode. See the API documentation
for more information.
Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);
Any associated instances or contained
collections will not be selected FOR
UPDATE, unless you decide to specify
lock or all as a cascade style for the
association.
It is possible to re-load an object
and all its collections at any time,
using the refresh() method. This is
useful when database triggers are used
to initialize some of the properties
of the object.
sess.save(cat);
sess.flush(); //force the SQL INSERT
sess.refresh(cat); //re-read the state (after the trigger executes)
How much does Hibernate load from the
database and how many SQL SELECTs will
it use? This depends on the fetching
strategy. This is explained in Section
20.1, “Fetching strategies”.
How to choose between them?
Choosing between get() and
load() is easy: If you’re certain the
persistent object exists, and
nonexistence would be considered
exceptional, load() is a good option.
If you aren’t certain there is a
persistent instance with the given
identifier, use get() and test the
return value to see if it’s null.
See also
Hibernate Load and Get
Loading Entities with Hibernate
Advantage of load() vs get()
Related question
Hibernate: Difference between session.get and session.load