Here is my problem:
I have this class it has few #oneToMany collections
public class ActivePropertyList implements Serializable
{
#OneToMany
#JoinTable(name = "PropertyAttributeLink",
joinColumns =
#JoinColumn(name = "EANHotelID"),
inverseJoinColumns =
#JoinColumn(name = "AttributeID", referencedColumnName="AttributeID"))
private Collection<AttributeList> attributeList;
#OneToMany(fetch= FetchType.LAZY)
#JoinColumn(name="EANHotelID")
private Collection<Hotelimageslist> hotelimageslist;
#OneToMany(fetch= FetchType.LAZY)
#JoinColumn(name="EANHotelID")
private Collection<Roomtypelist> roomtypelist;
//Getters & Setters ...
When I access this object from XHTML it takes too long to generate as I use <ui:repeat value=#{controller.ActivePropertyList.attributeList}> ...
PropertyAttributeLink has more than 5Mil rows and Images has more than 4Mil rows but when i use simple SQL query innerJoin i takes no more than few ms to generate Lists.
I've tried using namedQuery on AttributeList using HQL query but as AttributeList has no reference to ActivePropertyList as it is unidirectional #oneToMany it throws error on doing so.
Is there a way to create HQL NamedQuery to access each list just once and store it in controller?
something like
public List<AttributeList> getAttributeListByHotelID(int hotelID){
Query q = session().createQuery("from AttributeList AL inner join PropertyAttributeLink PA where PA.hotelID=:hotelID");
return q.list();
}
but this method doesn't work as hql needs AttributeList to know about PropertyAttributeLink
Pointing the joins just make the atributtes available for where conditions and other stuff, you should use FETCH to make the relations eager and have it inmediatly, avoiding the lazy iniciators, something like
from AttributeList AL inner join FETCH PropertyAttributeLink PA where PA.hotelID=:hotelID
As you see isn't so hard, i hope that helps you, you can get more information, as always, in the docs HQL - Associations and joins
Related
I use the H2 database and spring boot for this task, I have two entities.
The First entity is:
public class e1{
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name = "t12",
joinColumns = { #JoinColumn(name = "e1_ID")},
inverseJoinColumns = { #JoinColumn(name = "e2_ID")})
private Set<Game> listSet1 = new HashSet<>();
}
The second entity is:
public class e2{
#ManyToMany(mappedBy = "listSet1")
private Set<Player> listSet2 = new HashSet<>();
}
After running the application we have a table called "t12".
Now, I have a question, I would like to know, How to join with this table I mean ( #JoinTable t12) without an entity from the e2 class.
I mean, when we are running an application Spring use this annotation #JoinTable to create a table for two entities. Now after created this table I want to join with them. but the table ( I mean #JoinTable name "X") has no entity. I want to use Inner join with this table and one of my entities.
Thank you all.
What exactly do you need to do?
Spring Data JPA lets you write native SQL queries using the #Query annotation with nativeQuery = true.
e.g. In your Repository interface, you can try something like:
#Query(
value = "SELECT * FROM e1 INNER JOIN t12 ON t12.e1_ID = e1.id WHERE t12.e2_ID = :e2ID",
nativeQuery=true)
Collection<e1> findAllByE2ID(Long e2ID);
Some useful resources:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#_native_queries
https://www.baeldung.com/spring-data-jpa-query#2-native
https://attacomsian.com/blog/spring-data-jpa-many-to-many-mapping
Please take a look at some naming conventions in java and Spring Boot:
https://www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html
https://www.baeldung.com/java-class-file-naming
https://www.baeldung.com/spring-data-jpa-custom-naming
I have a situation where i get List of entity in hiberate, But inside the main entity i have another entity list. The entities that i use:
EmpListBean.java
#NamedNativeQueries({
#NamedNativeQuery(
name = "EmpList",
//Actual query involves lot of joins
query = " SELECT ID , NAME FROM EMPLOYEE WHERE EMP_ID=:EMPID"
,resultClass = EmpListBean.class
)
})
#Entity
public class EmpListBean {
#Column(name = "ID")
private int id;
#Column(name = "NAME")
private String empName;
// This is the list i need to retreive
#ManyToOne
#Column(name="workList")
private List<WorkListBean> workList;
//Getters & Setters
}
WorkListBean.java
#NamedNativeQueries({
#NamedNativeQuery(
name = "WorkListBeanList",
query = " SELECT ID , NAME FROM Work_List WHERE EMP_ID=:EMPID"
,resultClass = WorkListBean.class
)
})
#Entity
public class WorkListBean {
#Column(name = "ID")
private int id;
#Column(name = "NAME")
private String workName;
//Getters & Setters
}
The DAO Layer
Query query = session.getNamedQuery("EmpList");
query.setParameter("EMPID", myObj.getEmpId());
List<EmpListBean> oEmpListBean = query.list();
When executing below DAO layer code I get the "workList" Object as empty , I know this can be achieved by iterating the EmpListBean separately and calling named query for WorkListBean separately , but since the data is huge it takes too much time when doing that way, So wanted to know if there is any way that we could fetch WorkListBean inside EmpList Bean. The two entities used here are only for reference , the actual query i use is complex and could not reveal in this forum and it involves lot of table joins, So kindly let me know how this can be possible in hibernate.
I know this can be achieved by iterating the EmpListBean separately and calling named query for WorkListBean separately , but since the data is huge it takes too much time when doing that way
I understand you want to merge the two queries to include the data for both entities, then?
Once the association between EmpListBean and WorkListBean is properly defined, i.e. you have:
class EmpListBean {
...
#OneToMany
#JoinColumn(name = "EMP_ID")
private List<WorkListBean> workList;
}
you should be able to use the following approach:
session.createNativeQuery(
"SELECT employee.* FROM EMPLOYEE employee JOIN Work_List wl JOIN ... WHERE wl.EMP_ID=emp.id AND employee.EMP_ID=:EMPID AND ..." )
.addEntity("employee", EmpListBean.class )
.addJoin( "wl", "employee.workList")
.setResultTransformer( Criteria.ROOT_ENTITY )
.list();
Not sure if it works for named native queries, though, you'll need to check.
As commented by #Javalerner you should use #OneTomany and add Eager Loading with fetch="FetchType.EAGER"
and remove the Column annotation
#OneToMany(fetch = FetchType.EAGER)
private List<WorkListBean> workList;
I like Abd's response if you're looking for a simple #OneToMany.
Rather than forcing the bean to always eager load, you may want to just incorporate this concept into your NamedNative query using the FETCH JOIN style. Using this approach you can craft queries that are EAGER on an object whose association is otherwise Lazy. In that case, Hibernate will only eagerly fetch via your query and generally be lazy, which is I think what most people want.
Feel free to google around. Here is a well written article that may start you off.
http://www.basilv.com/psd/blog/2008/improving-performance-via-eager-fetching-in-hibernate
Best of luck!
to get whatever FetchType.LAZY is, you have to use JOIN FETCH in the sentence. When you use JOIN you get all whatever FetchType.EAGER is but not whatever FetchType.LAZY is
EmpListBean.java
#OneToMany(fetch = FetchType.LAZY, mappedBy="empListBean")
private List<WorkListBean> workList;
WorkListBean.java
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="EMP_ID")
private EmpListBean empListBean;
DAO
String hql = "SELECT empListBean "
+ "FROM EmpListBean empListBean "
+ "JOIN FETCH empListBean.workList workList "
+ "WHERE empListBean.id = :EMPID";
Query q = sessionFactory.getCurrentSession().createQuery(hql);
q.setParameter("EMPID", myObj.getEmpId());
List<EmpListBean> empListBean = query.list();
I've been searching over the web to find out a solution for this. It seems nobody has the answer... I start thinking i'm in wrong way adressing the problem.
Let's see if i can explain easy.
Im developing a contract maintenance. (table: contrat_mercan). For the contract, we will select a category (table: categoria), each category has qualities (table: calidad) in relation 1 - N (relationship table categoria_calidad).
This qualities must have a value for each contract where the category is selected, so I created a table to cover this relationship: contrato_categoria_calidad.
#Entity
#Table(name = "contrato_categoria_calidad")
public class ContratoCategoriaCalidad implements Serializable{
// Constants --------------------------------------------------------
private static final long serialVersionUID = -1821053251702048097L;
// Fields -----------------------------------------------------------
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "CCC_ID")
private int id;
#Column(name = "CONTRAT_MERCAN_ID")
private int contratoId;
#Column(name = "CATEGORIA_ID")
private int categoriaId;
#Column(name = "CALIDAD_ID")
private int calidadId;
#Column(name = "VALOR")
private double valor;
.... getters/ setters
In this table I wanted to avoid having an Id, three fields are marked as FK in database and first attempts where with #JoinColumn in the three fields. But it does not worked for hibernate.
Anyway, now ContratoCategoriaCalidad is behaving okay as independent entity. But I will need to implement all maintenance, updates, deletes for each case manually... :(
What I really want, (and I think is a better practice) is a cascade when I saveOrUpdate the contract as the other entities do, but I don't find the way to make a List in contrat_mercan table.
This is working perfect for other relationships in same table:
#OneToOne
#JoinColumn(name="CONDICION")
private Condicion condicion;
#OneToMany (cascade = {CascadeType.ALL})
#JoinTable(
name="contrato_mercan_condicion",
joinColumns = #JoinColumn( name="CONTRATO_MERCAN_ID")
,inverseJoinColumns = #JoinColumn( name="CONDICION_ID")
)
private List<Condicion> condiciones;
But all my attempts to map this failed, what i want, is to have in my Java entity contrat_mercan a field like this:
private List<ContratoCategoriaCalidad> relacionContratoCategoriaCalidad;
not a real column in database, just representation of the relationship.
I found solutions to join multiple fields of the same table, here, and here, but not to make a relationship with 3 tables...
Any idea? Im doing something wrong? Maybe i must use intermediate table categoria_calidad to perform this?
Thanks!!
If you want to access a list of related ContratoCategoriaCalidad objects from Contrato entity you need to declare a relationship between those two entities using proper annotations.
In ContratoCategoriaCalidad class change field to:
#ManyToOne
#JoinColumn(name = "CONTRATO_ID")
private Contrato contrato;
In Contrato class add field:
#OneToMany(mappedBy = "contrato")
private List<ContratoCategoriaCalidad> relacionContratoCategoriaCalidad;
If you want to enable cascade updates and removals consider adding cascade = CascadeType.ALL and orphanRemoval = true attributes to #OneToMany annotation.
Hope this helps!
I expect to get only one sql query, but I run into the N+1 select trap. I don't really understand why. Here is is the problem in detail:
I have an entity "PlayerRef":
#Entity
#Table(name = "player_ref")
public class PlayerRef {
//constructor etc...
#OptimisticLock(excluded = true)
#LazyCollection(LazyCollectionOption.TRUE)
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "playerRefs")
public Set<Player> getPlayers() {
return players;
}
}
And a class Player:
#Entity
#Table(name = "player")
public class Player {
//constructor etc...
#OptimisticLock(excluded = true)
#LazyCollection(LazyCollectionOption.TRUE)
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name="cr_player_ref_player",
joinColumns = {
#JoinColumn(name="player_id", unique = true)
}
,inverseJoinColumns = {
#JoinColumn(name="player_ref_id")
}
)
private Set<PlayerRef> getPlayerRefs() {
return this.playerRefs;
}
}
Now, in my program I use the following HQL query to get all playerRef entities:
Query playerRefQ = session.createQuery("select playerRef from PlayerRef playerRef ")
.setReadOnly(true);
playerRefQ.setParameter("sport", sport);
#SuppressWarnings("unchecked")
List<PlayerRef> allPlayerRefs = playerRefQ.list();
This results in the N+1 Select statements:
1)
select
playerref0_.id as id1_21_0_,
...
from
player_ref playerref0_
N times)
select
players0_.player_ref_id as player_r2_21_0_,
players0_.player_id as player_i1_34_0_,
player1_.id as id1_19_1_,
...
from
cr_player_ref_player players0_
inner join
betdata.player player1_
on players0_.player_id=player1_.id
where
players0_.player_ref_id=?
This is (again) very unexpected, since I thought the collection is lazy loaded and the set of players of each playerRef should be a hibernate-proxy.
Anyone knows how I can really only load the playerRef entities without also loading the associated players? For my use case I need all playerRefs but not the associated players.
In an earlier question of mine it was suggested, that somehow the toString() method of playerRef or Player could be overwritten in a way that references the associated entities. This is not the case. The N+1 queries happen right on accessing the list of all playerRef entities.
Notes:
This is a follow up question to Why does this result in non-lazy fetching and N+1 select statements?
A similar question also without solution is here: How to lazy load a many-to-many collection in hibernate?
You need to play with Batch size and the Fetch mode in order to solve that problem here is the reference link that will tell you how can you overcome the N+1 select issue
Hope this will help
http://www.mkyong.com/hibernate/hibernate-fetching-strategies-examples/
I've got two entities with unidirectional #OneToMany mapping:
#Entity
#Table(name = "table1")
public class A {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
Integer pk;
String name;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "a_pk", nullable = false)
#Where(clause = "is_deleted = 0")
List<B> children;
}
#Entity
#Table(name = "table2")
public class B {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
Integer pk;
String content;
#Column(name = "is_deleted",nullable=false)
#NotNull
boolean deleted = false;
}
I want to obtain a list of all B entities, which are children of A entities matching a restriction.
Criteria c = session.createCriteria(A.class)
.add(Restriction.like("name", "Test%"))
.createAlias("children","b");
???
And this is where I'm stuck: c.list() will return me a list of A objects. I don't care about A, I want B. How can I do it with Hibernate criteria/projections? If it matters, I use Hibernate 4.2.12
In this simplified case it would make sense to just fetch eagerly; in the real case there's a chain of four OneToMany unidirectional associations, and I want to get all (or better yet, SOME) children all the way down the tree knowing the root id; but I don't want to load all the intermediate stuff (it's not needed, and even if join-fetching four tables would work it's plain gross). Also, simply getting the root and following down the lazy associations is a clear example of N+1 problem escalated.
As a side question, does Hibernate criteria satisfy entity/property #Where restrictions?
You can use projection to get list of "b". Like this :
Criteria c = session.createCriteria(A.class)
.add(Restriction.like("name", "Test%"))
.createAlias("children","b").setProjection(Projections.property("b"));
after this when you try to get results using c.list() it will return a list of B.
I hope this helps!!
So, yeah, I went with using HQL in the end. Nothing special.
List<B> bList = session.createQuery(
"select b from A as a join a.children as b where a.name like 'Test%'"
).list();