Efficiently determining the IDs of entities referenced via OneToMany relationship - java

Let's say I have a Hibernate entity that declares a OneToMany relationship to a different entity:
#Entity
public class SomeEntity {
#OneToMany(fetch = FetchType.LAZY)
private List<OtherEntity> otherEntities = new LinkedList<OtherEntity>();
[...]
}
When mapping SomeEntity to the corresponding DTO, all I need are the IDs that identify OtherEntity as primary key (i.e., I am not actually interested in OtherEntity instances).
Does Hibernate support this pattern, i.e., only retrieving the IDs of entities referenced via a OneToMany relationship?
I cannot influence how SomeEntity is retrieved (i.e., I have an existing SomeEntity instance retrieved within te scope of the current Hibernate session), but let's assume that lazy loading has not yet taken place, so just retrieving the child objects' IDs (rather than the complete objects) would actually yield a performance benefit.

Well, if you only need the entities' ids and you want to be economical about it, when you get those entities from the database you should state in your query that you only want to get the ids of each entry, using projections, something like :
SELECT Entity.id as entity FROM Entity WHERE ...
This will return an array of objects of the same type as Entity's id field type.

You can try obtaining the primary key without accessing the entity itself (without otherEntities.get(0).getId()). To do this you can use the PersistenceUnitUtil class:
PersistenceUnitUtil#getIdentifier(yourEntity)
The PersistenceUnitUtil can be obtained from the EntityManagerFactory. So it could be something like:
EntityManager em = ...
PersistenceUnitUtil = em.getEntityManagerFactory().getPersistenceUnitUtil();
Unfortunately, I'm not aware if this will prevent the entity loading from occuring. However, just accessing the otherEntities collection or even obtaining references to each entity will not make the instance to be loaded; you need to invoke a method on the fetched entity in order to be sure it will be loaded.
You also might consider creating a #NamedQuery and return only the OtherEntity ID's.
HTH!

From hibernate reference, section 2.2.2.1.
http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/entity.html#entity-mapping-property
Declare your columns as lazy initialized:
#Basic(fetch = FetchType.LAZY)
private String getYourProperty() {
}
You also need to disable proxies for your entity class and byte instrument it. There is an example here:
Making a OneToOne-relation lazy

You can use the below HQL as told in the documentation to establish this.
session.createQuery(select new OtherEntity(oe.id) OtherEntity oe
where oe.parentSomeEntity.someId = :someId).list();//also set someId.
Add a constructor in OtherEntity to set the id also there should be a mapping to SomeEntity in OtherEntity.
This HQL will give you a List<OtherEntity> with only id set in the bean.

Related

Does FetchType.LAZY fetch anything in list or it's empty

