EclipseLink- JPQL join tables through query - sql

I have problem with my JPQl. this is a one to many relationship with TrainRoute and TrainRouteStation. I'm trying to create a inner join and fetch the data. native SQL query working when I used mysql workbeanch and I'm trying convert it to JPQL. also, I was trying to fix from 2 days.
Error : Object comparisons can only be used with OneToOneMappings. Other mapping comparisons must be done through query keys or direct attribute level comparisons.
Class: TrainRoute
#Basic(optional = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
#Column(name = "train_route_id", unique = true, nullable = false)
public Long getTrainRouteId() {
return this.trainRouteId;
}
public void setTrainRouteId(Long trainRouteId) {
this.trainRouteId = trainRouteId;
}
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER, mappedBy = "trainRoute")
public List<TrainRouteStationData> getTrainRouteStations() {
return this.trainRouteStations;
}
public void setTrainRouteStations(List<TrainRouteStationData> trainRouteStations) {
this.trainRouteStations = trainRouteStations;
}
Class: TrainRouteStation
#Basic(optional = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
#Column(name = "train_route_station_id", unique = true, nullable = false)
public Long getTrainRouteStationId() {
return this.trainRouteStationId;
}
public void setTrainRouteStationId(Long trainRouteStationId) {
this.trainRouteStationId = trainRouteStationId;
}
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "train_route_id", nullable = false)
public TrainRouteData getTrainRoute() {
return this.trainRoute;
}
JPQL :"SELECT s FROM TrainRouteData t inner join TrainRouteStationData s ON t.trainRouteId=s.trainRoute where s.stationSeqN >=1 AND s.stationSeqN <=3 AND t.trainRouteDescX='Test1-Test2' order by s.stationSeqN asc"
Native SQL : SELECT train_route_station.* FROM train_route inner join train_route_station ON train_route.train_route_id=train_route_station.train_route_id where train_route_station.station_seq_n >= 1 AND train_route_station.station_seq_n <= 3 AND train_route.train_route_desc_x='Test1-Test2' order by train_route_station.station_seq_n asc
And it throw an error:
Exception Description: Object comparisons can only be used with OneToOneMappings. Other mapping comparisons must be done through query keys or direct attribute level comparisons.
Mapping: [org.eclipse.persistence.mappings.DirectToFieldMapping[trainRouteId-->train_route.train_route_id]]
Expression: [
Query Key trainRouteId
How can I change that query?

That's not how joins work in JPQL.
The correct query is
select s from TrainRouteData t inner join t.trainRouteStations s
where s.stationSeqN >= 1
and s.stationSeqN <= 3
and t.trainRouteDescX = 'Test1-Test2'
order by s.stationSeqN asc

Related

eclipse jpa jql not generating on clause for one to many

I have two tables:
DIDPool
DIDRange
A pool can have many ranges.
The range table doesn't have a parent id (e.g. no foreign key back to the pool) as the range table is used by a number of other entities.
As such I've setup the join so that it uses an intermediate join table tbldidpooldidrange.
#Entity
#Table(name = "tbldidpool")
class DIDPool
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false)
private long id;
#OneToMany
#JoinTable(name = "tbldidpooldidrange", joinColumns = #JoinColumn(name = "did_pool_id")
, inverseJoinColumns = #JoinColumn(name = "did_range_id"))
private ArrayList<DIDRange> didRanges = new ArrayList<DIDRange>();
}
#Entity
#Table(name = "tbldidrange")
public class DIDRange
{#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false)
private long id;
#NotEmpty
#NotNull
#AttributeOverride(name = "phoneNumber", column = #Column(name = "start"))
private PhoneNumber start = new PhoneNumber();
}
The jpa correctly generates the intermediate join table.
My problem is in creating a jql expression that joins the two tables.
// Finds the pool that contains the given range or part range.
#SuppressWarnings("unchecked")
public List<DIDPool> findOverlapping(DIDRange didRange)
{
EntityManager em = EntityManagerProvider.getEntityManager();
Query query = em.createQuery("SELECT e FROM DIDPool e "
+ "join DIDRange r "
+ "where r.start.phoneNumber=:startNo"
+ " ) "
);
query.setParameter("startNo", didRange.getStartOfDIDRange().toCompactString());
return (List<DIDPool>) query.getResultList();
}
My understanding of jql is that you don't specify the join 'on' clause as jpa knows how to generate it.
However this is the sql the above clause generates:
SELECT t1.ID,
FROM tbldidrange t0, tbldidpool t1 WHERE t0.start = ?
bind => [61383208100]
As you can see there is no 'on' clause (and the where doesn't join the tables) and the intermediate join table isn't even mentioned.
What am I doing wrong here?
I've been the reading the jpa documentation and its really not very helpful.
Your query should be
Query query = em.createQuery("SELECT e FROM DIDPool e "
+ "join e.didRanges r "
+ "where r.start.phoneNumber=:startNo"
+ " ) "
);
You have to use the ToMany attribute in the JOIN.

JPA query returns duplicate objects

I have this relationship in db. In the Item table the primary keys are Item_id and site. ItemPart table's primary keys are also them and there are other 2 primary keys as well. These tables are join by item_id and site as one to many relationships.
These are the 2 entity classes.
public class Item {
#Id
#Column(name = "item_id", updatable = false, nullable = false)
private String itemId;
#Id
#Column(name = "site", updatable = false, nullable = false)
private String site;
#OneToMany(mappedBy = "item")
private List<ItemPart> itemPart;
...
public class ItemPart {
#Id
#Column(name = "item_id", updatable = false, nullable = false)
private String itemId;
#Id
#Column(name = "site", updatable = false, nullable = false)
private String site;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "item_id", referencedColumnName = "item_id", insertable = false, updatable = false),
#JoinColumn(name = "site", referencedColumnName = "site", insertable = false, updatable = false)
})
private Item item;
...
What I want is when giving an item_id return a result like below. If I give item_id = "100" and there is one item with item_id = 100 and 2 itemParts with item_id =100 and site=S01.
{
"Item":[
{
"item_id":"100",
"site":"S01",
"itemPart":[
{
"item_id":"100",
"site":"S01",
"another_pk": "x"
},
{
"item_id":"100",
"site":"S01",
"another_pk": "y"
}
],
...
}
]
But actually, it returns 2 similar item objects with each of them 2 itemPart objects. Like this.
{
item:
itemPart:[
{...},
{...}
]
},
{
item:
itemPart:[
{...},
{...}
]
}
I want to fix this and I'm using JPA. Can anyone find where is the problem here.
#Query("SELECT p FROM Item p JOIN fetch p.ItemPart n WHERE
p.item_id = ?1 ")
List<Item> findAllByItemId(String itemId);
You should correct your query like this:
#QueryHints(value = {
#QueryHint(name = org.hibernate.jpa.QueryHints.HINT_PASS_DISTINCT_THROUGH, value = "false")
})
#Query("select distinct p from Item p join fetch p.itemPart where p.itemId = :itemId ")
List<Item> findAllByItemId(#Param("itemId") String itemId);
See also this section of hibernate documentation.
I put 'distinct' in the #Query then the problem was solved.
#Query("SELECT distinct p FROM Item p JOIN fetch p.ItemPart n WHERE
p.item_id = ?1 ")
List<Item> findAllByItemId(String itemId);

Nested query in JPQL

I have 2 entities -
#Table(name = "MM_MONITORING_CARD")
#Entity(name = "mm_MonitoringCard")
public class MonitoringCard extends StandardEntity {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CLIENT_ID")
private Counterparty client;
#Column(name = "START_DATE")
private LocalDate startDate;
#Column(name = "END_DATE")
private LocalDate endDate;
...otherColumn
}
and
#DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.STRING)
#Table(name = "MM_COUNTERPARTY")
#Entity(name = "mm_Counterparty")
#DiscriminatorValue("COUNTERPARTY")
#NamePattern("%s|name")
public class Counterparty extends StandardEntity {
#Column(name = "TYPE", insertable=false, updateable=false)
private String type;
...otherColumn
I need get all examples monitoringCard with condition - 1) between start date and end date 2) with Counterparty type = 'someType'
I do method -
List<MonitoringCard> monitoringCardList = dataManager.load(MonitoringCard.class)
.query("select distinct cm from mm_MonitoringCard m join mm_Counterparty cm where (m.current_date between cm.startDate and cm.endDate) and cm.type = :type")
.parameter("type", "someType")
.list();
but i get error, how can i make a correct request?
thanks
Your query is not correct. It should use a join like this:
select distinct m.Counterparty from mm_MonitoringCard m
where (m.current_date between m.Counterparty.startDate and m.Counterparty.endDate)
and m.Counterparty.type = :type
In JPA you don't need to do an explicit join when navigating on a ToOne relationship.

