When to Use Entitymanager.Find() VS Entitymanager.Getreference() with JPA

When to use EntityManager.find() vs EntityManager.getReference() with JPA

I usually use getReference method when i do not need to access database state (I mean getter method). Just to change state (I mean setter method). As you should know, getReference returns a proxy object which uses a powerful feature called automatic dirty checking. Suppose the following

public class Person {

private String name;
private Integer age;

}

public class PersonServiceImpl implements PersonService {

public void changeAge(Integer personId, Integer newAge) {
Person person = em.getReference(Person.class, personId);

// person is a proxy
person.setAge(newAge);
}

}

If i call find method, JPA provider, behind the scenes, will call

SELECT NAME, AGE FROM PERSON WHERE PERSON_ID = ?

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?

If i call getReference method, JPA provider, behind the scenes, will call

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?

And you know why ???

When you call getReference, you will get a proxy object. Something like this one (JPA provider takes care of implementing this proxy)

public class PersonProxy {

// JPA provider sets up this field when you call getReference
private Integer personId;

private String query = "UPDATE PERSON SET ";

private boolean stateChanged = false;

public void setAge(Integer newAge) {
stateChanged = true;

query += query + "AGE = " + newAge;
}

}

So before transaction commit, JPA provider will see stateChanged flag in order to update OR NOT person entity. If no rows is updated after update statement, JPA provider will throw EntityNotFoundException according to JPA specification.

regards,

What is the difference between EntityManager.find() and EntityManger.getReference()?

JPA has the concept of an EntityManager, as you know. During your work in the entity manager some objects are loaded from the database, can be modified and afterwards flushed to the database.

find() has to return an initialized instance of your object. If it is not already loaded in the EntityManager, it is retrieved from the database.

getReference() is allowed to return a proxy instead of an initialized instance, if the entity has not been loaded in the EntityManager before. In this proxy, only the primary key attribute is initialized. Proxies can be created without hitting the database, because the only initialized attribute is already given to the getReference() function.

The latter is useful when you have an entity A referencing an entity B, and you want to set the b-attribute of A to B, without having to load B from the database.

Only if you reference other attributes of B, the proxy will be initialized.

Difference between CrudRepository findOne() and JpaRepository getOne()

It is just a guess but in 'pure JPA' there is a method of EntityManager called getReference. And it is designed to retrieve entity with only ID in it. Its use was mostly for indicating reference existed without the need to retrieve whole entity. Maybe the code will tell more:

// em is EntityManager
Department dept = em.getReference(Department.class, 30); // Gets only entity with ID property, rest is null
Employee emp = new Employee();
emp.setId(53);
emp.setName("Peter");
emp.setDepartment(dept);
dept.getEmployees().add(emp);
em.persist(emp);

I assume then getOne serves the same purpose. Why the queries generated are the same you ask? Well, AFAIR in JPA bible - Pro JPA2 by Mike Keith and Merrick Schincariol - almost every paragraph contains something like 'the behaviour depends on the vendor'.

EDIT:

I've set my own setup. Finally I came to conclusion that if You in any way interfere with entity fetched with getOne (even go for entity.getId()) it causes SQL to be executed. Although if You are using it only to create proxy (eg. for relationship indicator like shown in a code above), nothing happens and there is no additional SQL executed. So I assume in your service class You do something with this entity (use getter, log something) and that is why the output of these two methods looks the same.

ChlebikGitHub with example code

SO helpful question #1

SO helpful question #2

How do I use JPA's entityManager.getReference in Spring Boot?

It's injected normally, Spring Boot has nothing to do with it (Spring Boot is just Spring albeit with some configuration differences).

Just add

@PersistenceContext
private EntityManager em;

to your class and you can access the raw EntityManager object and all that it offers. There's nothing wrong with using it, you can't get everything done with repositories.

difference between getReference() and load()

  1. EntityManager#getReference() is functionally equal to session#load(). This can be verified by the hibernate 's EntityManager implementation ( AbstractEntityManagerImpl) which delegates the work to session#load().

    session#load() and session#get() have some difference in behaviour . For details , please see this.

  2. IdentifierLoadAccess is under the package org.hibernate . So it is Hibernate native API . All interfaces defined by JPA specification are under the package javax.persistence.

  3. JPA are standard Java API for persistence which means if your application only uses JPA API , theoretically , it is portable between different JPA providers. You application would work even you change to use other JPA provider only switch to other JavaEE application server.

Spring Jpa Entity - EntityManager.getReference

First, you should add @ManyToOne relation to the pet property:

@Entity
@Table(name = "PERSON")
public class Person {

//...

@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "pet_guid")
privat Pet pet;
}

It says to Hibernate to use a foreign key to the Pet entity (and its table).

Second, you should use the method getOne of your PersonRepository to get a reference to the Pet entity, for example:

@Service
public class PersonService {

private final PetRepository petRepo;
private final PersonRepository personRepo;

//...

@NonNull
@Transactional
public Person create(@NonNull final PersonDto personDto) {
Person person = new Person();
//...
UUID petId = personDto.getPetId();
Pet pet = petRepo.getOne(perId);
person.setPet(pet);
//...
return person.save(person);
}

}


Related Topics



Leave a reply



Submit