Let's say i have a association as ,
class Department{
......
#OneToMany(fetch = FetchType.LAZY)
private List<Employee> employees;
}
Now when i fetch Department, will it fetch anything in employees list or will it be completely empty.
Will identifiers for employee object be loaded in list like say i have employee object attributes as below:-
Employee{
id
name
doj
....
}
Lazy object like {
id -> 111
name -> null
doj -> null
}
Now when i initialize employee object or access it's properties using getters, then the object will be loaded from database using id as an identifier...??
Instead of the real collection class (e.g. ArrayList) a different List implementation is injected into your field (PersistentList). Depending on the calls on that collection and the lazy strategy it will do different things:
In case lazy="lazy" the call to any of the collections methods would get the collection fully loaded
If lazy="extra", then calls to some functions would trigger SQL without loading the collection. E.g. list.size() would trigger select count.... While getting the 1st element would select only that element. This may be suitable for large collections. Note, that this behaviour may also depend on the collection type - unordered collections will load all elements anyway.
Lazy fetch type, Hibernate won’t load the relationships for that particular object instance.
FetchType.LAZY = Doesn’t load the relationships unless explicitly “asked for” via getters
FetchType.EAGER = Loads ALL relationships
In your case It won't load Employee List from database unless you explicitly fire query for it, Because you have set fetch type ( fetch = FetchType.LAZY ). If fetch type was ( fetch = FetchType.EAGER ) then It would explicitly fire a select query for Employee list. In that object you would get all employee property eg name, doj.
the object will be loaded from database using id as an identifier...??
Department{
#OneToMany(fetch = FetchType.EAGER,,mappedBy = "department")
private List<Employee> employees;
}
In Emplooyee.... You need to mapped it by reference of department object.
eg:
Employee{
// Reference of department.
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "departmentid", nullable = false)
private Department department;
}
This way it will become bidirectional. now Hibernate will fire query by reference (id in native sql) .
If you want to be loaded lazily the set Fetch mode FetchType.LAZY in both mapping....
In your case until you explicitly access Department.employees (through a getter or any other means) It will not load the Employee entities. There will be a Proxy for this. It will be initialized once at the first call to access this employees collection.
How Lazy Loading Works in Hibernate
The simplest way that Hibernate can apply lazy load behavior upon your entities and associations is by providing a proxy implementation of them. Hibernate intercepts calls to the entity by substituting a proxy for it derived from the entity’s class. Where the requested information is missing, it will be loaded from the database before control is ceded to the parent entity’s implementation.
Please note that when the association is represented as a collection class, then a wrapper (essentially a proxy for the collection, rather than for the entities that it contains) is created and substituted for the original collection. When you access this collection proxy then what you get inside returned proxy collection are not proxy entities; rather they are actual entities. You need not to put much pressure on understanding this concept because on runtime it hardly matters.
Please refer this for more information:
http://howtodoinjava.com/hibernate/lazy-loading-in-hibernate/
Also enable hibernate.sql.show=true so that you can see what queries are being fired when you are trying to fetch the collections.

JPA OneToOne Lazy relation

I have 2 entities: User and UserProfile that have a bidirectional #OneToOne relationship between them.
Due to some old DB Design the UserProfile is the owner (i have the column user_id in users_profiles table)
The relationship is Lazy as I have fetchType Lazy and optional = false.
Everything works as expected, I mean when I load an UserProfile it does not automatically loads the User also. I guess this is perfectly normal as I load from the owner side.
My problem is that if I load a User (owned side) it loads automatically the UserProfile although the relationship is lazy.
I mean: Is this normal that when I load an entity from the owned side to load the owner entity also ?
#Entity
#Table(name = "users")
public class User extends BaseEntity implements Serializable {
#OneToOne(mappedBy = "user", optional=false, fetch = FetchType.LAZY)
private UserProfile profile;
// .................rest of entity
}
#Entity
#Table(name="users_profiles")
public class UserProfile extends BaseEntity implements Serializable {
#OneToOne(optional=false, fetch = FetchType.LAZY)
#JoinColumn(name="user_id")
private User user;
// ... rest of entity here
}
The way that I test this is by loading the User entity with EntityManager method find(id).
I have noticed that when the relation is not lazy I have only one query with a join inside. If I put the current setup I have two individual queries: 1 for user and the other one for profile.
It is important to realize, that lazy loading behavior is not enforced by JPA specification, it is only recommended. It is up to hibernate as the implementation to choose if it is supported and under which conditions.
Hibernate usually does its best to load the data lazily when requested, but in case of one-to-one mapping, when the relationship is stored in another table (the primary key is in table users_profiles), it needs to query also the second table to retrieve the primary key to create a proxy object. In fact, it does not retrieve only id, but full row and creates the UserProfile in eager way, because it costs almost nothing to fetch additinal data when the table needs to be joined in any case.
The answer for Hibernate: one-to-one lazy loading, optional = false suggests that making relationship non-optional should make it lazy, but I doubt it is true. Hibernate would need to create a proxy without an ID, which is doubtfully correct in all cases. One case where that would fail is when you remove the proxy object from the collection in parent entity, and then try to read its data - as the proxy itself does not carry enough information to retrieve data lazily, it is not possible without connection to parent entity.
This is normal, by default, hibernate creates run-time proxies. It loads the objects as a proxy unless a fetch mode is specified or set to false.
for example, load() always retrieves proxy objects. If called more than once within same session, it reads data from persistent context cache. That's because once the object is loaded in cache, the next subsequent calls perform repeatable read.

