I need to set the fetch mode on my hibernate mappings to be eager in some cases, and lazy in others. I have my default (set through the hbm file) as lazy="true". How do I override this setting in code? MyClass has a set defined of type MyClass2 for which I want to set the FetchMode to EAGER.
Currently, I have something like:
Session s = HibernateUtil.getSessionFactory().openSession();
MyClass c = (MyClass)session.get(MyClass.class, myClassID);
You could try something like this: (code off the top of my head)
Criteria crit = session.createCriteria(MyClass.class);
crit.add(Restrictions.eq("id", myClassId));
crit.setFetchMode("myProperty", FetchMode.EAGER);
MyClass myThingy = (MyClass)crit.uniqueResult();
I believe that FetchMode.JOIN or FetchMode.SELECT should be used instead of FetchMode.EAGER, though.
If you're not using Criteria there's also the JOIN FETCH keyword that will eagerly load the association specified by the join.
session.createQuery("select p from Parent p join fetch p.children c")
There is a static initialize(Object) method in the Hibernate main class. You could use that to force loading of your collection:
MyClass c = (MyClass)session.get(MyClass.class, myClassID);
Hibernate.initialize(c.getMySetOfMyClass2());
However, a default value of lazy fetching is just that: a default value. You probably want to override the laziness in the mapping for your particular Set.
Related
I would like someone to explain me why Hibernate is making one extra SQL statement in my straight forward case. Basically i have this object:
#Entity
class ConfigurationTechLog (
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long?,
val configurationId: Long,
val type: String,
val value: String?
) {
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "configurationId", insertable = false, updatable = false)
val configuration: Configuration? = null
}
So as you can see, nothing special there. And when i execute this query :
#Query(value = "SELECT c FROM ConfigurationTechLog c where c.id = 10")
fun findById10() : Set<ConfigurationTechLog>
In my console i see this:
Hibernate:
/* SELECT
c
FROM
ConfigurationTechLog c
where
c.id = 10 */ select
configurat0_.id as id1_2_,
configurat0_.configuration_id as configur2_2_,
configurat0_.type as type3_2_,
configurat0_.value as value4_2_
from
configuration_tech_log configurat0_
where
configurat0_.id=10
Hibernate:
select
configurat0_.id as id1_0_0_,
configurat0_.branch_code as branch_c2_0_0_,
configurat0_.country as country3_0_0_,
configurat0_.merchant_name as merchant4_0_0_,
configurat0_.merchant_number as merchant5_0_0_,
configurat0_.org as org6_0_0_,
configurat0_.outlet_id as outlet_i7_0_0_,
configurat0_.platform_merchant_account_name as platform8_0_0_,
configurat0_.store_type as store_ty9_0_0_,
configurat0_.terminal_count as termina10_0_0_
from
configuration configurat0_
where
configurat0_.id=?
Can someone please explain me, what is happening here ? From where this second query is coming from ?
I assume you are using Kotlin data class. The kotlin data class would generate toString, hashCode and equals methods utilizing all the member fields. So if you are using the returned values in your code in a way that results in calling of any of these method may cause this issue.
BTW, using Kotlin data claases is against the basic requirements for JPA Entity as data classes are final classes having final members.
In order to make an association lazy, Hibernate has to create a proxy instance instead of using the real object, i.e. it needs to create an instance of dynamically generated subclass of the association class.
Since in Kotlin all classes are final by default, Hibernate cannot subclass it so it has to create the real object and initialize the association right away. In order to verify this, try declaring the Configuration class as open.
To solve this without the need to explicitly declare all entities open, it is easier to do it via the kotlin-allopen compiler plugin.
This Link can be useful for understand what kind (common) problem is that N + 1 Problem
Let me give you an example:
I have three Courses and each of them have Students related.
I would like to perform a "SELECT * FROM Courses". This is the first query that i want (+ 1) but Hibernate in background, in order to get details about Students for each Course that select * given to us, will execute three more queries, one for each course (N, there are three Course coming from select *). In the end i will see 4 queries into Hibernate Logs
Considering the example before, probably this is what happen in your case: You execute the first query that you want, getting Configuration Id = 10 but after, Hibernate, will take the entity related to this Configuration, then a new query is executed to get this related entity.
This problem should be related in specific to Relationships (of course) and LAZY Fetch. This is not a problem that you have caused but is an Hibernate Performance Issue with LAZY Fetch, consider it a sort of bug or a default behaviour
To solve this kind of problem, i don't know if will be in your case but ... i know three ways:
EAGER Fetch Type (but not the most good option)
Query with JOIN FETCH between Courses and Students
Creating EntityGraph Object that rappresent the Course and SubGraph that rappresent Students and is added to EntityGraph
Looking at your question, it seems like an expected behavior.
Since you've set up configuration to fetch lazily with #ManyToOne(fetch = FetchType.LAZY), the first sql just queries the other variables. When you try to access the configuration object, hibernate queries the db again. That's what lazy fetching is. If you'd like Hibernate to use joins and fetch all values at once, try setting #ManyToOne(fetch = FetchType.EAGER).
The Hibernate documentation gives some information on Hibernate filters, and talks about HQL and load fetching in that section. But it never explicitly mentions anything about using them in conjunction with criteria queries. Would something like the following be expected to work? Assume Hibernate 4.3.
Entity definition
#Entity
#FilterDef(name="myFilter", parameters=#ParamDef( name="myParam", type="integer" ) )
#Filters( {
#Filter(name="myFilter", condition="myField = :myParam")
} )
// assume MyType contains some mapped field named "myField"
public class MyType { ... }
Query code
sess.enableFilter("myFilter").setParameter("myParam", 1);
Criteria crit = sess.createCriteria(MyType.class);
//set some other restrictions on crit
List items = crit.list();
Will items have had myFilter applied?
Yes, it should work for Criteria too. The filters are applied on the Session level, no matter what you use to load the entities (e.g., HQL, entityManager.find(), Criteria API).
Given entity classes:
class A { String name ... }
class B { #OneToOne A a = ... ; String gender; ... }
I am able to create a hibernate criteria and query it and all of it's associations.
However, B is not referenced from A at all. It's populated in a separate fashion and I would like to keep it as such.
Is there a way to perform a hibernate criteria query on A, and say, where this also is in B's relation, or where it is in B's relation and some field in B is this and that?
Here is some non-working pseudo code:
criteria(A.class).add(
Restrictions.eq("name", "something")
).createAlias(
B.class, "..."
).add(
Restrictions.eq("gender", "Female")
);
Note, I would prefer not having to create a collection on A containing B's and using addAll.
I might consider adding a dead reference though, meaning something that is never intended to be accessed or updated but needed for hibernate to pull this off. It could be something like saying that this is maintained by another table.
You can use a subquery, as follows:
// create a 'DetachedCriteria' query for all the items in 'B'
DetachedCriteria dc = DetachedCriteria.forClass(B.class).setProjection(
Projections.projectionList().add(Projections.property("propertyInB"))
);
// then: search for A.id by adding 'dc' as asubquery:
session.createCriteria(A.class).add(
Subqueries.propertiesIn( new String[]{"propertyInA"}, dc)
).list();
This is roughly equivalent to: 'SELECT * FROM A a WHERE a.id in(SELECT id FROM B).
I hope my SQL is valid ;) .
See hibernate documentation:
https://docs.jboss.org/hibernate/orm/3.3/reference/en-US/html/querycriteria.html
I am working on a new module in an existing project. The project already has a user table, a pojo and a corresponding mapping file. The problem is that they are fetching all the properties eagerly by mentioning lazy="false". But, in my module, I am doing lot of read & write in a single request, so I don't want to fetch eagerly. What I want to know is that, is it possible to create an another mapping file for the same table & same pojo to load all the properties lazily? I have tried by assigning different entity-name for the mapping files, but while deploying, I am getting the error "Repeated column in mapping for entity".
I saw this answer, but it says "do not map child", then how will I get the proxies?
That's one major drawback for using EAGER fetching as the default strategy. Usually you'd have a LAZY children collection that you can eagerly fetch on a HQL query basis.
What's worth mentioning is that HQL/Criteria queries overrule the default fetch strategy (the one given by your entity mappings) so that you can explicitly specify what to fetch.
For Criteria queries, you might give a try to Criteria.setFetchMode FetchMode.LAZY, although it's deprecated.
Another way to overrule the EAGER fetching is to use a javax.persistence.fetchgraph. This way, you can specify what you want to fetch and all the EAGER fetching properties that were not included in the Entity Graph are going to be fetched lazily.
I think, your question is to not to load associated entity , egarly ?
for this when :
FetchType.LAZY = Doesn’t load the relationships unless explicitly called via getter.
FetchType.EAGER = Loads ALL relationships default
In your case , if my understanding is correct then use
lazy="false"
fetch="select" so it will select on demand via getter.
check this url , it will give more clear idea :
Hibernate XML Mapping: Lazy False or Fetch Select?
A Short Primer On Fetching Strategies
The point Hibernate mapping setting lazy = 'false' is making, is that you can map the same table twice if you use two different java classes. In your case, if you know for a fact that setting lazy="true" will suffice:
create empty subclass of your pojo
map your subclass the way you want to the same user table (for examply copy-pasting the existing mapping and changing lazy property value)
in the module you create only use the subclass
Like Vlad Mihalcea said, use Criteria API it will work. I've tried it :)
Criteria c = session.createCriteria(YourHibernateClass.class);
c.setFetchMode(urLazyPropName,FetchMode.LAZY);
Atleast this you can do with out changing existing hbm files
Some other approaches mentioned we used based on JPA inheritance (#MappedSuperclass) and/or database views and examplified with the following pseudo code (it works both ways for EAGER->LAZY or LAZY->EAGER scenarios):
#Table( name = "table_x" )
#Entity
class DaoX { #...( fetch = FetchType.EAGER ) refY ; /* ... other stuff ... */ }
our DaoX used e.g. in other code like this:
DaoX x ;
#Entity
class DaoZ { DaoX x ; }
via inheritance
so if we would like to have it lazily loaded we could pimp the inheritance hierarchy up like this with only minimal additional code:
#Table( name = "table_x" )
#MappedSuperclass
class abstract BaseDaoX { /* ... other stuff ... */ }
#Entity
class DaoX extends BaseDaoX { #...( fetch = FetchType.EAGER ) refY ; }
#Entity
class DaoXLazy extends BaseDaoX { #...( fetch = FetchType.LAZY ) refY ; }
our DaoX usages should be substituted by BaseDaoX where possible (no direct JPA mapping)
BaseDaoX x ;
#Entity
class DaoZ { DaoX x ; }
#Entity
class DaoZLazy { DaoXLazy x ; }
so you can use DaoXLazy or DaoZLazy for your desired scenarios.
via views (in LAZY->EAGER scenarios)
(if you can change your current EAGER to LAZY which is generally more appropriate)
you could just map your (maybe deeply nested) lazy stuff with minimal load e.g. like this ( we like to load prop_f1 and prop_b1 here )
-- db view:
create or replace view view_x_eager as
select
tx.*,
f.prop_f1,
b.prop_b1
from table_x tx
-- assuming tx:f ~ 1:1 and f:b ~ 1:1 for simplicity here:
left outer join table_foo f on ( f.id = tx.foo_id )
left outer join table_bar b on ( b.id = f.bar_id )
#Table( name = "view_x_eager" )
class DaoXEager extends BaseDaoX {
#...( fetch = FetchType.EAGER ) refY ;
String prop_f1 ;
String prop_b1 ;
}
Using JPA 2.0. It seems that by default (no explicit fetch), #OneToOne(fetch = FetchType.EAGER) fields are fetched in 1 + N queries, where N is the number of results containing an Entity that defines the relationship to a distinct related entity. Using the Criteria API, I might try to avoid that as follows:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<MyEntity> query = builder.createQuery(MyEntity.class);
Root<MyEntity> root = query.from(MyEntity.class);
Join<MyEntity, RelatedEntity> join = root.join("relatedEntity");
root.fetch("relatedEntity");
query.select(root).where(builder.equals(join.get("id"), 3));
The above should ideally be equivalent to the following:
SELECT m FROM MyEntity m JOIN FETCH myEntity.relatedEntity r WHERE r.id = 3
However, the criteria query results in the root table needlessly being joined to the related entity table twice; once for the fetch, and once for the where predicate. The resulting SQL looks something like this:
SELECT myentity.id, myentity.attribute, relatedentity2.id, relatedentity2.attribute
FROM my_entity myentity
INNER JOIN related_entity relatedentity1 ON myentity.related_id = relatedentity1.id
INNER JOIN related_entity relatedentity2 ON myentity.related_id = relatedentity2.id
WHERE relatedentity1.id = 3
Alas, if I only do the fetch, then I don't have an expression to use in the where clause.
Am I missing something, or is this a limitation of the Criteria API? If it's the latter, is this being remedied in JPA 2.1 or are there any vendor-specific enhancements?
Otherwise, it seems better to just give up compile-time type checking (I realize my example doesn't use the metamodel) and use dynamic JPQL TypedQueries.
Instead of root.join(...) you can use root.fetch(...) which returns Fetch<> object.
Fetch<> is descendant of Join<> but it can be used in similar manner.
You just need to cast Fetch<> to Join<> it should work for EclipseLink and Hibernate
...
Join<MyEntity, RelatedEntity> join = (Join<MyEntity, RelatedEntity>)root.fetch("relatedEntity");
...
Starting with JPA 2.1 you can use dynamic entity graphs for this. Remove your fetch and specify an entity graph as follows:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<MyEntity> query = builder.createQuery(MyEntity.class);
Root<MyEntity> root = query.from(MyEntity.class);
Join<MyEntity, RelatedEntity> join = root.join("relatedEntity");
query.select(root).where(builder.equal(join.get("id"), 3));
EntityGraph<MyEntity> fetchGraph = entityManager.createEntityGraph(MyEntity.class);
fetchGraph.addSubgraph("relatedEntity");
entityManager.createQuery(query).setHint("javax.persistence.loadgraph", fetchGraph);
Using root.fetch() on EclipseLink will create a SQL with INNER JOIN because have 3 types and the default is INNER.
INNER, LEFT, RIGHT;
The suggestion is to use CreateQuery.
TypedQuery<T> typedQuery = entityManager.createQuery(query);
EDIT: You can Cast the root to From like this:
From<?, ?> join = (From<?, ?>) root.fetch("relatedEntity");