i have the following entities
class ProductUnitOfMessure
#ManyToOne
#JoinColumn(name="PRODUCT_ID")
private Product product;
#ManyToOne
#JoinColumn(name="VARIANT_ID")
private Variant variant;
class Product
#OneToMany(mappedBy = "product", fetch = FetchType.LAZY)
List<Variant> variants;
class Variant
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PRODUCT_ID")
private Product product;
Now when i run the query
select p from ProductUnitOfMeasures p where p.variant.sku=?1 or p.product.sku =?1
(sku of type String).
it return empty list, although there are some as per the below ProductUnitOfMessure table
ProductUnitOfMeasures
when i only run select p from ProductUnitOfMeasures p where p.product.sku =?1
i get some values, why this is happening even though i use OR not AND ?
database is oracle
JPA forces inner joins to be used for references when specifying '.' on a relationship by default. Inner joins automatically exclude nulls from the results - which might be inline with what you might want if you are calling ProductUnitOfMeasures.getVariant().getSku() == youValue as you wouldn't want to deal with NPEs. If you want nulls included in the results, database have what is known as outer join semantics, which allow rows with nulls to be included and still used for the rest of the filter.
Something like:
"select p from ProductUnitOfMeasures p left join p.variant variant left join p.product product where variant.sku = ?1 or product.sku = ?1"
Will allow ProductUnitOfMeasures with either null variant or null product references to still be considered, allowing your ProductUnitOfMeasures with a null variant but with a product.sku value matching your filter to still be returned.
Related
I have a Join looking like:
Join<RepairEntity, RepairAssignEntity> joinRepairAssignEntities = root.join(RepairEntity_.repairAssignEntities, LEFT);
when it converted to SQL it looks like
left outer join
repair_assign repairassi1_
on repairenti0_.id=repairassi1_.repair_id
I want it to be like:
left outer join
repair_assign repairassi1_
on repairenti0_.PARENT_ID=repairassi1_.repair_id
If I add condition to that join:
Predicate equal = cb.equal(root.get(RepairEntity_.parentId), repairEntityPath.get(RepairEntity_.id));
joinRepairAssignEntities = joinRepairAssignEntities.on(equal);
it makes it
left outer join
repair_assign repairassi1_
on repairenti0_.id=repairassi1_.repair_id
and (
repairenti0_.parent_id=repairassi1_.repair_id
)
How do I get rid of
on repairenti0_.id=repairassi1_.repair_id
?
A priori it seems that the entities are not well related, if you want to join by the parent field it should be something like this:
Entity RepairEntity
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="PARENT_ID")
RepairAssignEntity repairAssignEntity;
Entity RepairAssignEntity
#OneToMany(mappedBy="repairAssignEntity", fetch=FetchType.LAZY)
List<RepairEntity> RepairEntities;
if not, you would have to restructure the query to adapt it to the JPA entity modeling.
Regarding to get rid of the condition "on repairenti0_.id = repairassi1_.repair_id" you cannot because it is the implicit one of the relationship defined in the entity, but if you modify the modeling as I indicate, only the condition "repairenti0_.parent_id = repairassi1_ will appear. .repair_id "
I am quite a beginner with Spring Data, and I have to code my first difficult query :)
I am making a reservation engine for an event. My data model is composed by:
a RoomType entity defining a possible configuration for a room (e.g. double, triple, quadruple)
a Room entity representing the actual Room
a RoomArrangement entity defining all the possible RoomTypes for a Room (e.g. the room 7 can be configured as Triple or Double room)
a RoomAssignment entity representing the actual configuration chosen for a room after having been reserved
FKs are configured this way
RoomType <--* RoomArrangement *--> Room <-- RoomAssignment
(see code below for Entity definition)
I need to find the Room without an Assignment that has the highest Priority (= nearest to 1) for a given RoomType.
I have configured Spring as below.
RoomType.java
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "room_type_id")
private List<RoomArrangement> roomArrangements;
RoomArrangement.java
#ManyToOne(targetEntity = RoomType.class)
#JoinColumn(name = "room_type_id", nullable = false)
private RoomType roomType;
#ManyToOne(targetEntity = Room.class)
#JoinColumn(name = "room_id", nullable = false)
private Room room;
#Column(name = "priority")
private Integer priority;
Room.java
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "room_id")
private List<RoomArrangement> roomArrangements;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "room", cascade = CascadeType.ALL)
private RoomAssignment assignment;
RoomArrangementRepository.java
RoomArrangement findFirstByRoomTypeAndRoom_AssignmentIsNullOrderByPriorityAsc(RoomType rt);
The query above is translated to
select
roomarrang0_.id as id1_3_,
roomarrang0_.priority as priority2_3_,
roomarrang0_.room_id as room_id3_3_,
roomarrang0_.room_type_id as room_typ4_3_
from
room_arrangements roomarrang0_
left outer join
rooms room1_ on roomarrang0_.room_id=room1_.id
where
roomarrang0_.room_type_id=9
and
(room1_.id is null)
order by
roomarrang0_.priority asc
limit 1;
The issues are two:
I do not know where the where clause
(room1_.id is null)
comes from
I do not know where the "AndRoom_AssignmentIsNull" clause has gone
Should I "invert" the OneToOne relationship and put the FK on the Room class?
Thanks for your help!
Lorenzo
I've tried to apply some of the suggestions, and "turned" the query on the RoomRepository.
The query came like this:
Room findFirstByRoomArrangements_RoomTypeAndAssignmentIsNullOrderByRoomArrangements_PriorityAsc(RoomType rt);
We come to the same problem:
select
room0_.id as id1_6_,
room0_.room_name as room_nam2_6_
from
rooms room0_
left outer join
room_arrangements roomarrang1_ on room0_.id=roomarrang1_.room_id
where
roomarrang1_.room_type_id=?
and
(room0_.id is null)
order by
roomarrang1_.priority asc
limit ?;
I think the problem lies in the fact that the one-to-one relationship between Room and RoomAssignment is represented on the database with a FK on the room_assignments table to the rooms table.
I will try to put the FK on the rooms table instead of on the room_assignments table and see if something changes.
If you need to find a room without an without an Assignment, shouldn't you be looking that in the Room repository?
You said that you have to find a Room but you are returning a RoomArrangement in a RoomAssignmentRepository. This is quite confusing.
Let's assume you are in right place, that means RoomRepository and as you said:
I need to find the Room without an Assignment that has the highest Priority (= nearest to 1) for a given RoomType.
try to use the following method name
Room findByRoomArrangementRoomTypeAndRoomAssignmentIsNullOrderByPriorityAsc(RoomType rt)
Made it!
In the end, the problem lied in the fact that the one-to-one relationship between Room and RoomAssignment was mapped by an FK from RoomAssignment to Room. Apparently, Spring Data didn't manage this configuration properly: the Assignment is null was translated to room_assignment.room_id = null and, since room_id was an FK to room.id, to room.id = null.
Inverting the FK mapping, the query is translated to
select
room0_.id as id1_6_,
room0_.assignment_id as assignme3_6_,
room0_.room_name as room_nam2_6_
from
rooms room0_
left outer join room_arrangements roomarrang1_ on room0_.id=roomarrang1_.room_id
where
roomarrang1_.room_type_id=?
and (room0_.assignment_id is null)
order by
roomarrang1_.priority asc
limit ?
which correctly returns what I needed.
I have 3 entities, infact many more joined together for brevity i'm skipping those and i'm using open jpa 2.2.2 and oracle 11g. Any thoughts what's going wrong here?
Entity SystemRules{
#OneToMany(mappedBy = "systemRule", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<ServiceActionMap> serviceActionMap;
}
Entity ServiceActionMap{
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "SYSTEM_RULE_ID")
private SystemRules systemRule;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "RFS_TYPE_ID", nullable = true)
private RfsTypeMap rfsType;
}
Entity RfsTypeMap{
#Id
#Column(name="RFS_TYPE_ID" ,nullable=false)
private BigDecimal rfsTypeId;
#Column(name="RFS_NAME")
private String rfsName;
}
Now, I am trying to order the result by RfsTypeMap.rfsName, i'm creating query using criteria builder in following manner
CriteriaQuery<SystemRules> query = cb.createQuery(SystemRules.class);
Root<SystemRules> root= query.from(SystemRules.class);
root.fetch(SystemRules_.serviceActionMap).fetch(ServiceActionMap_.rfsType, JoinType.LEFT);
My order by clause is like this
cb.desc(cb.upper(systemRules.get("serviceActionMap").get("rfsType").get("rfsName").as(String.class)));
Generate JPQL looks like, where i expect a left outer join clause between ServiceActionMap and RfsTypeMap but it's missing. Same gets translated in SQL and i miss those records which are having ServiceActionMap.rfsType as null value.
SELECT DISTINCT s FROM SystemRules s INNER JOIN FETCH s.serviceActionMap INNER JOIN FETCH s.serviceActionMap INNER JOIN FETCH s.ruleProperty INNER JOIN FETCH s.ruleProperty where ... ORDER BY UPPER(s.serviceActionMap.rfsType.rfsName)
I tried going over several answers here but no success, tried explicitly putting a where clause for ServiceActionMap.rfsType is null as suggested on few answers but it's getting ignored, since join happens before where evaluation. Somewhere this question openJPA outer join on optional many-to-one when have order by clause matches my scenario but not able to generate suggested JPQL through criteria API.
I found one related bug on apache jira https://issues.apache.org/jira/browse/OPENJPA-2318. But, not sure that's the case.
I see that every join is repeated twice and even the join alias is always referring to SystemRules. It might be that orderby has caused the repeating inner joins and we may need to explicitly use Join object to refer to extended column.
CriteriaQuery<SystemRules> query = cb.createQuery(SystemRules.class);
Root<SystemRules> root = query.from(SystemRules.class);
Join<SystemRules, ServiceActionMap> join1 = root.join(SystemRules_.serviceActionMap, JoinType.INNER);
Join<ServiceActionMap, RfsTypeMap> join2 = join1.join(ServiceActionMap_.rfsType, JoinType.LEFT);
query.orderBy(cb.desc(cb.upper(join2.get(RfsTypeMap_.rfsName))));
Say I want to get all rows of the MyEntity that have an id lower than 10. This entity contains a list of Another entity. I would like this list to be fetched only by a subset of the listAnother. This subset containing only Another where the user contained in it is a specific one.
Basically in SQL it would be like this :
SELECT * FROM myentity m
LEFT JOIN another a
ON m.idTable=a.my_entity
AND a.user = "test1"
WHERE m.idTable < 10;
I didn't manage however to translate this query to jpql.
My entities being like this :
#Entity
public class MyEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int idTable;
#OneToMany(mappedBy = "myEntity")
private List<Another> listAnother;
}
#Entity
public class Another implements Serializable {
#Id
private int idAnother;
// bi-directional many-to-one association to Thethread
#ManyToOne(fetch = FetchType.LAZY)
private MyEntity myEntity;
#ManyToOne(fetch = FetchType.LAZY)
private User user;
}
#Entity
public class User implements Serializable {
#Id
private String username;
}
In jpa I could do this :
SELECT m FROM MyEntity where m.idTable < 10;
And then for each entity I get from this list call this:
SELECT a FROM Another Where a.user.username=:'test' AND a.myEntity=:entity;
However I would like to do it all at once in one query. Can I do this with criteria ? I didn't take the time to learn it but if it's possible then I will.
JPQL and Critaria API are equal in terms of what you can express with them. What is possible with JPQL is possible with Criteria and vice versa.
With JPQL, you can simply combine your 2 queries into one in this way:
SELECT a FROM Another a Where a.user.username=:test AND a.myEntity.idTable < 10
You can use dot notation (.) to join multiple entities in the query, provided that the relationship is X-to-one. If you have X-to-many relationship, you need to use JPQL JOIN, which is not very complicated. Example with (LEFT) JOIN:
SELECT m FROM MyEntity m LEFT JOIN m.listAnother a Where a.user.username=:test AND m.idTable < 10
The result is of course not equal - in first case you will get list of Another entities and you can get MyEntity by a.myEntity, in the second case you will get list of MyEntity, which all have at least one Another entity with given user
In hibernate you can use Filters and FilterJoinTable. Here you can read how to do that. Similar problem was solved here.
You need to extend the logic which you applied to check the username (a.user.username=:'test') which was for many-to-one relation between anything and user by taking it one level up to myEntity and then using it for one-to-many relation as well -
SELECT m FROM MyEntity where m.idTable < 10 and (m.listAnother.user.username=:'test')
The join condition "m.listAnother.myEntity=:entity" wouldn't be needed now as in our query we have started from the specific myEntity and then moved down to listAnother.user.username .
I don't have the table definitions to try this query myself, exact SQL may require some tweaks - but logically it should work like the way I showed above, i.e. just the way you joined Another with User, the same way you can join MyEntity with Another by just traversing down the child listAnother.
I make a query:
String query = "SELECT DISTINCT a FROM A a FETCH ALL PROPERTIES " +
"JOIN a.Bs AS b " +
"JOIN b.Cs AS c WHERE c = :c";
Query q = DAO.getSession().createQuery(query);
q.setParameter("c", c);
return q.list();
Even though I've said FETCH ALL PROPERTIES on a, when I access all the collections that A has, they still need to be loaded, thus aren't eagerly loaded. They have been defined as lazy loading, and that is the default behaviour I want, but this is the exception: I would like them loaded right now. I've tried swapping JOIN for LEFT OUTER JOIN to provoke Hibernate into loading them, and I've tried setting q.setFetchMode("a", FetchMode.EAGER), but it doesn't exist for Query.
The list of As is quite long, and they have quite a few collections, so making this an n+1 query thing is very slow (about ten seconds, as opposed to doing it in a single query which would be sub-second speed). I'd far prefer one query and loading all that's necessary in that one go. Any suggestions on how I can do that?
PS, Little bonus question: If I replace the "JOIN b.Cs AS c WHERE c = :c";
line with "WHERE :c IN b.Cs";, I get an SQL exception:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '))' at line 1
The double paranthesis it's referring to is "and ('151000000-0000' in (.))" where 151000000-0000 is the primary key of c. Any idea why I get this error when I do it this way compared to not getting it when I do it the with joining b.Cs in?
UPDATE, as requested, here is the way I use for mapping. B and C are very similarly designed:
#Entity
#Table(name = "tblA")
public class A {
#Id
String AId;
#Column(name = "shortName", length = 12, nullable = false)
String shortName;
#OneToMany(fetch=FetchType.LAZY, mappedBy="theA")
private Set<B> Bs;
#OneToMany(fetch=FetchType.LAZY, mappedBy="theA")
private Set<D> Ds;
#OneToMany(fetch=FetchType.LAZY, mappedBy="theA")
private Set<E> Es;
#OneToMany(fetch=FetchType.LAZY, mappedBy="theA")
private Set<F> Fs;
}
theA in B, D, E and F is defined like this:
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "AId", nullable = true)
#ForeignKey(name="FK_KategoriID")
private A theA;
Cheers
Nik
fetch all properties is not what you want; it's used for telling Hibernate that you want it to fetch single-valued lazy-loaded properties. Details are here.
You need to specify join fetch in your query instead:
SELECT DISTINCT a FROM A a
LEFT JOIN FETCH a.Bs AS b
LEFT JOIN FETCH b.Cs AS c
WHERE c = :c
As far as bonus question goes, WHERE :c IN b.Cs is illegal syntax. Depending on how your C is mapped, you may want to look at elements() function instead.
FETCH ALL PROPERTIES only works for lazy properties (where a property is a String, Integer, ...) not one-to-may associations (i.e. collections)
see A Short Primer On Fetching Strategies