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()
Related
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();
I'm a beginner in the Spring Framework.
I'm using the hibernate-types for casting a PostgreSQL Interval into a Java Duration Object.
For selecting all rows this works fine. I've created a data model for the specific table and used the TypeDef annotation for the mapping:
#Entity
#Table(name = "test")
#Typedef(
typeClass = PostgreSQLIntervalType.class,
defaultForType = Duration.class
)
#Getter #Setter
public class Test {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private Duration flightDuration;
}
But now I want to create a custom native query. I want to select all rows from table Test, where the flightDuration is a RequestParam of a webservice.
Like this as a basic example (the real query is much more complex and needs some special postgres functions):
public interface TestRepository extends JpaRepository<Test, int> {
#Query( value = SELECT * FROM test t WHERE flight_duration = ?1 , nativeQuery = true )
List<Test> findByFlightDuration(Duration flightDuration);
}
But here is my problem. I don't know how to use the PostgreSQLIntervalType for casting the Duration into a PostgreSQL Interval.
If i execute the query like the way above, i'll get the following error:
org.postgresql.util.PSQLException: ERROR: cannot cast type bigint to interval
So where i have to set an annotation (or whatever) like the TypeCast in the Test Class, that the Duration will be casted into a PostgreSQLIntervalType?
There is no reasonfor using a native query. You can simply JPQL.
#Query( value = "FROM test t WHERE flight_duration = ?1")
List<Test> findByFlightDuration(Duration flightDuration);
If you REALLY need the nativeQery, because you need it, then you have to cast the integer in your query into an Interval of PostgreSQL.
I suppose you provide seconds in your query, and the ?1 is the number of seconds your flight will take.
Try the following:
#Query(value = SELECT * FROM test t WHERE flight_duration = (?1 || ' secs') , nativeQuery = true )
List<Test> findByFlightDuration(int flightDurationInSec);
More information here: https://www.postgresql.org/message-id/Pine.LNX.4.44.0410051443550.13144-100000#matrix.gatewaynet.com
But I suggest you to review if you really need this or not. Maybe you can try with JPQL. Let us know.
For native SQL queries, you need to use the Hibernate-specific setParameter Query method to provide the Hibernate Type that's needed to handle custom column types:
List<Test> tests = entityManager.createNativeQuery("""
SELECT *
FROM test t
WHERE flight_duration = :duration
""", Test.class)
.unwrap(org.hibernate.query.Query.class)
.setParameter("duration", flightDuration, PostgreSQLIntervalType.INSTANCE)
.getResultList();
This way, Hibernate will know how to handle the duration query parameter.
I created a table in a Database, so I got a "customerId" in that table and a "cardId", so a Customer can has multiples cardId's . What I tried right now is that:
public CustomerId getCustomerId(String cardId) {
this.getEntityManager();
return em.find(CustomerMappingHelper.class, customerId);
}
even though this won't work (I guess since my Ecplise shows me some Errors) ......furthermore I have in an another class a lookup methode, right now I stuck because I'm not sure how I can look up after a customer Id through an another card Id, just the logic behind that to look in that row ?
Used JPQL and solved my Problem, here I search the customerId through the cardId
public String getCustomerId(String cardId) {
Query q = em.createQuery("SELECT c.customerId FROM CustomerMapping c WHERE c.cardId = :cardId");
q.setParameter("cardId", cardId);
String customerId = (String) q.getSingleResult();
return customerId;
The following is my select query in BrandMapper.xml.
<select id="getBrand" parameterType="String" resultMap="brandResult">
SELECT
B.bid as bid,
B.bname as bname,
B.avg_price as avg_price,
B.total_number as total_number,
P.pid as pid,
P.pname as pname,
P.bid as pbid,
P.bname as pbname,
P.specs as pspecs,
P.price as price
from Brands B left outer join Products P on P.bid = B.bid
where B.bname = #{bname, jdbcType=VARCHAR}
</select>
This is the interface
public interface BrandMapper {
BrandDAO getBrand(String bname);
}
This is the service class
#Service
public class BrandService {
#Autowired
private BrandMapper brandMapper;
public BrandDAO getBrand(String bname) {
System.out.println("Inside DBService getBrand");
return brandMapper.getBrand(bname);
}
}
My problem is that the getBrand function in BrandService returns a null value. If I replace the parameter #{bname} inside the BrandMapper.xml by a hardcoded string it works and returns the correct class. What am I doing wrong here? Is there any logs or anything available where I can see the actual query which is being constructed? Any help is appreciated.
I managed to enable loggin using log4j and this is the query which is getting executed
SELECT B.bid as bid, B.bname as bname, B.avg_price as avg_price, B.total_number as total_number, P.pid as pid, P.pname as pname, P.bid as pbid, P.bname as pbname, P.specs as pspecs, P.price as price from Brands B left outer join Products P on P.bid = B.bid where B.bname = ?
The parameter is not getting replaced. I cant figure out what I am doing wrong here.
The question mark is a placeholder in Prepared Statements, the logged query is perfectly fine and looks as expected. The real value should be passed along the query to your database as a separate parameter.
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;