JPA #NamedQuery with two tables, possible? - java

I'm having a kind of dummy problem, I need to make a #NamedQuery with a join with other table, some simple thing.
But in all my #NamedQuery I'm only dealing with my mapped Object/Table.
For example in my Object/Table Mapped cars:
#NamedQuery(name = Cars.GET_AVAILABLE_CARS,
query = "select c.id from Cars c where c.status = (select d.status from CarStatus d where d.color=red)")
I'm trying to use: #SecondaryTables but no success for now.
One other thing that is working is give all things from other table as a parameter, but I don't think this will be good in performance.
Like:
#NamedQuery(name = Cars.GET_AVAILABLE_CARS, query =
"select c.id from Cars c where c.status = :" + CarStatusParam)
Any tips?
Thanks in advance

A named query can use everything that an API query can use, so can clearly do subqueries. Which implementation of JPA ?

I guess that you have something like this:
#Entity
public class Cars{
private String status;
//... something else
}
#Entity
public class CarStatus{
private String status;
private String color;
//... something else
}
if so, change this
#Entity
public class Cars{
private CarStatus status; //<--THIS!!
//... something else
}
#Entity
public class CarStatus{
private String color;
//... something else
}
and then update your NamedQuery to this:
query ="select c.id from Cars c where c.status.color = 'red'"
If I am wrong and the tables should not be related that way, then you should change your query tu use a join instead of a subquery. Something like this:
query = "select c.id from Cars c join CarStatus d where c.status = d.status and d.color = 'red'"

What you are trying to do is not a join. It is a subselect. And in terms of performance it is as (in)efficient as getting the param beforehand.
If you insist, however, to use the subselect, the JPA Query Language support it. As it supports joins.
Take a look here (at 7.11 8.11 . Subqueries).

The answer is yes it's possible. You need to make sure your columns define which table to look in. See the below code, you named query should work after that addition.
#Column(table = "SECONDARY_TABLE", name = "EXAMPLE_COLUMN_NAME")
private String example;

Related

Spring Data Jpa to get data which doesn't have entity representation

I'm trying to find the best way to map my data on ORM. I have a query which gets me data from MySQL database which look like
SELECT d.ID AS Id,
equipment.EQUIP_ID,
equipment.EQUIP_REFERENCE
FROM
tbl_devices d
INNER JOIN
tbl_equipment equipment ON equipment.EQUIP_ID = d.DEV_ID
What would be the most optimal way to get these data with Spring boot and Spring data??
Should I use #Query annotation and execute this or somehow create entities for Equipment and Devices tables and then use JPQL/HQL to join tables in a query, but then how should I map the result??
Thanks in advance.
You can use JdbcTemplate (import from org.springframework.jdbc.core.JdbcTemplate) to run the SQL statement above you wrote. After you can create a pojo to map result into it via BeanPropertyRowMapper. For example:
final String sql = "SELECT d.ID AS Id, equipment.EQUIP_ID, equipment.EQUIP_REFERENCE FROM tbl_devices d INNER JOIN tbl_equipment equipment ON equipment.EQUIP_ID = d.DEV_ID";
YourPojo result = jdbcTemplate.query(
sql,
new BeanPropertyRowMapper<>(YourPojo.class)
);
Pojo class maybe like following:
#Data
public class YourPojo {
private Long id;
private Long equipId;
private Your_type equip_reference_name;
}
A quick and dirty solution would be to use projections.
First, you create a projection interface:
public interface DeviceDetails {
Long getId();
Long getEquipId();
String getEquipReference();
}
You then modify the query to match column aliases with the projection properties:
SELECT d.ID AS id,
equipment.EQUIP_ID as equipId
equipment.EQUIP_REFERENCE As equipReference
...
Finally, you put the query method in a repository of your choice:
#Query(value =..., nativeQuery = true)
List<DeviceDetails> findDeviceDetails();

JPA Query Missing alias and column (Hibernate)

I have the following relevant JPA annotated classes in a Spring-Boot JPA enabled project (All Groovy Code):
#Entity
abstract class Character {
#Id
String id;
String name;
#ElementCollection(targetClass = Episode)
#Enumerated(EnumType.ORDINAL)
Collection<Episode> appearsIn;
}
#Entity(name = "Human")
public class Human extends Character {
String homePlanet;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "favorite_droid_id")
Droid favoriteDroid;
}
public enum Episode {
PHANTOM_MENACE,
ATTACK_OF_THE_CLONES,
REVENGE_OF_THE_SITH,
A_NEW_HOPE,
EMPIRE_STRIKES_BACK,
RETURN_OF_THE_JEDI,
THE_FORCE_AWAKENS
}
When I attempt to execute the following JPA Query:
def query = em.createQuery("from Human h where h.appearsIn in (:episodes)");
query.setParameter("episodes", EnumSet.of(Episode.THE_FORCE_AWAKENS));
def result = query.getResultList();
The generated SQL statement does not seem to have the alias to the Character table or the column name for appears_in:
select human0_.id as id2_0_, human0_.name as name3_0_, human0_.favorite_droid_id as favorite6_0_, human0_.home_planet as home_pla5_0_
from character human0_
cross join character_appears_in appearsin1_
where human0_.dtype='Human' and human0_.id=appearsin1_.character_id and (. in (?))
I have also tried using equals instead of in, with the same behavior:
from Human h where h.appearsIn = :episode
Produces the following SQL:
select human0_.id as id2_0_, human0_.name as name3_0_, human0_.favorite_droid_id as favorite6_0_, human0_.home_planet as home_pla5_0_
from character human0_
cross join character_appears_in appearsin1_
where human0_.dtype='Human' and human0_.id=appearsin1_.character_id and .=?
Any help is greatly appreciated.
Your query is invalid - as #Neil Stockton pointed out, by writing h.appearsIn in (:episodes) you are saying "collection in collection" which does not make sense.
You should rather declare a "collection member variable" like this:
select distinct h
from Human h
join h.appearsIn ai
where ai in (:episodes)
ai represents a single element of appearsIn (like an iterator).

