I have two unrelated tables, each one with field email. I need a query which introduces column taken from second table if emails match or will be null if no match is found. In SQL this is easy:
SELECT tableA.id, tableA.email, tableB.name
FROM tableA
LEFT JOIN tableB ON tableA.email=tableB.email
ORDER BY tableB.name
Unfortunately JPA doesn't allow joins over unrelated entities so I converted it to:
SELECT tableA.id, tableA.email,
(SELECT tableB.name FROM tableB WHERE tableB.email=tableA.email) AS aname
FROM tableA
ORDER BY aname
Now, it works as JPA query but we are using Query DSL so off we go to converting it:
JPQLQuery query = new JPAQuery(em);
List<Dto> items=query.from(qTableA)
.list(new QDto(qTableA.id, qTableA.email,
new JPASubQuery().from(qTableB)
.where(qTableB.email.eq(qTableA.email)).unique(qTableB.name)))
It works but now I have no idea how to implement sorting and filtering by field introduced by subquery.
Dto is a POJO used to collect results, QDto is a class autogenerated from Dto.
Question is: how to join two unrelated tables using Query DSL and JPA and avoiding native SQL? Is it possible? Sorting and filtering on fields from tableA and tableB.name is a requirement.
Join on unrelated entities is not covered by latest JPA spec (2.1)
However Hibernate 5.1.0+ and EclipseLink 2.4.0+ support ad hoc joins. http://blog.anthavio.net/2016/03/join-unrelated-entities-in-jpa.html
Related
I'm trying to use QueryDSL to fetch a collection of entities (table Category) where each entity has a #OneToMany association with an other type of entity (table User), and a join is used to get it all in one go.
Problem is that the second entity (the users) contains CLOB fields which should not be included. I don't want to include them in my query. Given the following QueryDSL query :
JPAQuery<CategoryEntity> baseQuery = new JPAQuery<>(entityManager)
.select(QCategoryEntity.categoryEntity)
.from(QCategoryEntity.categoryEntity)
.leftJoin(QCategoryEntity.categoryEntity.users, QUserEntity.userEntity)
.where(somePredicate);
QueryDSL will generate something like
SELECT categoryen0_.id, (...), useren0_.id, (...)
FROM category categoryen0
LEFT OUTER JOIN user useren0 ON ...
WHERE ...
How can a specific projection be applied to this query such that the CLOB data is excluded?
Notes :
I'm trying to avoid native queries and changing the domain model.
I have not found a way to use Projections on the join
itself.
Using subqueries inside joins is not supported by JPQL hence
it's not supported by QueryDSL either.
Turns out this was not working well due to my use of fetch joins, which prevented me from using projections. Solving it was a matter of using a basic join instead and manually reconstructing the relationship.
Collection<Tuple> tuples = new JPAQuery<>(entityManager)
.select(QCategoryEntity.categoryEntity, Projections.constructor(UserEntity.class, <constructor arguments>)
.from(QCategoryEntity.categoryEntity)
.join(QCategoryEntity.categoryEntity.users, QUserEntity.userEntity)
.where(somePredicate);
It is my understanding that when I use fetch when querying an object, Ebean will try to fetch the relationship using a join.
e.g.
Ebean.find(ProjectRequest.class)
.fetch("attachments")
.findList();
This works as expected when trying to fetch one relationship.
But when I try to fetch more than one relation it doesn't query any of the relationships with a join and fetches all the relationships with a separate query.
e.g.
Ebean.find(ProjectRequest.class)
.fetch("projectConstructionCosts")
.fetch("attachments")
.fetch("projectRequestComments")
.fetch("additionalContacts")
.where()
.in("project_status", projectStatusValues)
.findList();
I should be able to do this based on the code examples in this documentation page.
https://ebean-orm.github.io/apidoc/10/io/ebean/FetchConfig.html
I use "*" for fetchProperties and work for me
Ebean.find(ProjectRequest.class)
.fetch("projectConstructionCosts", "*")
.fetch("attachments", "*")
.fetch("projectRequestComments", "*")
.fetch("additionalContacts", "*")
.where()
.in("project_status", projectStatusValues)
.findList();
it doesn't query any of the relationships with a join and fetches all the relationships with a separate query
The reason Ebean does not use a SQL join but instead creates additional separate queries to load those associations is because the relationships are ToMany relationships and Ebean will never generate SQL Cartesian product (because it is deemed too risky / expensive).
That is, Ebean will only allow 1 of the ToMany relationships to be included in the "root query" (in order to avoid sql cartiesian product) and the other relationships are then loaded as "secondary queries".
Additionally note that if the query includes a max rows limit then Ebean will honor this in generated SQL. In this case Ebean can't include a SQL join to a ToMany and that would become a "secondary query" (not part of the root SQL query).
Reference: https://ebean-orm.github.io/docs/query/fetch
I would like to do the following query using spring jpa. I am able to build the where clause for my statement with Predicate.toPredicate. However, I don't know how to join on more than one column. On a single column it can be done in the repository using #Query and #Param
SELECT a.name, a.column_x, b.column_y
FROM table_a a
INNER JOIN table_b b
ON b.name = a.name
AND b.pk_2 = a.pk_2
AND b.pk_3 = a.pk_3
WHERE ...;
Another question I have is, is an intermediate tableA_tableB association beneficial if I have something like this, oneToMany relations.
Table 1: thing
thing_name
type
tenant
other1
other2
Table 2: thing_sub_prop
prop_name
value
Association table: thing_thing_sub_prop
type
thing_name
tenant
prop_name
value
Or is it better to just have two tables, thing and thing_sub_prop with the primary key columns of thing repeated in thing_sub_prop as a foreign key?
I have two related entities being mapped by JPA annotations backed by hibernate. The entities both have sequence-backed identity columns in oracle. Our also have monthly partitions, represented by a column called ENTRY_DATE.
T_MASTER T_JOINED
--------- -----------
MASTER_ID JOINED_ID
ENTRY_DATE ENTRY_DATE
MASTER_ID(FK)
... ....
To gain the benefit of the partition key, I'd like Hibernate to join on both the identity IDs and the partition key, but when I use the following annotation in the Joined class:
#ManyToOne
#JoinColumns(value={
#JoinColumn(name="MASTER_ID"),
#JoinColumn(name="ENTRY_DATE")})
private Master master;
I get errors about having too many join columns. I am forced to use
#JoinColumn(name="MASTER_ID")
private Master master;
I'm a bit of a JPA/Hibernate noob. Is it possible to use a partition key in addition to the primary key when joining to related entities?
Thanks!
You probably need to declare both columns as parts of composite primary key in Master. Hibernate would not care that much about what is real PK in the database. Mapping wil be slightly more complex with #Embeddable but it should solve the problem.
yes.
Using native sql it looks as follows:
Query query = session.createSQLQuery( "SELECT {T_master.*}, {T_joined.*} FROM schema.t_master AS T_master OUTER LEFT JOIN schema.T_joined AS T_joined ON T_master.ENTRY_DATE = T_joined.ENTRY_DATE AND T_master.MASTER_ID = T_joined.JOINED ID " /*WHERE ... */ );
query.setEntity( "T_master", T_master.class);
query.setJoin( "T_joined", "T_master.joinedSet"); //joinedSet is the one-to-many mapping
query.setJoin( "T_master", "T_joined.master" );
Thus, you get to be very verbose on your query, and hibernate only acts as an object mapper.
Using Criteria or HQL, it is impossible to have OUTER LEFT JOIN ON some field other than primary keys. This is why if you have such an option, use native SQL code.
I have a question using Hibernate Criteria, I need to convert this query using criteria.
SELECT * FROM A a_ INNER JOIN B b_ ON a_.column1=b_.column1 AND b_.column2 IN (X,Y) AND active='Y';
I need the result as table A.
I just solved this issue, here is my code
Criteria criteria = session.createCriteria(ProductOffer.class);
criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
Date effDate = TypeConvertUtil.toDate(param.get("effDate"));
criteria.add(Restrictions.le("effDate", effDate));
criteria.add(Restrictions.gt("expDate", effDate));
criteria.createAlias("productOfferPropDefs", "def",JoinType.LEFT_OUTER_JOIN);
criteria.setFetchMode("productOfferPropDefs", FetchMode.JOIN);
criteria.add(Restrictions.le("def.effDate", effDate));
criteria.add(Restrictions.gt("def.expDate", effDate));
criteria.createAlias("def.productOfferProps", "prop",JoinType.LEFT_OUTER_JOIN);
criteria.setFetchMode("def.productOfferProps", FetchMode.JOIN);
criteria.add(Restrictions.le("prop.effDate", effDate));
criteria.add(Restrictions.gt("prop.expDate", effDate));
productOfferList = criteria.list();
Please beware that
criteria.createAlias("productOfferPropDefs", "def",JoinType.LEFT_OUTER_JOIN);
this parameter is important:
JoinType.LEFT_OUTER_JOIN
if you did not use it, and your relation is one-to-many, it will hit the 1:N classic issue for hibernate
If the associations are defined, see http://docs.jboss.org/hibernate/core/3.3/reference/en/html/querycriteria.html#querycriteria-associations
In case associations are not specified in the entities definition, you can't use criteria.
You can use HQL to do inner joins (need to write in implicit join notation), for doing left joins you have to use native SQL.