confused about Lazy loading - jpa

While trying to do some tests on lazy loading, to check if i'm understanding it well, i got totally confused.
Here's the entities i'm using on my test:
#Entity
public class Family {
#Id
private int id;
#OneToMany(mappedBy="family", fetch=FetchType.LAZY)
private Set<Person> members;
//getters & setters
public String toString(){
String s="";
for(Person p:getMembers()){
s+=p.getFirstName();
}
return s;
}
}
#Entity
public class Person implements Comparable<Person>{
#Id
private int id;
private String firstName;
private String lastName;
#ManyToOne
private Family family;
//getters &setters
}
here's my main method:
public static void main(String[] args) {
factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
em = factory.createEntityManager();
Query q = em.createQuery("select f from Family f");
List<Family> families= q.getResultList();
em.clear();
em.close();
factory.close();
for(Family f:families){
System.out.println(f);
}
}
What i understood from lazy loading, is that if an attribute is marked to be fetched lazily, and doesn't get accessed while it's managed, it won't be loaded in memory and any attempt to access it later won't work. Now what confuses me is that the test described above doesn't have any problem when accessing the lazy members attribute through the detached Family list, even after closing the EM and the EMF ! ... Is that normal? Am-i miss-understanding the lazy loading concept?
Note : I'm using a J2SE environment with an embedded DB. My provider is EclipseLink
Thanks in Advance
George

Check that your toString method is not triggered before the factory is closed, such as if the entity is being logged. I would not recommend triggering relationship in a toString method as this is error prone and can be triggered unexpectedly. Turning on EclipseLink logging will help show you where it gets accessed in the factory's lifecycle, assuming it is not part of the problem.

Ensure that you are using the eclipselink agent, or using static weaving. If you are using neither, then LAZY will not be weaved, and you will have EAGER.
Also EclipseLink supports access to LAZY relationships after the EntityManager is closed.
Although not after the factory is closed. However if the object was in the cache, then it may work after being closed as well. Also, if you have another factory open on the same persistence unit, then the persistence unit is still open.

It might be because the JPA provider is not required to use lazy initialization. It is not a must requirement for a JPA provider but a hint.
The JPA is required to eagerly fetch data when FetchType.EAGER is specified, but is not required to lazily fetch data when FetchType.LAZY is specified.

Related

EAGER loading with one select doesn't work in Spring Data JPA

there is already a similar post. Since this is already older, I hope something has changed since then (How does the FetchMode work in Spring Data JPA)
I would like to run all jpa repository#findById in one select, if the relationship is marked with EAGER. However, spring data ignores the EAGER specification and the FETCH.JOIN annotation from hibernate.
Is there a generic solution that all findById queries are executed in one select?
I wouldn't want to write a separate JPL or EntityGraph for each query. Does anyone know a generic solution?
JpaReposistory
The easiest option would be to write a JpaRepository<T, Id>. This is still a custom repository. However, you do not have to write so much code. You mainly have to write a repository interface for each relevant class and annotate the findById(Long id) method with a graph. The advantage is that if you edit your entity, the repository method will not need any changes because you define the entity graph within the entity class itself.
#Entity
#NamedEntityGraph(name = "Department.detail",
attributeNodes = #NamedAttributeNode("employees"))
public class Department {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany(fetch = FetchType.LAZY)
private List<Employee> employees;
// ...
}
public interface DepartmentRepository extends JpaRepository<Department, Long> {
#EntityGraph(value = "Department.detail", type = EntityGraphType.LOAD)
List<Department> findById(Long id);
}
As Spring data ignores the #Fetch(Fetchmode.JOIN) annotation or the information fetch = FetchType.EAGER, you cannot influence the join how you want it to be within the entity itself.
JPQL Query Where You Need It
Another option can be considered as a bad software engineering style: You can call the database queries directly where you need them. This means that you execute the code which you would usually write in the repository.
public ClassWithQueryResults {
#PersistenceContext
private EntityManager entityManager;
public void methodWhereYouNeedYourResults() {
TypedQuery<Department> query = entityManager.createQuery(
"SELECT DISTINCT d FROM Department d LEFT JOIN d.employees e",
Department.class);
List<Department> departments = query.getResultList();
// ...
}
}
Repository With JPQL, Generics and Reflection
Taking the previously suggested idea, you can create a custom repository which is valid for all your entities. The first step would be to create an attribute in your entity class in which you store the attribute which should be fetched.
public class Department extends AbstractEntity {
public static void String ATTRIBUTE_TO_FETCH = "employees";
...
}
With some tweaking, this can be extended to an array/list of all the fields which should be fetched. As this attribute is directly in your entity classes, the chance for any mistakes and future effort is low. Obviously, this attribute should have the same name in all your entities.
The next step would be to create the repository. I provide an example with the findAll() method. You have to pass it only the class name of the entities you want to have and the generics and reflection do the rest. (Consider what you want to do with the exceptions.)
public <T> List<T> findAll(Class<T> tClass)
throws NoSuchFieldException, IllegalAccessException {
String className = tClass.getSimpleName();
String attributeToFetch = (String)
tClass.getDeclaredField("ATTRIBUTE_TO_FETCH").get(null);
String queryString = String.format("SELECT DISTINCT p FROM %s p LEFT JOIN p.%s c",
className, attributeToFetch);
TypedQuery<T> query = entityManager.createQuery(queryString, tClass);
return query.getResultList();
}
Depending on how you want to implement this, the modification/generation of a query through simple manipulation of a String can offer the possibility of SQL injection attacks.

