Is there a way to specify the only fields to be eagerly fetched in hibernate/JPA #ManyToOne - FetchType.EAGER?
Something like:
#ManyToOne(fetch=FetchType.EAGER, eagerFields={"id","name"})
private Company company;
The JPA 2.0 specification does not define the possiblity you are trying to achieve as #ManyToOne annotation is defined as follows (see section 11.1.26 of the JPA 2.0 specification):
public #interface ManyToOne {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default EAGER;
boolean optional() default true;
}
What you can do to achieve eager loading only for selected fields, is possible if you specify #Basic(fetch = FetchType.LAZY) for the fields of Companyentity you want to load lazily; BUT that is not encouraged for fields other than association fields because it depends on the mercy of the provider implementation as the following extract shows:
The EAGER strategy is a requirement on the persistence provider runtime that data must be eagerly
fetched. The LAZY strategy is a hint to the persistence provider runtime that data should be fetched
lazily when it is first accessed. The implementation is permitted to eagerly fetch data for which the
LAZY strategy hint has been specified. In particular, lazy fetching might only be available for Basic
mappings for which property-based access is used.
Related
In Hibernate Search 5.x I can map entity method as the fulltext field like this:
#Entity
public class Person {
#Id
#GeneratedValue
private Long id;
private String name;
private String surname;
public String getWholeName() {
return name + " " + surname;
}
// getters, setters
}
// Mapping configuration, programmatic approach
SearchMapping sm = new SearchMapping();
sm
.entity(Person.class)
.indexed()
.property("wholeName", ElementType.METHOD)
.field();
Then I have a field with name "wholeName" in my fulltext index and it contains return value of getWholeName() method.
How to do it in Hibernate Search 6? I found only a way how to map an entity field but not a method. Thank you!
Short answer
If there is no field named wholeName, Hibernate Search 6 will automatically fall back to the getter. The ElementType from Hibernate Search 5 is no longer necessary, and that's why it was removed.
Note that Hibernate Search is also smarter when it comes to detecting changes in entities. That's usually great, but the downside is that you'll need to tell Hibernate Search what other attributes wholeName relies on. See this section of the documentation (you can also find an example using the programmatic mapping API here).
Long answer
When an attribute has a field but no getter, or a getter but no field, there is no ambiguity. Hibernate Search uses the only available access type.
When an attribute has both a field and a getter, there is a choice to be made. Hibernate Search 6 chooses to comply with Hibernate ORM's access type.
Hibernate ORM accesses attributes either by direct access to the field ("field" access type) or through getters/setters ("property" access type).
By default, the access type in Hibernate ORM is determined by where your #Id annotation is. In this case, the #Id annotation is located on a field, not a method, so Hibernate ORM will use the "field" access type. And so will Hibernate Search.
You can also set the Hibernate ORM access type explicitly using the #Access annotation, either for the whole entity (put the annotation on the class) or for a particular property (put the annotation on the field). Hibernate Search will comply with this too.
I googled a lot and It is really bizarre that Spring Boot (latest version) may not have the lazy loading is not working. Below are pieces of my code:
My resource:
public ResponseEntity<Page<AirWaybill>> searchAirWaybill(CriteraDto criteriaDto, #PageableDefault(size = 10) Pageable pageable{
airWaybillService.searchAirWaybill(criteriaDto, pageable);
return ResponseEntity.ok().body(result);
}
My service:
#Service
#Transactional
public class AirWaybillService {
//Methods
public Page<AirWaybill> searchAirWaybill(AirWaybillCriteriaDto searchCriteria, Pageable pageable){
//Construct the specification
return airWaybillRepository.findAll(spec, pageable);
}
}
My Entity:
#Entity
#Table(name = "TRACKING_AIR_WAYBILL")
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#airWaybillId") //to fix Infinite recursion with LoadedAirWaybill class
public class AirWaybill{
//Some attributes
#NotNull
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FK_TRACKING_CORPORATE_BRANCH_ID")
private CorporateBranch corporateBranch;
}
And when debugging, I still getting all lazy loaded attributed loaded. See image below.
One of my questions is could Jackson be involved in such behaviour?
Is there any way that I may have missed to activate the lazy loading?
EDIT
Another question, could the debugger be involved in ruining the lazy loading?
EDIT 2:
For specification build, I have :
public static Specification<AirWaybill> isBranchAirWayBill(long id){
return new Specification<AirWaybill>() {
#Override
public Predicate toPredicate(Root<AirWaybill> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return cb.equal(root.join("corporateBranch",JoinType.LEFT).get("id"),id);
}
};
}
Hibernate Session exists within method with #Transactional.
Passing entity outside Service class is a bad practise because session is being closed after leaving your search method. On the other hand your entity contains lazy initialised collections, which cannot be pulled once session is closed.
The good practise is to map entity onto transport object and return those transport objects from service (not raw entities).
SpringBoot by default has enabled:
spring.jpa.open-in-view = true
That means transaction is always open. Try to disable it.
more information here
Most likely you are debugging while still being inside the service, thus while the transaction is still active and lazy loading can be triggered (any method called on a lazy element triggered the fetch from the database).
The problem is that lazy loading cannot occur while being outside of the transaction. And Jackson is parsing your entity definitely outside the boundaries of one.
You either should fetch all the required dependencies when building your specification or try with the #Transactional on the resource level (but try that as of last resort).
Just so that you know, LAZY fetching strategy is only a hint.. not a mandatory action. Eager is mandatory:
The LAZY strategy is a hint to the persistence provider runtime that
data should be fetched lazily when it is first accessed. The
implementation is permitted to eagerly fetch data for which the LAZY
strategy hint has been specified.
When using a debugger, you are trying to access the value of your variables. So, at the moment you click that little arrow on your screen, the value of the variable in question is (lazily) loaded.
I suppose you are using Hibernate as JPA.
From specification:
The EAGER strategy is a requirement on the persistence provider runtime that data must be eagerly fetched. The LAZY strategy is a hint to the persistence provider runtime that data should be fetched lazily when it is first accessed. The implementation is permitted to eagerly fetch data for which the LAZY strategy hint has been specified. https://docs.jboss.org/hibernate/jpa/2.2/api/javax/persistence/FetchType.html
Hibernate ignores fetch type specially in OneToOne and ManyToOne relationships from non owning side.
There are few options how to force Hibernate use fetch type LAZY if you really need it.
The simplest one is to fake one-to-many relationship. This will work because lazy loading of collection is much easier then lazy loading of single nullable property but generally this solution is very inconvenient if you use complex JPQL/HQL queries.
The other one is to use build time bytecode instrumentation. For more details please read Hibernate documentation: 19.1.7. Using lazy property fetching. Remember that in this case you have to add #LazyToOne(LazyToOneOption.NO_PROXY) annotation to one-to-one relationship to make it lazy. Setting fetch to LAZY is not enough.
The last solution is to use runtime bytecode instrumentation but it will work only for those who use Hibernate as JPA provider in full-blown JEE environment (in such case setting "hibernate.ejb.use_class_enhancer" to true should do the trick: Entity Manager Configuration) or use Hibernate with Spring configured to do runtime weaving (this might be hard to achieve on some older application servers). In this case #LazyToOne(LazyToOneOption.NO_PROXY) annotation is also required.
For more informations look at this:
http://justonjava.blogspot.com/2010/09/lazy-one-to-one-and-one-to-many.html
Just a guess: you are forcing a fetch while building your specification.
I expect something like
static Specification<AirWaybill> buildSpec() {
return (root, query, criteriaBuilder) -> {
Join<AirWaybill, CorporateBranch> br = (Join) root.fetch("corporateBranch");
return criteriaBuilder.equal(br.get("addressType"), 1);
};
}
If this is the case, try changing root.fetch to root.join
The retrieved data already lazy but you are using debug mode its return value when click to watch a data from a debugger.
You can solve this problem with wit 2 steps with jackson-datatype-hibernate:
kotlin example
Add In build.gradle.kts:
implementation("com.fasterxml.jackson.datatype:jackson-datatype-hibernate5:$jacksonHibernate")
Create #Bean
#Bean
fun hibernate5Module(): Module = Hibernate5Module()
Notice that Module is com.fasterxml.jackson.databind.Module, not java.util.Module
Another consideration is while using Lombok, #Data/#Getter annotation causes to load lazy items without need. So be careful when using Lombok.
This was my case.
I think I might have a solution. You can give this a try. This worked for me after 4 hours of hit and trial -
User Entity :
class User {
#Id
String id;
#JsonManagedReference
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Address> addressDetailVOList = new ArrayList<Address>();
}
Address entity :
class Address {
#JsonBackReference
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "userId")
private User user;
}
Your parent class will use #JsonManagedReference, and child class will use #JsonBackReference. With this, you can avoid the infinite loop of entity objects as response and stack overflow error.
I also faced the same issue with Spring data JPA. I added the below annotation & able to get the customer records for a given ORDER ID
Customer to Order : one to Many
Order to customer is lazy load.
Order.java
#ManyToOne(cascade = CascadeType.ALL,targetEntity = CustomerEntity.class,fetch = FetchType.LAZY)
#Fetch(FetchMode. JOIN)
#JoinColumn(name = "CUSTOMER_ID",referencedColumnName = "CUSTOMER_ID",insertable = false,updatable = false)
#LazyToOne(LazyToOneOption.PROXY)
Private CustomerEntity customer
Customer.java
#Entity
#TabLe(name = "CUSTOMER" ,
uniqueConstraints = #UniqueConstraint(columnNames= {"mobile"}))
public class CustomerEntity {
#GeneratedVaLue(strategy = GenerationType.IDENTITY)
#CoLumn(name = "customer_id" )
private Integer customerld;
private String name;
private String address;
private String city;
private String state;
private Integer zipCode;
private Integer mobileNumber;
#OneToMany(mappedBy = " customer" )
#Fetch(FetchMode.JOIN)
#LazyToOne(LazyToOneOption.PROXY)
private List<OrderEntity> orders;
}
Entity{
String code;
String parentCode;
...
#ManyToOne
#JoinColumn(name="parentCode",referencedColumnName="code")
Entity parentEntity;
}
My entity class is like this. what i want to do is using findAll() to get an entity list with each entity get its own direct parent. But spring jpa will get parent's parent until the root , i need to avoid this.
Thank you!
Since a default fetch type for a #ManyToOne relation is an FetchType.EAGER I think you have just add a fetch type as LAZY explicitly:
Entity{
String code;
String parentCode;
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="parentCode",referencedColumnName="code")
Entity parentEntity;
}
It's not about Spring JPA but JPA itself. When you add a relationship, following defaults apply unless specified otherwise.
#xxToOne - FetchType.EAGER
#xxToMany- FetchType.LAZY
So, in your example you have a #ManyToOne which has a default EAGER fetch and one join query is appended. If your parent has another #xxToOne it adds one more join and so on. It's good to know the boundaries of your entities and decide which type of FetchType is required.
Even if you add like this:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="parentCode",referencedColumnName="code")
Entity parentEntity;
.. if you parent has more relationships you might be getting everything loaded while fetching parent. Thus, all relationships need to be Lazy. It's a design choice based on entities.
But be aware about the ORM's N+1 problem : JPA Hibernate n+1 issue (Lazy & Eager Diff)
I have a User class and a Role class. Both of these classes are JPA entities, and hence stored in a Person table and a Role table, as well as a corresponding link table Person_Role used for joins, since the association is many to many. A user may have many roles, and a role may be assigned to many users.
#Entity
#Table(name="role")
public class Role implements Comparable<Role>
{
// data members
#Id #GeneratedValue
private int id; // primary key
private String name; // name of the role
private String description; // description of the role
...
}
#Entity
#Table(name="person")
public class Person implements Comparable<Person>
{
// data members
#Id #GeneratedValue
protected int id; // the primary key
protected String username; // the user's unique user name
protected String firstName; // the user's first name
protected String lastName; // the user's last name
protected String email; // the user's work e-mail address
#Transient
protected String history; // chronological list of changes to the person
// don't want to load this unless an explicit call to getHistory() is made
#Transient
protected Set<Role> roles; // list of roles assigned to the user
// don't want to load this unless an explicit call to getRoles() is made
...
}
The User entity is used extensively throughout the application, as it is a shared reference for many objects, and is used in many, many searches. 99.99% of the time, the user's roles and history are not needed. I'm new to JPA, and have been reading the "Java Persistence with Hibernate" book in order to learn. As I understand lazy fetching, it will load all the corresponding User data from the database when any getXXX() method is called.
Ex: user.getFirstName() would cause a database hit and load all the data, including roles and history, for the user.
I want to avoid this at all costs. Its just needless in 99.99% of the use cases. So, what's the best way to handle this?
My initial thought is to mark the Set<Role> roles and Set<String> history in the User class as #Transient and manually query for the roles and history only when the user.getRoles() or user.getHistory() method is called.
Thanks for any suggestions.
As I understand lazy fetching, it will load all the corresponding User
data from the database when any getXXX() method is called.
You can force JPA to be eager or lazy while fetching data from the database but first and foremost it depends on JPA provider. As described in JPA 2.1 specification, chapter 11.1.6:
The FetchType enum defines strategies for fetching data from the
database:
public enum FetchType { LAZY, EAGER };
The EAGER strategy is a requirement on the persistence provider
runtime that data must be eagerly fetched. The LAZY strategy is a
hint to the persistence provider runtime that data should be fetched lazily when it is first accessed. The implementation is permitted to
eagerly fetch data for which the LAZY strategy hint has been
specified. In particular, lazy fetching might only be available for
Basic mappings for which property-based access is used.
A nice presentation on how fetching strategies work and how performant they are in real-life scenarios you can find here.
Ex: user.getFirstName() would cause a database hit and load all the
data, including roles and history, for the user.
Data are retrieved either directly from the persistence context (usually it has a short lifespan) or indirectly from the underlying database (when it's not found in the transactional/shared caches). If entity manager is requested to get your entity object and it does not exist in the persistence context it needs to go deeper - into the database in the worst scenario.
I want to avoid this at all costs. Its just needless in 99.99% of the
use cases. So, what's the best way to handle this?
An example approach:
#Entity
#NamedQuery(name="Person.getNameById",
query="SELECT p.name FROM Person p WHERE p.id = :id")
public class Person
{
#Id #GeneratedValue
protected int id;
private String name; //the sole attribute to be requested
#ManyToMany //fetch type is lazy by default
#JoinTable
protected Set<Role> roles; //not loaded until referenced or accessed
...
}
Usually the best way to go is the find method. It's perfect when you want to retrieve all non-relationship attributes at once:
Person p = em.find(Person.class, id)
An alternative for you would be to use named query. It's useful when you need a single attribute or a small subset of attributes:
String name = em.createNamedQuery("Person.getNameById", String.class)
.setParameter("id", id)
.getSingleResult()
My initial thought is to mark the Set roles and Set history in the
User class as #Transient and manually query for the roles and history
only when the user.getRoles() or user.getHistory() method is called.
Transient attributes are not persisted in a database. Whatever you will set to these attributes it will stay in memory only. I would prefer JPA doing it lazily.
It will not Load all the data just the the relative to
Person entity = (Person) this.em.find(Person.class, id);
in lazy fetching it will issue a select statement from only the table person, as for protected Set<Role> roles;it will not be loaded but replaced with a proxy object
Hibernate uses a proxy object to implement lazy loading. When we request to load the Object from the database, and the fetched Object has a reference to another concrete object, Hibernate returns a proxy instead of the concrete associated object.
Hibernate creates a proxy object using bytecode instrumentation (provided by javassist). Hibernate creates a subclass of our entity class at runtime using the code generation library and replaces the actual object with the newly created proxy.
Let's say we have an entity
#Entity
public class Person {
#Id int id;
#Basic String name;
#Basic String remark;
}
Let's say "remark" field is filled with big texts, but rarely used. So it would be good if when you run jpql: SELECT p FROM Person p, EclipseLink just executes sql select id, name from person
And than when you call person.getRemark(), it will get fetched with select remark from person where id = ?.
Is it possible with EclipseLink 2.1?
You can indeed define a fetch attribute in a Basic annotation and set it to LAZY. But let me quote what the specification says about it:
11.1.6 Basic Annotation
(...)
The EAGER strategy is a requirement
on the persistence provider runtime
that data must be eagerly fetched.
The LAZY strategy is a hint to the persistence provider runtime that
data should be fetched lazily when it
is first accessed. The implementation
is permitted to eagerly fetch data for
which the LAZY strategy hint has
been specified. In particular, lazy
fetching might only be available for
Basic mappings for which
property-based access is used.
In the particular case of EclipseLink, the behavior will depend on the context (Java EE vs Java SE) as explained in What You May Need to Know About EclipseLink JPA Lazy Loading.
In a Java EE environment (assuming the container implements the appropriate container contracts of the EJB 3.0 specification):
EclipseLink JPA performs lazy loading when the fetch attribute is set to javax.persistence.FetchType.LAZY.
In a Java SE environment:
By default, EclipseLink JPA ignores the
fetch attribute and default javax.persistence.FetchType.EAGER applies.
To configure EclipseLink JPA to perform lazy loading when the fetch attribute set to FetchType.LAZY, consider one of the following:
How to Configure Dynamic Weaving for JPA Entities Using the EclipseLink Agent
How to Configure Static Weaving for JPA Entities
Try add annotation #Basic(fetch = FetchType.LAZY)
#Entity
public class Person {
#Id int id;
#Basic String name;
#Basic(fetch = FetchType.LAZY) String remark;
}
We solved this problem (when using ActiveRecord and Hibernate) by putting the large string (usually a CLOB or BLOB) into it's own table with a FK to the main table (Person in this case.) Then it works like you want.