I am a beginner when it comes to JPA and I am struggling to write the proper entity classes for my database structure.
Firstly, here are my tables:
create table article (
id varchar(100) not null primary key,
title varchar(255) not null
)
create table article_provider (
providerId varchar(60) not null,
articleId varchar(100) null,
isOnSale BOOL NOT NULL,
constraint article_provider_articleid_fk foreign key (articleId) references article (id) on update cascade on delete cascade
)
As you can see I have a one-to-many relationship (every article can have 0..n providers).
Now when accessing articles I would also like to know, if the article is on sale by any of it's providers. For this I would use the following SQL query:
SELECT article.*, MAX(article_provider.isOnSale) AS isOnSale FROM article
LEFT JOIN article_provider ON article.id = article_provider.articleId
GROUP BY article.id
How would I best go about fetching this type of result using Hibernate/JPA?
Your query is not right -- you use attributes in SELECT clause that are not in GROUP BY clause, but for example to select Article.id + MAX(whatever) you can use something like:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Article> query = builder.createQuery(Article.class);
Root<Article> root = query.from(Article.class);
Join<Article, ArticleProvider> join = root.join(Article_.provider, JoinType.LEFT);
query.multiselect(root.get(Article_.id), builder.max(sale));
query.groupBy(root.get(Article_.id));
// query will return collection of scalar attributes (ID + Number)
Object result = entityManager.createQuery(query).getResultList()
To select exactly Article.* + MAX(sales) you would need to use the above as a subquery and join again with Article entity.
But there are simillar of examples elsewhere: How do I write a MAX query with a where clause in JPA 2.0?
EDIT:
another tip: https://www.thoughts-on-java.org/hibernate-tip-subquery-criteriaquery/
correction/clarification: I have only assumed you want to use Criteria API where subquery and combining the results for Hibernate is probably the only way. But you may try to use JPA query language and/or different provider where subquery with multiple results can be used -- jpa eclipselink subquery in from clause
Related
I've got table
CREATE TABLE stp2_vehicles.can_data (
id bigserial NOT NULL,
receive_time timestamp NOT NULL,
gps_frame_id int8 NULL
);
based on it I need to add criteria predicate required for bigger query.
What i need in where clausule is select returning ids of can_data - only one id for each gps frame the one with earliest timestamp.
So in sql it would look like this:
select cd.id
from stp2_vehicles.can_data cd
right join
(select gps_frame_id, min(receive_time) as min_time
from stp2_vehicles.can_data
group by gps_frame_id
) cd2 on cd.gps_frame_id = cd2.gps_frame_id and cd.receive_time = cd2.min_time
;
unfortunately criteria api doesn't support joining with result of subquery.
I've tried also approach with self join (should be supported by criteria api) but solution I came with seems too computationally complex or has errors I can't see:
select cd.id
from stp2_vehicles.can_data cd
inner join stp2_vehicles.can_data cd2 on cd.id=cd2.id
where cd.receive_time = (select min(receive_time) from stp2_vehicles.can_data cd3 where cd.gps_frame_id = cd3.gps_frame_id );
By the way:
I've managed to solve simpler case:
select min(cd.id) from can_data cd group by cd.gps_frame_id
CriteriaQuery<CANData> criteriaQueryCanData = cb.createQuery(CANData.class);
Subquery<Long> subquery = criteriaQueryCanData.subquery(Long.class);
Root<CANData> canDataSubRoot = subquery.from(CANData.class);
subquery
.select(cb.least(canDataSubRoot.<Long>get(CANData_.ID)))
.groupBy(canDataSubRoot.<Long>get(CANData_.GPS_FRAME));
Since the subquery seems to be "static", you can define an entity for the result of the subquery by using the #Subselect annotation.
Hi I am looking for JPA criteria query (Java code) for the following sql
My SQL Query:
SELECT #today := now();
select r.globalid, r.groupid from review r
left join group ug on r.globalid = ug.globalid
and r.groupid = ug.groupid --having problem here???
and #today between r.startdate and r.enddate
where ug.globalid is null
These are two entities named:
Review (ID(Primary Key),GlobalId , GroupID,.....)
Group (ID (Primary Key), GlobalId, GroupID, .....)
in the above two entities there are multiple columns.However,the two columns in both the entities are same they are, globalid and groupid
I would like to join both the tables with the matched columns using criteria query(Java JPA with Hibernate) as there is no entity relationship exists between these two entities I am confused how to join them.
Edit:
This is purely using JPA.
Any help is greatly appreciated.
I'm trying to imagine how to use jOOQ with bridge tables.
Suppose you have
CREATE TABLE TableA (
id BIGSERIAL PRIMARY KEY
)
CREATE TABLE TableB (
id BIGSERIAL PRIMARY KEY
)
CREATE TABLE TableBridge (
id BIGSERIAL,
table_a_id INTEGER NOT NULL,
table_b_id INTEGER NOT NULL,
CONSTRAINT tablea_pk_id PRIMARY KEY (table_a_id)
REFERENCES TableA (id) MATCH SIMPLE,
CONSTRAINT tableb_pk_id PRIMARY KEY (table_b_id)
REFERENCES TableB (id) MATCH SIMPLE
)
When mapping this schema using jOOQ there will be three record classes, TableARecord, TableBRecord and TableBridgeRecord.
If I want to persist through an insert a record for TableA, should I simply first create and persist the TableB records, then persit rows for TableB and then manually add the TableBridge rows? Isn't there any way to automatically save also the rows in the bridge table?
There are several ways to solve this kind of problem:
1. Do it with a "single" jOOQ statement (running three SQL statements)
The most idiomatic way to solve this kind of problem with standard jOOQ would be to write a single SQL statement that takes care of all three insertions in one go:
ctx.insertInto(TABLE_BRIDGE)
.columns(TABLE_BRIDGE.TABLE_A_ID, TABLE_BRIDGE.TABLE_B_ID)
.values(
ctx.insertInto(TABLE_A)
.columns(TABLE_A.VAL)
.values(aVal)
.returning(TABLE_A.ID)
.fetchOne()
.get(TABLE_A.ID),
ctx.insertInto(TABLE_B)
.columns(TABLE_B.VAL)
.values(bVal)
.returning(TABLE_B.ID)
.fetchOne()
.get(TABLE_B.ID)
)
.execute();
The above works with jOOQ 3.8. Quite possibly, future versions will remove some of the verbosity around returning() .. fetchOne() .. get().
2. Do it with a single SQL statement
I assume you're using PostgreSQL from your BIGSERIAL data type usage, so the following SQL statement might be an option to you as well:
WITH
new_a(id) AS (INSERT INTO table_a (val) VALUES (:aVal) RETURNING id),
new_b(id) AS (INSERT INTO table_b (val) VALUES (:bVal) RETURNING id)
INSERT INTO table_bridge (table_a_id, table_b_id)
SELECT new_a.id, new_b.id
FROM new_a, new_b
The above query is currently not supported entirely via jOOQ 3.8 API, but you can work around the jOOQ API's limitations by using some plain SQL:
ctx.execute(
"WITH "
+ " new_a(id) AS ({0}), "
+ " new_b(id) AS ({1}) "
+ "{2}",
// {0}
insertInto(TABLE_A)
.columns(TABLE_A.VAL)
.values(aVal)
.returning(TABLE_A.ID),
// {1}
insertInto(TABLE_B)
.columns(TABLE_B.VAL)
.values(bVal)
.returning(TABLE_B.ID),
// {2}
insertInto(TABLE_BRIDGE)
.columns(TABLE_BRIDGE.TABLE_A_ID, TABLE_BRIDGE.TABLE_B_ID)
.select(
select(field("new_a.id", Long.class), field("new_b.id", Long.class))
.from("new_a, new_b")
)
);
Clearly also here, there will be improvements in future jOOQ APIs.
3. Do it with UpdatableRecords
In this particular simple case, you could get away simply by calling:
TableARecord a = ctx.newRecord(TABLE_A);
a.setVal(aVal);
a.store();
TableBRecord b = ctx.newRecord(TABLE_B);
b.setVal(bVal);
b.store();
TableBridgeRecord bridge = ctx.newRecord(TABLE_BRIDGE);
bridge.setTableAId(a.getId());
bridge.setTableBId(b.getId());
bridge.store();
I have a many-to-many relationship between two entities:
Entity A
ID
NAME
Entity B
ID
NAME
Join Table A_B
A_ID
B_ID
I'm trying to do an insert based off of the secondary unique attributes (names). I want to avoid fetching each entity by their name and then saving (2 queries & 1 insert vs 1 insert). Effectively, I'm looking to do something like this:
#Query(nativeQuery = true, value = "INSERT INTO A_B(A_ID, B_ID) VALUES ((SELECT ID FROM A WHERE NAME = ?), (SELECT ID FROM B WHERE NAME = ?))")
void addToJoinTable(String nameA, String nameB);
I saw this post, but I'm getting exceptions because an insert doesn't return a result set. The post mentions setting nativeQuery to true would resolve the issue, but I've had no such luck... Is there another & better way to do this?
Disclaimer: I am new to JPA, so this might be a stupid question...
I have a problem in creating subqueries with Hibernate. Unfortunately the Subqueries class is almost entirely undocumented, so I have absolutely no clue how to convert the following SQL into a Hibernate Criteria:
SELECT id
FROM car_parts
WHERE car_id IN ( SELECT id FROM cars WHERE owner_id = 123 )
I was hoping the following would 'just work':
session.createCriteria(CarParts.class).add(eq("car.owner", myCarOwner));
but unfortunately it does not. So it seems I actually have to use the Subqueries class to create the Criteria. But I was unable to find a reasonable example though Google, so that leads me to asking it here.
Try Like this:
Table details): Category (id, name, desc, parentId, active)
DetachedCriteria subCriteria = DetachedCriteria
.forClass(Category.class);
subCriteria.add(Restrictions.isNull("parent"));
subCriteria.add(Restrictions.eq("active", Boolean.TRUE));
subCriteria.add(Restrictions.eq("name", categoryName));
subCriteria.setProjection(Projections.property("id"));
Criteria criteria = getSession().createCriteria(Category.class);
criteria.add(Restrictions.eq("active", Boolean.TRUE));
criteria.add(Subqueries.propertyEq("parent", subCriteria));
It will generate the query like:
select
*
from
Categories this_
where
this_.active=1
and this_.parentId = (
select
this0__.id as y0_
from
Categories this0__
where
this0__.parentId is null
and this0__.active=1
and this0__.name='Health Plan'
)
Good Luck!
-Rohtash Singh
Try to create an alias for the "car" property before adding the eq expression like this:
session.createCriteria(CarParts.class)
.createAlias("car", "c")
.add(eq("c.owner", myCarOwner));
As first check the ORM configuration between Car and CarPart entities, usually you need the setup the relationship between them. After that try to execute the following code:
List result = session.createQuery("from " + CarPart.class.getName() +
" as parts join parts.car as car where car.owner = :myOwner")
.setParameter("myOwner", 123)
.list();