JPA Hibernate Lazy many-to-one fetch proxy - java

I'm using JPA 2.1 and Hibernate 4.3.7
I tried to tuned my app so I turn relationships to lazy and fetch only what I need them
I have a problem with the many-to-one relationships, when turn to lazy when I load the entity again Hibernate replace the entity by a proxy even if I fetch the entity and this proxy is not working in the view part (JSF) of the application.
The problem disapear when the many-to-one is in eager mode but hibernate perform one select more for each many-to-one even if I don't need them
#Entity
public class Department {
#Id
private Integer id;
//...
}
1/
#Entity
public class Employee {
#Id
private Integer id;
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "id_department", referencedColumnName = "id")
private Department department;
//...
}
the JPQL query:
SELECT e FROM Employee e LEFT JOIN FETCH e.department WHERE e.id=:id
=> one select query => faster but department is of type Department_$$_jvst3ac_5f (employee.getDepartment().getClass().getCanonicalName()) and this proxy doesn't work in the view part of the application
2/
#Entity
public class Employee {
#Id
private Integer id;
#ManyToOne(fetch = FetchType.EAGER, optional = true)
#JoinColumn(name = "id_department", referencedColumnName = "id")
private Department department;
//...
}
the JPQL query:
SELECT e FROM Employee e WHERE e.id=:id
=> two selects => slower but department is loaded as Department and everything goes fine in the view part of the application
The relation is unidirectional, Department have no references of emplyees
Is this possible to have the department without proxy when using FETCH JOIN?
After the response of Luiggi I will precised that the data are fetched with lazy many-to-one + fetch join. When I do a employee.getDepartment().toString() I have Department{ id=11, ...} but the class of this department is still Department_$$_jvst3ac_5f. For reason I don't know, the JSF/PrimeFaces selectOneMenu component don't work properly whith HibernateProxy even if the data are fetched
I tried to use the Hibernate annotation #LazyToOne(LazyToOneOption.FALSE) in addition of #ManyToOne(fetch = FetchType.LAZY) but the result is similar of #ManyToOne(fetch = FetchType.EAGER) alone...