HQL generated a incorrect SQL query

I got [42000] You have an error in your SQL syntax error when I execute the HQL below:
update Status set result=18 where (userByUserId.userName like 'administrator')
And this is the corresponding SQL:
update
status cross
join
set
result=18
where
userName like 'administrator'
The results I am getting are incorrect.
But another similar query is in working order:
select count(*) from Status where (userByUserId.userName like 'administrator')
the corresponding SQL:
select
count(*) as col_0_0_
from
status status0_ cross
join
user user1_
where
status0_.userId=user1_.userId
and (
user1_.userName like 'administrator'
)
Does anyone know what happened?
Here is the code(partly):
Status.java
#Table(name = "status")
#Entity
#KeyField("statusId")
public class Status implements Serializable {
private Integer result;
#Column(name = "result", nullable = false, insertable = true, updatable = true
, length = 10, precision = 0)
#Basic
public Integer getResult() {
return result;
}
private Integer userId;
#Column(name = "userId", nullable = false, insertable = true, updatable = true
, length = 10, precision = 0)
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
private User userByUserId;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "userId", referencedColumnName = "userId", nullable = false,
insertable = false, updatable = false)
public User getUserByUserId() {
return userByUserId;
}
}
User.java
#Table(name = "user")
#Entity
#KeyField("userId")
public class User implements Serializable {
private String userName;
#Column(name = "userName", nullable = false, insertable = true, updatable = true, length = 24,
precision = 0, unique = true)
#Basic
public String getUserName() {
return userName;
}
private Collection<Status> statusesByUserId;
#OneToMany(mappedBy = "userByUserId", cascade = CascadeType.ALL)
public Collection<Status> getStatusesByUserId() {
return statusesByUserId;
}
}