order by when using Constructor Expressions in JPA 2

As a part of JSR-338 came the feature of using Constructor Expressions.
The question is how I can use order by when using Constructor Expressions.
Given the example JPQL:
select com.acme.AggregatedInfo(
c,
(select status from Account where ssn = c.ssn)
)
from Customer c
where m.id in (:customerIdList)
And the class AggregatedInfo
class AggregatedInfo {
private final Customer customer;
private final String status;
public AggregatedInfo(Customer customer, String status) {...}
// boilerplate javacode....
}
Im using this from a DAO like this:
public List<AggregatedInfo> getAggregatedResult(final List<Long> customerIdList)
return em.createQuery(hql, AggregatedInfo.class)
.setParameters("customerIdList", customerIdList)
.getResultList();
}
If I want to order by the status - how can this be done via JPQL ?
I have tried the following without success:
select com.acme.AggregatedInfo(
c,
(select status from Account where ssn = c.ssn)
) as a
from Customers c
where m.id in (:customerIdList)
order by c.status
But this does not work.
Is it doable ?
Please explain.
Try the following. It worked for me with the similar implementation in Hibernate. It should work for you too
public List<AggregatedInfo> getAggregatedResult(final List<Long> customerIdList)
return em.createNativeQuery(hql, AggregatedInfo.class)
.setParameters("customerIdList", customerIdList)
.getResultList();
}
Replace entityManager.createQuery() with entityManager.createNativeQuery()

Criteria API ignores JOIN

I have a simple criteria api query with a (inner) join
public void find(Category category) {
CriteriaBuilder b = getQueryBuilder();
CriteriaQuery<Product> q = createQuery();
Root<Product> root = q.from(Product.class);
Join<Product, Category> myCategory= root.join("category");
q.where(b.equal(myCategory, category));
entityManager.createQuery(q).getResultList();
}
The query works, but if I enable in persistence.xml the sql logging I can see that the query is a
SELECT * FROM product, category WHERE ...
and no
SELECT * FROM product join category on category.id = product.category ...
Any idea what the reason for this is? The where statement is very slow, so a real join would be really better.
I'm using eclipselink 2.5.1, Java EE7 and postgres
I also have a superclass from Product
#Entity
#Audit
public class SuperClass {}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class Product extends SuperClass {}
But this should be no problem?
A join is actual being perform, however the SQL used to perform the join is written in implicit join notation. The join will have the same performance as explicit join notation (notation using JOIN and ON). A "real" join is being performed, however it is just not in the explicit join notation (aka ANSI SQL 92) you expect.
You are selecting from Root with Product.class, but you need to select from join.
And I think result of you join must be mapped to some class, containing both entities Product and Category. If you like to get only product from join you can write something like this:
CriteriaBuilder b = getQueryBuilder();
CriteriaQuery<Product> q1 = createQuery();
CriteriaQuery<Product> q2 = createQuery();
Root<Product> root = q1.from(Product.class);
q2.select(root.as(Product.class)).from(root.join("category"))
entityManager.createQuery(q2).getResultList();

Simple hql named query that uses a inner join

I want to do something like this in my domain/entity object :
#Entity
#NamedQueries({
#NamedQuery(name="favouriteCats", query="from Cat c inner join on UserCat uc where uc.isFavourtie = true and uc.user = :user")
})
public final class Cat extends BaseTable
So that in my service layer I can do this :
Query query = session.getNamedQuery("favouriteCats")
query.setParameter(0, MyUser);
return query.list();
However, my syntax in HQL is incorrect - and aftern ten minutes looking at official docs I have decided to give up and ask here ... ?
My usercat table is joined like so :
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumn(name="cat_fk", insertable=false, updatable=false)
private cat
The sql is this, it works fine at my db command prompt:
select c.*
from cat as c inner join usercat as uc on c.id = uc.cat_fk
and uc.isFavourite = 1 //bit field
and uc.user_fk = 74 //just user id
Is it just me or is the hibernate documentation rather painful, and do you find yourself often wondering whether it would be quicker just to write normal jdbc prepared statements to populate your pojos/domain objects/dto's... ?
I think this might work for you, but I am guessing your Usercat class here:
select c from Usercat as uc inner join uc.cat as c where uc.isFavourtie = true and uc.user = :user
Case Issue, Right query would be:
from Cat c inner join on Usercat uc where uc.isfavourtie = true and uc.user = :user
Note : C in Cat is capital, U in Usercat is capital where as c in Usercat is small and f in isfavourite is small.

Categories