The problem is that when you use lazy loading you will obtain a proxy of the class (as you already stated) and this proxy can fetch the data from database only if the hibernatesession is still open. Seems like your session is being closed when returning the data to the view, so when trying to use the lazily-loaded field in the view you're getting the exception.
Possible solutions:
Keep the field as fetch eager and pay the overhead for each query against your entity (probably this isn't good and can affect performance, but is a solution).
Maintain your field as lazy and use the proper get method before the Hibernate session is closed in order to the proxy to retrieve the relevant data to be used after the session is closed.

Related

Controlling lazy/eager loading of #Formula columns dynamically

We have a few entities with a bunch of properties annotated with Hibernate's #Formula annotation. The SQL snippets in the annotations mainly run scalar sub-queries (e.g. COUNT queries). As an example, we have a one-to-many relationship hierarchy that's four levels deep: A <- B <- C <- D (where <- marks a one-to-many association). Pretty often when fetching an entity of type A, we'd like to know the amount of associated entities of type D. For this we use a #Formula-annotated property in A.
As we don't need these values every time, we've declared the #Formula properties as lazy-loaded (we've enabled Hibernate's bytecode enhancement to make this possible). But for some queries, we'd like to load these properties eagerly. We often load hundreds of entities of type A in one query, and it'd be important performance-wise to control the eager/lazy loading of these properties dynamically. We already use JPA's entity graphs to control which properties get loaded eagerly for certain queries, but entity graphs don't seem to work here. Even if we list the #Formula properties in the entity graph, they're still loaded lazily.
Is it possible to control lazy/eager loading of #Formula columns dynamically on a per query basis? We're currently restricted to the JPA Criteria Query API, and named queries are not a possibility here.
Update:
The properties in question are not associations to other entities, but just some calculated values. This means that e.g. fetch profiles don't apply here, as they're only applicable to entity associations (or at least that's how I understood the Hibernate manual). Here's an example of one of our #Formula properties:
#Entity
public class A {
#Basic(fetch = FetchType.LAZY)
#Formula("(select count(*) from entity_D_table where ...)")
private int associatedDCount;
...
}
You could use the Critria api to make it return a DTO instead of an Entity.
In your criteria query use a Projection to select only the column you need.
ProjectionList properties = Projections.projectionList();
properties.add(Projections.property("id").as("id"));
properties.add(Projections.property("name").as("name"));
properties.add(Projections.property("lazyField").as("lazyField"));
criteria.setProjection(properties);
criteria.setResultTransformer(new AliasToBeanResultTransformer(MyEntityDTO.class));
That way the select query will only contains the fields you ask, whatever the mapping EAGER or LAZY.
You can try to have a look at Hibernate's fetch profiles https://docs.jboss.org/hibernate/orm/4.2/manual/en-US/html/ch20.html#performance-fetching-profiles.
You can for example annotate an entity like that
#Entity
#FetchProfile(name = "country_states", fetchOverrides = {
#FetchProfile.FetchOverride(entity = Country.class, association = "states", mode = FetchMode.JOIN)
})
public class Country implements Serializable {...
and activate the JOIN mode when querying, like this:
session=getSession();
session.beginTransaction();
//enable fetch profile for EAGER fetching
session.enableFetchProfile("country_states");
As shown in http://www.concretepage.com/hibernate/fetchprofile_hibernate_annotation
It turns out it's not hard to pull this off without having to resort to bytecode instrumentation.
Create a "formula" entity mapped to the same table:
#Entity
#Table("A")
public class ACounts {
#Id
private Long id;
#Formula("(select count(*) from entity_D_table where ...)")
private int dCount;
public int getDCount() {
return dCount;
}
}
Then in your parent entity, A, use #ManyToOne to relate lazily to this "formula" entity:
#Entity
public class A {
#Id
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id", nullable = false, insertable = false, updatable = false)
private ACounts counts;
public ACounts getCounts() {
return counts;
}
...
}
Now the count query will only be issued when the count is requested (i.e. it's lazy!):
A a = ...
// lazily invoke count query now:
a.getCounts().getDCount()
ref: https://stackoverflow.com/a/55581854/225217

JPA Hibernate unexpectedly fetches records of #OneToOne mapped entity, should I change mapping to #ManyToOne or do something else?

I have an entity with #OneToOne mapped subentity:
#Entity #Table
public class BaseEntity {
#Id
private String key;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private InnerEntity inner;
}
#Entity #Table
public class InnerEntity {
private String data;
}
It was working perfectly on persist and merge operations until I decided to fetch all records in a named query (SELECT e FROM BaseEntity e). Problems are that after calling it, Hibernate fetches all records from BaseEntity and then executes distinct queries for each InnerEntity. Because table is quite big it takes much time and takes much memory.
First, I started to investigate if getInner() is called anywhere in running code. Then I tried to change fetchType to EAGER to check if Hibernate it's going to fetch it all with one query. It didn't. Another try was to change mapping to #ManyToOne. Doing this I've added updatable/insertable=false to #JoinColumn annotation. Fetching started to work perfectly - one SELECT without any JOIN (I changed EAGER back to LAZY), but problems with updating begun. Hibernate expects InnerEntity to be persisted first, but there's no property with primary key. Of course I can do this and explicity persist InnerEntity calling setKey() first, but I would rather solve this without this.
Any ideas?
If you want inner field to be loaded on demand and your relation is #OnToOneyou can try this
#OneToOne(fetch = FetchType.LAZY, optional = false)
When using HQL hibernate doesn't consider the annotations, so you should tell it how to work.
In your case you should right the HQL like this:
SELECT e FROM BaseEntity as e left join fetch e.inner

Hiberate query not returning newly added records from same transaction

My Entity is like
#Entity
#Table(name = "Item")
public class Item implements Serializable {
#Id
#GeneratedValue
#Column(name = "ID")
private long id;
#JoinColumn(name = "PARENT_ID")
#JsonIgnore
private Item parent;
}
I am doing 3 things in a single transaction
Persist some items using EntityManager
hibernate query "from item where id in newIdList"
hibernate query "from item where parent=parentid"
In First step after persisting new items I do entityManager.flush(); and flush mode here is commit.
In second step I do given hibernate query. Here I get the proper result but in third step when I do hibernate query it returns me the results. But this result does not contain the newly persisted query.
I think the problem is due to parentId condition. As per requirements I cannot change the condition. Is there any way we can solve this problem?
#JoinColumn does not establish a relationship from Item > Parent.
You need to annotate this relationship with the relevant association mapping, #OneToOne, #ManyToMany, #OneToMany, #ManyToOne etc.
Try 3rd step after commit... This is not direct solution to your problem but just give a try..

How to cascade delete entities with unidirectional 'ManyToOne' relationship with JPA

I have two entity classes 'User' and 'Department' with unidirectional 'ManyToOne' relationship as below.
public class User{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "DEPARTMENT_ID", nullable = true)
private Department department;
}
public class Department{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
If I want to delete some users and cascade remove the referenced departments if not any user references the department, is there any features of JPA to use please?
You can use CascadeType.DELETE, however this annotation only applies to the objects in the EntityManager, not the database. You want to be sure that ON DELETE CASCADE is added to the database constraint. To verify, you can configure JPA to generate a ddl file. Take a look at the ddl file, you'll notice that ON DELETE CASCADE is not part of the constraint. Add ON DELETE CASCADE to actual SQL in the ddl file, then update your database schema from the ddl. This will fix your problem .
This link shows how to use ON DELETE CASCADE on for CONSTRAINT in MySQL. You do this on the constraint. You can also do it in a CREATE TABLE or ALTER TABLE statement. It's likely that JPA creates the constraint in an ALTER TABLE statement. Simply add ON DELETE CASCADE to that statement.
Note that some JPA implementors do provide a means for this functionality.
Hibernate does supply this functionality using the #OnDelete annotation.
You can tell hibernate to delete 'orphan' entries with;
#Cascade({ org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })

How to retrieve nested JPA entities in a single query

I am trying to retrieve entities using eclipselink JPA and am looking for a way to reduce the number of queries run to retrieve a single entity. I believe I should be using the #JoinFetch annotation to retrieve sub-entities in the same query as the main entity. This works fine for a single level of join, but not for multiple levels.
In the example below, EntityA contains a collection of EntityB which contains an EntityC. When I retrieve EntityA, I want a single query to return all 3 sets of entity data. In reality it generates 2 queries, 1 joining EntityA and EntityB and then a separate query joining EntityB and EntityC.
Is it possible to combine this into one query?
class EntityA {
#OneToMany(mappedBy = "entityALink", fetch = FetchType.EAGER)
#JoinFetch
private Collection<EntityB> entityBs;
}
class EntityB {
#JoinColumn(name = "X", referencedColumnName = "Y")
#ManyToOne(optional = false, fetch = FetchType.EAGER)
private EntityA entityALink;
#JoinColumn(name = "A", referencedColumnName = "B")
#ManyToOne(optional = false, fetch = FetchType.EAGER)
#JoinFetch
private EntityC entityCLink;
}
class EntityC {
#Id
#Basic(optional = false)
#Column(name = "SomeColumn")
private String someField
}
If you need reduce number of queries, you may using lazy initialization - FetchType.LAZY instead of FetchType.EAGER - in this way jpa get data from databases when need. But you must remember, this is not working when entity is disconnected from manager. So if you send this entity to other servers in serialize the form (ex. in multi-level application) you must again connected this entity with manager. If you application runs in one server, then you don't have this problem.
Summing up is not the exact answer to your question, but maybe helpful for optimize this code.
Exact answer for you question:
You may using named queries, but then query is parse to sql native query, and you don't sure that this working as you want. But maybe you may using native query method?
em.createNativeQuery("SELECT ... your queries")
For this purpose, please read about using #SqlResultSetMapping annotation to configure result entity class...
First write a query to get EntityA.
EntityA entity = <your Query> ;
then call
Collection<EntityB> entityB = entity.getEntityBs();
for(EntityB eachB : entityB){
EntityC entityCLink = eachB.getEntityCLink();
}
Note: Create setter & getters in each entity.

Categories

Resources