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
Related
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 have the following problem (pseudo-java-code):
Let me a class A,B,C with the following relationships:
#Entity
#Table(name = "A")
public class A {
#OneToMany(mappedBy = "a")
private B b;
}
#Entity
#Table(name = "B")
public class B {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id")
private A a;
#OneToOne(mappedBy = "b", fetch = FetchType.LAZY)
private C c;
}
#Entity
#Table(name = "C")
public class C {
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "b_id")
private B b;
}
I'm using JpaRepository with #Query annotation and I implemented the following query:
#Query("SELECT DISTINCT(a) FROM A a "
+ "LEFT JOIN FETCH a.b as b"
+ "WHERE a.id = :id ")
A findById(#Param("id") Integer id);
I want retrieve the informations about class A and B, but not C.
Somehow (I don't know why) the query try to retrive also the relation between B and C.
And then, with hibernate, start the lazy invocation for retrieving C.
Naturally, if I fetch also the relation between B and C (adding LEFT JOIN FETCH b.c as c) that's not happen.
My question is, why? Why I'm forced to fetch all nested relations and not only the ones which I need?
thank you.
Carmelo
Nullable #OneToOne relation are always eager fetched as explained in this post
Making a OneToOne-relation lazy
Unconstrained (nullable) one-to-one association is the only one that
can not be proxied without bytecode instrumentation. The reason for
this is that owner entity MUST know whether association property
should contain a proxy object or NULL and it can't determine that by
looking at its base table's columns due to one-to-one normally being
mapped via shared PK, so it has to be eagerly fetched anyway making
proxy pointless.
Shot in the dark, but there are some issues with lazy-loading #OneToOne-relationships. At least in older versions of Hibernate. I think (but can't seem to find any documentation) that this was fixed in one of the newer versions, so if you are not using a new version of Hibernate, try upgrading.
I want to get all the listeners that have an action with the name "Create".
I have the following entities (simplified for this question):
#Entity
public class Listener
{
...elided...
#ManyToMany(targetEntity = Action.class, fetch = FetchType.EAGER)
#JoinTable(
name = "ListenerActions",
joinColumns = #JoinColumn( name = "listenerId", referencedColumnName = "id" ),
inverseJoinColumns = #JoinColumn( name = "actionId", referencedColumnName = "id" )
)
List<Action> actions;
}
#Entity
public class Action
{
...elided...
private String name;
}
How do I filter on the list of actions? I only need one of the actions in a Listener's "actions" to have the name "Create".
A path expression in a JPA 2 query can navigate to a collection-valued field, including across a one-to-many or many-to-many relationship, but that must be the last step in the path; JPA does not permit you to write a path expression that navigates further from there. Therefore, to perform the kind of filtering you want to do, you need to perform a join or create a subquery. The JPA 2.0 specifications provide this example of the former:
SELECT DISTINCT o
FROM Order o JOIN o.lineItems l
WHERE l.product.productType = 'office_supplies'
, which has the form you want.
Adapting that to your schema is left as an exercise.
Consider the following mapping with JPA annotations
#ManyToMany(cascade = { CascadeType.ALL })
#JoinTable(name = "infotype_validations",
joinColumns = { #JoinColumn(name = "info_type_id") },
inverseJoinColumns = { #JoinColumn(name = "validation_id") }
)
#OrderBy(value="validation_id desc")
public Set<Validation> getValidation() {
return validation;
}
My intention is to have a jointable in the database and each time the getValidation() is called in my services the records get returned ordered by validation_id. Now to test my functionality I make use of DbUnit. Each time I start a testclass my database gets created and hibernate creates my tables afterwhich DbUnit fills them with data. When I comment #OrderBy my tests pass but when I uncomment it, I get table infotype_validations can't be found. I've looked at the available documentation online and it seems it is perfectly possible to have #OrderBy in this kind of mapping. So what am I missing ?
You need to use the field name not the column name.
//Assuming the field is validationId
#OrderBy(value="validationId desc")
public Set<Validation> getValidation() {
return validation;
}
Also make sure that the infotype_validations table exists within your database and the spelling matches.
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