Kotlin inheritance and JPA

I'm trying to implement inheritance with Kotlin and JPA. My abstract base class (annotated with #Entity) holds the ID (annotated with #Id and #GeneratedValue) and other metadata, like createDate, etc. I'm getting several errors from Hibernate, one for each field except the ID:
org.hibernate.tuple.entity.PojoEntityTuplizer - HHH000112: Getters of lazy classes cannot be final: com.example.BaseEntity.createDate
As I've read I need to include the open keyword for each property.
I have 3 questions regarding this:
Why do I have to do that in the superclass, and don't need in subclass? I'm not overriding those properties.
Why isn't it complaining about the ID?
It seems to work without the open keyword, then why is the error logged?
Edit:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
abstract class BaseEntity(
#Id #GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long = 0,
val createdAt: Instant = Instant.now()
)
#Entity
class SubClass(
val someProperty: String = ""
) : BaseEntity()
I'm using the JPA plugin for Gradle, which I believe creates the noarg constructor, that's why I don't have to specify everything nullable.
Thank you!
The logged error has to do with lazy loading.
Hibernate extends entities at runtime to enable it. It is done by intercepting an access to properties when an entity is loaded lazily.
Kotlin has flipped the rules and all classes are final by default there. It is the reason why we're advised to add an open keyword.
If a property is not open hibernate cannot intercept access to it because final methods cannot be overridden. Hence the error.
Why isn't it complaining about the ID?
Because #Id is always loaded. There is no need to intercept access to it.
It seems to work without the open keyword, then why is the error logged?
The key word here is seems. It may introduce subtle bugs.
Consider the following #Entity:
#Entity
public class Book {
#Id
private Long id;
private String title;
public final Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public final String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
And the #Test:
#Test
public void test() {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
// signal here
Book book = new Book();
book.setId(1L);
book.setTitle("myTitle");
entityManager.persist(book);
// noise
entityManager.getTransaction().commit();
entityManager.close();
entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
// signal
Book reference = entityManager.getReference(Book.class, 1L);
String title = reference.getTitle();
assertNull(title); // passes
entityManager.getTransaction().commit();
entityManager.close();
}
This test passes but it should not (and fails if getTitle is not final).
This would be hard to notice
Why do I have to do that in the superclass, and don't need in subclass? I'm not overriding those properties.
Looks like Hibernate gives up when it sees final #Entity.
Add open to SubClass and you will the precious:
2019-05-02 23:27:27.500 ERROR 5609 --- [ main] o.h.tuple.entity.PojoEntityTuplizer : HHH000112: Getters of lazy classes cannot be final: com.caco3.hibernateanswer.SubClass.someProperty
See also:
final methods on entity silently breaks lazy proxy loading
How to avoid initializing HibernateProxy when invoking toString() on it? - my old question (note that Hibernate uses Byte Buddy these days).
PS
Did you forget to include #MappedSuperclass on BaseEntity?
Without the annotation it should fail with something like:
org.hibernate.AnnotationException: No identifier specified for entity: com.caco3.hibernateanswer.SubClass

Lazy Loading with EJB + JPA + Jersey

I have the following working without FetchType.LAZY:
#Entity
public class Test {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String text;
#ManyToOne
#JoinColumn(name = "lazy_id")
private Lazy lazy;
//getters and setters
}
#Entity
public class Lazy {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String text;
//getters and setters
}
And the query method:
public List<Test> all() {
try {
return em.createQuery("FROM Test t").getResultList();
} catch (NoResultException e) {
return null;
}
}
This is the JSON result:
[{"id":1,"text":"test 1","lazy":{"id":1,"text":"lazy 1"}},
{"id":2,"text":"test 2","lazy":{"id":2,"text":"lazy 2"}}]
However I want to return just the id and text data, so I tried to change the #ManyToOne(fetch = FetchType.LAZY)
Then I get this errors:
Severe: Generating incomplete JSON
Severe: org.hibernate.LazyInitializationException: could not initialize proxy [model.Lazy#1] - no Session
I could do something like changing the query to fetch only the fields I want:
public List<Test> all() {
try {
return em.createQuery("SELECT t.id, t.text FROM Test t").getResultList();
} catch (NoResultException e) {
return null;
}
}
But then my response in the JavaScript front end is:
[[1,"test 1"],[2,"test 2"]]
Not a array of objects anymore, mapping everything giving the amount of entities I have is far from ideal.
Most of the content I found is how to fetch the data afterwards, which is not my concern, all I need is to send only fields I want in the first place. I`m not sure whether the EJB #TransactionAttribute should be used or not, I couldn't find a working example. I also tried to change the strategy to a #OneToMany in the Lazy class but to no avail.
Since your question dates back a bit, I hope it's still relevant for you:
If you declare a mapping as lazy (or it is like that by the default behaviour), JPA won't fetch it until it is accessed. So your Lazy class will only be accessed if JSON tries to convert the whole thing and at that point it seems that you no longer have an open session, so the data can't be fetched and will result in an org.hibernate.LazyInitializationException.
If you stick with a lazy mapping (which is in general mostly fine), you have to explicitely fetch or access it, if you need the data for an use case.
Check out Vlad's excellent explanation on the topic.

Difference between lazy loading and eager loading in JPA?

public class University {
private String id;
private String name;
private String address;
private List<Student> students;
// setters and getters
}
In lazily loading when I load a University from the database, JPA loads its id, name, and address fields for me. Students will not load. When I call getStudents() method, JPA will then execute the query
select * from students where universitycode=id
Is my understanding of lazy loading correct?
Correct. If you use eager loading on the other hand, JPA will proactively load students for you and return fully-populated University object.
Whether single JOIN query will be used or two separate queries is up to the JPA provider (EclipseLink, Hibernate...)

Empty constructors and setters on JPA Entites

I don't like the requirement on have at least one empty constructor and public setters on JPA entities. While I understand the issue on the EntityManager side, this invalidates class invariants.
Does anyone have a solution for this (design pattern or idiom level) ?
Thanks!
Igor
With JPA, the default constructor is required, however, you are not required to use setters. You can choose a property access strategy(field or method) based on where you place the annotations.
The following code will use direct field access and will work as a part of an entity without a setter:
#Column(name = DESCRIPTION)
private String description;
public String getDescription() { return description; }
Versus method access with a setter:
private String description;
#Column(name = DESCRIPTION)
public void setDescription(String description) {
this.description = description;
}
public String getDescription() { return description; }
In point of fact you should have both a no-args constructor and getter and setter methods. The requirements are indicated in section 2.1 of the spec.
The no-arg constructor requirement is found on page 17 in my copy :
The entity class must have a no-arg
constructor. The entity class may have
other constructors as well. The no-arg
constructor must be public or
protected.
Page 18 has the requirement for accessor methods :
The persistent state of an entity is
represented by instance variables,
which may correspond to Java- Beans
properties. An instance variable may
be directly accessed only from within
the methods of the entity by the
entity instance itself. Instance
variables must not be accessed by
clients of the entity. The state of
the entity is available to clients
only through the entity’s accessor
methods (getter/setter methods) or
other business methods. Instance
variables must be private, protected,
or package visibility.
Field vs. Property access indicates how the JPA provider interacts with your entity, not how the client application interacts with it. The client should always use get and set methods.
Some JPA providers are more lenient in these requirements and you may be able to make the constructor private (as suggested above) with a specific vendor. The application might not be portable though so you could be in for a surprise if you migrate in the future.
So I wouldn't recommend omitting the methods entirely. In order to resolve the problem I'd mark the public no-arg ctor as deprecated (put something in the javadoc about it being for JPA provider use only). The set methods can contain the logic you want to maintain your invariants.
It isn't ideal but it should prevent the wrong ctor from being used by accident (I'm assuming you have a ctor that sets the invariants).
OpenJPA can add a no-arg ctor as a part of enhancing your entities.
Just so we're clear, the requirement is mandated in the JPA spec. The previous answer says that you can make the no-arg ctor private, but that is not compliant with the spec(I see the link points to a Hibernate specific page). The spec states that an Entity must have a public or protected no-arg ctor.
-Rick
With DataNucleus you don't have to add a default constructor if you don't want to; it will be added automatically by bytecode enhancement. Also you could persist fields instead of properties, hence no need for public setters.
--Andy (DataNucleus)
Just make your constructor protected or private, so you preserve the class invariants!
public class Person {
private String firstName;
private String lastName;
public Person(String firstName, String lastName) {
setFirstName(firstName);
setLastName(lastName);
}
// private no-arg constructor for hibernate.
private Person() {
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
// private setters for hibernate
private void setFirstName(String nme) {
firstName = nme;
}
private void setLastName(String nme) {
lastName = nme;
}
}
see http://www.javalobby.org/java/forums/m91937279.html for details.
Yes, persist fields instead of properties, but on the not wanting a default constructor you're typically (unless the byte code enhancer does some trick) you're not going to get away from it.
You have to allow your jpa implementation to instantiate the class, so a public default constructor is mandatory.