Is it possible to temporarily disable cascading for a Hibernate entity?

Given a Hibernate/JPA entity with cascading set to ALL for a related entity:
#Entity
public class Entity {
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "entity")
private Set<RelatedEntities> relatedEntities;
}
Is it possible to temporarily turn off the cascading, e.g. to allow Entity to be persisted without also persisting its relatedEntities?
No, it is not possible to do it, and at least according to my modest opinion, it would not be a good thing to do so either. When other developers look at the mappings and the code that does persist/merge/delete... they would expect the cascades to be applied and introduce the unexpected behavior if they oversee that the cascades are temporarily disabled somewhere else for the code they are about to change.
However, you can map to the same table a new entity class which does not have the fields that are cascaded. Then just use that entity in situations in which you don't want the cascades to be applied.
You can't temporarily disable cascading (to my knowledge, at least), but since you use Hibernate you can insert new entity using HQL
String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
int createdEntities = s.createQuery( hqlInsert ).executeUpdate();
There is always a "manual" solution where you remember relatedEntities in a variable for later use, and set null value as its value on Entity instance before persisting it.

How to avoid loading lazy bidirectional relationships with MOXy?

My question is a follow up to this comment.
I'm mixing JPA and JAXB (MOXy) annotations on the same class, which works fine most of the time. As described in the linked thread, #XmlInverseReference prevents cycle exceptions when bidirectional relationships are marshalled. But in order to detect the cycle, MOXy has to inspect the back reference of the linked entity, which leads to extra SQL SELECTs if a lazy relation needs to be populated.
To illustrate the problem in detail, consider this made-up example:
#Entity
#Access( AccessType.FIELD )
#XmlRootElement
#XmlAccessorType( XmlAccessType.FIELD )
public class Phone {
#ManyToOne
#JoinColumn( name = "employeeID" )
#XmlElement( name = "employee" )
#XmlInverseReference( mappedBy = "phones" )
private Employee employee;
private String number;
[...]
}
#Entity
#Access( AccessType.FIELD )
#XmlRootElement
#XmlAccessorType( XmlAccessType.FIELD )
public class Employee {
#OneToMany( mappedBy = "employee" )
#XmlElementWrapper( name = "phones" )
#XmlElement( name = "phone" )
#XmlInverseReference( mappedBy = "employee" )
private List<Phone> phones;
private String name;
[...]
}
Now I'd run queries on Phones with a JAX-RS method like this (using an underlying EJB):
#Inject
private PhoneService phoneService;
#GET
#Path( "/phones" )
public List<Phone> getPhonesByNumber( #QueryParam( "number" ) String number ) {
List<Phone> result = phoneService.getPhonesByNumber( number );
return result;
}
What happens is this: The JPQL query within the PhoneService EJB triggers an SQL SELECT on the Phone table (filtered by the number), and if I use a JOIN FETCH query, I can get the associated Employee with the same single SELECT statement.
When the JAX-RS method returns, the JAXB marshalling kicks in, which leads to an additional SQL SELECT: this one selects all Phones whose employeeID points to the Employee who is associated with the originally requested Phones. So the lazy relationship from Employee to Phone is resolved now, presumably because MOXy must be able to determine if the original Phone is contained in the collection.
I've tried using JPA property access and JAXB field access for the phones field, as suggested in the other thread, to no avail. I've also tried nulling out the phones field in the linked Employee instance after retrieving the result from the EJB, i.e. when my entities are detached already, but this led to an immediate SQL SELECT again (it seems like EclipseLink will do this whenever any manipulation is done to an IndirectList?). The only workaround solution I could find is to use MOXy #XmlNamedObjectGraphs with a subgraph that excludes the phones field. But that's not practical, especially if the involved entities have many attributes.
As I may need to query in the other direction too, e.g. employees by name with their associated phones, I can't just mark phones as #XmlTransient.
Does anyone have an elegant solution to suppress those extra SQL statements?
From my experience the easiest way to accomplish what you are trying is to detach all the entity classes before you pass them to a presentation layer like a JAX-RS rest api. You can even use the #OneToMany(mappedBy = "employee", cascade = CascadeType.DETACH) and EntityManager.detach() to detach your phone class and subsequently detach your employee class or vice versa. This will ensure that during the marshaling of your entity, Jax-RS doesn't trigger any SELECT statements that you wouldn't normally want.
I always detach model entities before I pass them to the presentation layer so that they can interact with the model classes how they please without affecting performance or the database.
I collected some information about EclipseLink from these three threads. Important bits:
Detached Objects get the connection need to traverse the LAZY relationship from the EntityManagerFactory and will able able to use it as long as the EntityManagerFactory is open. The connection used in not the transactional one and when you want to use the entity in a transaction it will have to be properly merged.
 