unable to update objects of one to one relation in hibernate

I have relation as shown bellow:
#Entity
#Table(name = "ORDER_", catalog = "smartorder")
public class Order implements Serializable {
/**
* serial version id
*/
private static final long serialVersionUID = 13875615L;
#Id
#Column(name = "ORDER_ID", unique = true, nullable = false)
#SequenceGenerator(name = "ORDER_ID_GEN", sequenceName = "ORDER_ID_SEQ")
#GeneratedValue(generator = "ORDER_ID_GEN")
private long orderId;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "INVOICE_ID", referencedColumnName = "INVOICE_ID")
private Invoice invoice;
// setters and getters
}
#Entity
#Table(name = "INVOICE_")
public class Invoice implements Serializable {
/**
* serial version id
*/
private static final long serialVersionUID = 13875612L;
#Id
#Column(name = "INVOICE_ID", unique = true, nullable = false)
#SequenceGenerator(name = "INVOICE_ID_GEN", sequenceName = "INVOICE_ID_SEQ")
#GeneratedValue(generator = "INVOICE_ID_GEN")
private int invoiceId;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "ORDER_ID", referencedColumnName = "ORDER_ID")
private Order order;
#Column(name = "SUB_TOTAL", precision = 6, nullable = false)
private double subTotal;
#Column(name = "SERVICE_TAX", precision = 6, nullable = false)
private double serviceTax;
#Column(name = "VAT", precision = 6, nullable = false)
private double vat;
#Column(name = "SURCHAARGE", precision = 6, nullable = false)
private double surChaarge;
#Column(name = "GRAND_TOTAL", precision = 6, nullable = false)
private double grandTotal;
//setters and getters
}
I am able to save the records properly. But when i am trying to update orders objects by setting invoice object to order object then the order object is nor persisting only invoice object is persisting.
Order o = getSession().load(Order.class,1L);
o.setInvoice(new Invoice(.........));
getSession().update(o);
in console I am able to see one SQL statement only,
insert into INVOICE_ (DISCOUNT, GRAND_TOTAL, ORDER_ID, ROUNDING, SERVICE_TAX, SUB_TOTAL, SURCHAARGE, VAT) values (?, ?, ?, ?, ?, ?, ?, ?)
Invoice Id is not getting update in Order table :(
Can anyone suggest whats the issue is.
Thanks in advance.....
This may depend on your unusual design.
With INVOICE_ID in ORDR_ and ORDER_ID in INVOICE_ you have both tables at the same time as parent and child of each other.
If your database uses foreign keys deleting and inserting will be hard.
You should use one type/table as parent, (e. g. Order, because it's normaly first) and the other as child (order_id will be in invoice_ table).
In your object model you can have both directions (see first example of http://docs.oracle.com/javaee/6/api/javax/persistence/OneToOne.html)
The issue is incorrect scenario in which you used your Entities/Tables and the one-to-one mapping style. The concept of One-To-One does not corresponed with your current design of both tables and entities.
Please, try to read more about one-to-one here: The concept for one-to-one mapping. Explain the mapping
And mostly take a deep look here: Hibernate – One-to-One example (Annotation), where you can find examples of the one-to-one mapping.
If you really would like to continue with one-to-one mapping you have to:
Remove the "INVOICE_ID" column from the "INVOICE_" table (surprising but a fact)
make the "ORDER_ID" column in the "INVOICE_" table as a primary key (another fact)
change the mapping of the Invoice entity to be more submissive (driven by Order entity)
Example of changes of the Invoice mapping:
// just a draft, to give you idea about the
// "submissive" side mapping.
// All the ID stuff of the Invoice is driven by its
// "Master" - Order
#GenericGenerator(name = "generator", strategy = "foreign",
parameters = #Parameter(name = "property", value = "order"))
#Id
#GeneratedValue(generator = "generator")
#Column(name = "ORDER_ID", unique = true, nullable = false)
public Integer getOrderId() {
return this.orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
#OneToOne(fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
public Order getOrder() {
return this.order;
}
Please, take it as a draft, to show how different the one-to-one concept is.