This is a special feature of TopLink's implementation where the detached instances created from non-tx reads still have access in their proxies to retrieve additional dettached instances. If the object was detached through serialization this would not be possible.
 
If you would like TopLink Essentials to not process lazy relationships after the EM has closed I would recommend filing an enhancement request in GlassFish.
I couldn't find such an enhancement request though, let alone an implemented possibility to disable this feature (on a case-by-case basis).
There are five possible workarounds I could think of, each with its own drawbacks:
Just don't mix JAXB and JPA annotations on the same class: use a different set of additionatlly instantiated JAXB classes instead and perform explicit mapping between the two views. This could be a little expensive if lots of entities are returned from a query.
Like I mentioned in my question, use MOXy's (named) object graph feature to exclude (relationship) fields from being traversed.
Use a JAXB Marshaller.Listener to exclude all uninstantiated IndirectContainers.
Since serialization is supposed to break this EclipseLink feature for detached entities, serialize them before marshalling them. Seems awkward and even more expensive though.
This comes closest to emulating turning off the feature, but also looks hackish: access the wrapping IndirectContainer and its contained ValueHolderInterface and set them to null. Sample code:
(...)
import org.eclipse.persistence.indirection.IndirectContainer;
// entities must already be detached here, otherwise SQL UPDATEs will be triggered!
Employee e = phone.getEmployee();
IndirectContainer container = (IndirectContainer) e.getPhones();
container.setValueHolder( null );
e.setPhones( null );

Initializing a transient attribute of a JPA entity during CriteriaQuery

I'm wondering if it is possible to initialize a transient attribute of an entity during a criteria query.
Example
#Entity
public SampleEntity{
#Id
private long id;
[more attributes]
#Transient
private String someTransientString;
[getters and setters]
}
Now I want to compose a CriteriaQuery that loads all SampleEntitys and automatically sets someTransientString to imamightlyfinestring. I have something like the following SQL in mind:
SELECT ID AS ID, [..], 'imamightilyfinestring' AS SOME_TRANSIENT_STRING FROM SAMPLE_ENTITY
I of course know that I can simply iterate the resulting collection and manually set the attribute, but I'm wondering if there is a way to do it within JPA2.
Thanks :)
No, you cannot do it in query.
If you can figure out value for someTransientString outside of query, you can use PostLoad callback (excerpt from JPA 2.0 specification):
The PostLoad method for an entity is invoked after the entity has been
loaded into the current persistence context from the database or after
the refresh operation has been applied to it. The PostLoad method is
invoked before a query result is returned or accessed or before an
association is traversed.
Just add following to your entity:
#PostLoad
protected void initSomeTransientString() {
//Likely some more complex logic to figure out value,
//if it is this simple, just set it directly in field declaration.
someTransientString = "imamightilyfinestring";
}
You can also initialize your transients in the default (i.e., no argument) constructor.
You can see that this is the strategy used, for example, in EclipseLink (read last comment in the following link):
https://bugs.eclipse.org/bugs/show_bug.cgi?id=292385

Categories

Resources