How would you get a list/array of the distinct values in a table using JPA?
Lets say I have an entity and repository of a Foo object from a table that has columns a, b, c. All I want is to determine all unique(distinct) values in column b where c equals "bar" and have it (the distinct values of b)returned as a list of Strings (not Foo objects).
Other ORM's I have found it very simple to do, but I can't seem to figure out how to do this via JPA. It's a query that's not mapped to an object, but rather just extracting values as a simple list of Strings.
Can this be done in JPA, if so, how?
Just use this simple JPQL query:
select distinct foo.b from Foo foo where foo.c = 'bar'
query is one way as said by JB Nizet
Another way with java8, i would define these methods in repository itself. Find distinct Foos and then convert to list of b and return.Call this method with any parameter you want.
public interface FooRepository extends CrudRepository.. {
// name this method whatever you want
default List<String> findDistinctbs(final String b) {
// note following is jpa convention to get list of Foos..
List<Foo> fooList = findDistinctByb(String b);
List<String> bList = fooList.stream().map(Foo::getB).collect(Collectors.toList());
return bList;
}
Related
So I have an abstract class that prepares my query up to after where clause. It looks something like this:
SelectConditionStep<Record2<Integer, String>> whereQuery = dslContext.select(FOO.DIGITS, FOO.WORD)
.from(FOO)
.where(/*some conditions*/);
It then returns whereQuery and that instance is used by concrete implementations to add stuff onto it.
Is it possible to make this call return SelectConditionStep<MyClass> so that I don't have to write all Record types in method signature (note that this is a simplified version, imagine having Record10). MyClass would, in this example, have two fields, Integer and String fields.
Or if not that, is there any other way to do it.
I am using Postgres as a db
Assuming you have an immutable POJO MyClass, e.g. a Java 16 record:
record MyClass(int digits, String word) {}
You could use a nested record to achieve something similar:
Select<Record1<MyClass>> whereQuery =
ctx.select(row(FOO.DIGITS, FOO.WORD).mapping(MyClass::new))
.from(FOO)
.where(...)
so I'm working on a website (Angular frontend, Java backend).
I've got two different #Entity classes. They're connected as 1:1.
Let's call them A and B. A has attributes a1, a2 and b_id, B has b1, b2.
Now I made an interface which extends CrudRepository<A, Long> where I join those two tables via JPQL.
The problem is now: whenever I want to order them by an attribute present in A it works and when I want to sort them by an attribute in B it says "can't find b1 in A".
Example query:
#Query("SELECT ... FROM A a JOIN B b ON a.b.id = b.id")
Page<CustomTableRow> test(Pageable pageable);
Current pageable:
PageRequest.of(page, pageSize, Sort.by(direction, attributeName))
At first I thought Spring would automatically search for the attribute in one of the two classes but it seems it doesn't.
One workaround would be to set a prefix for the attributeName depending on where I expect them to be, meaning I have to switch-case my way through all attributes I have (like 20) and set either an "a." or "b." in front of the attributeName from the HTTP request, to get stuff like this:
Sort.by(direction, "b.b2") or Sort.by(direction, "a.a2") instead of, say, Sort.by(direction, "b2"), which would produce the error mentioned above.
But that's ugly. Is there something better?
I have a situation where I need to return only few fields of a POJO.
Here is a SOF Question: retrieve-single-field-rather-than-whole-pojo-in-hibernate question regarding the same, but few things still seems to be obscure.
1) The answer suggests to use -
String employeeName = session.createQuery("select empMaster.name from EmployeeMaster empMaster where empMaster.id = :id").setInteger("id",10).uniqueResult();
So, here is my concern - Every pojo field is normally private, so "empMaster.name" will simply not work. And am not sure if empMaster.getName() is the solution for this. Will calling the getter methods work?
2) If i am querying multiple fields, (which is my case) (assuming getter methods work) the query will be some thing like -
List<String> employeeDetails = session.createQuery("select empMaster.getName(), empMaster.getDesignation() from EmployeeMaster empMaster where empMaster.id = :id").setInteger("id",10).uniqueResult();
Note the return type has changed from String to List<String>.
2(a) Hope this is right?
2(b) what if i am interested in age/salary of employee which will be of int type. I think the return type will be List<String> or List<Object>. Well any how in the application i can recast the String or Object to the proper type int or float etc. So this should not be a problem.
3) Now what if I am querying multiple employee details (no where clause), so the query will be something like - (not sure if the part of query after from is correct)
List<List<<String>> employeesDetails = session.createQuery("select empMaster.getName(), empMaster.getDesignation() from EmployeeMaster;
Anyway, point here is to emphasise the change in the return type to : List<List<<String>> employeesDetails. Does it work this way ???.
(The question quoted above also has answers pointing to use Projections. I have questions about it but will post them on another question, don't want to mixup.)
I will list the points in the order you mentioned them:
The query has nothing to do with the POJO's field visibility. You are doing a simple query to the database, as if you were doing a query using SQL, and columns in a table have nothing to do with the fact that their mapped POJOs' fields in an application are public or private. The difference is only the language that you're using: now you're using the Hibernate Query Language (HQL), which allows you to express your query with respect to the POJOs' definitions instead of the database's tables' definitions. In fact, doing
session.createQuery("select empMaster.getName() from EmployeeMaster...");
will throw a syntax error: there can be no parenthesis in an object's field name.
By the way, you have to parse your query result to a String, otherwise there would be a compiler semantics error.
String name = (String) session.createQuery("select empMaster.name from EmployeeMaster...").setYourParameters().uniqueResult();
When you do a query whose SELECT clause contains more than one field, and you call uniqueResult(), you're obtaining an array of Objects (Object[]). You iterate through each element in the array and cast it to the corresponding type.
First of all, you probably forgot to add a .list() method call at the end of your query.
session.createQuery("select empMaster.name, empMaster.designation from EmployeeMaster").list();
Otherwise, the query will not be executed, and you would just have a QueryImpl object waiting to be triggered.
Now, when you're expecting multiple results in your query and selecting several fields, you're getting back a List<Object[]>. The elements of the list will be the array of fields for a specific record, and the array per se will be the fields.
For example, I have a base entity called 'MyEntityParent' and two sub types 'ZChild' and 'AChild'.
When using the following HQL, this will sort the result list by the type's internal integer value (same as the special class property):
select e from MyEntityParent e
order by type(e)
However, I need to sort the result list by their entity type's name. I.e., first, instances of type 'AChild', then, instances of type 'ZChild'.
Try
select e from MyEntityParent e order by e.class
From the Hibernate 3.6 documentation:
The special property class accesses the discriminator value of an instance in the case of polymorphic persistence. A Java class name embedded in the where clause will be translated to its discriminator value.
http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/queryhql.html#queryhql-where
But this is deprecated in Hibernate 4.1
Also this is not exactly your wish as it returns the discriminator value, not the exact type name if i read the docs correctly.
There is no straightforward solution, because HQL is translated to SQL and database is not aware of how the value maps to the name of the class. There is way to make it, but it is not nice:
Query gets complex, because we have to define order in query.
Select list contains additional value that defines order, so we cannot return list of entities.
When model changes, query should be modified
Query is:
SELECT e,
CASE TYPE(e) WHEN AChild THEN 1
WHEN ZChild THEN 2
ELSE 3
END AS orderValue
FROM MyEntityParent e ORDER BY orderValue
And as said, result is no more list of entities, it is list of object arrays, so accessing entity is bit more hard. Entity itself is first item in array (index 0) and ordedValue in second:
List<Object[]> result = em.createQuery("JPQL given above").getResultList();
for (Object[] resultRow : result) {
MyEntityParent myEntityParent = (MyEntityParent) resultRow[0];
}
Solution is valid JPQL, so it is not Hibernate specific, but works also with other JPA providers.
I'm still a little new to hql, and I had a question about aggregation functions and efficiency for a query I'm writing.
Let's say I have this class mapped in hibernate (getters/setters/constructors/etc. omitted for brevity):
public class Foo
{
private int i;
private String s;
private float f;
}
I want to do a hql query to get the Foo instance with the highest i value and specified s & f values. My current solution is this:
List<Foo> fooList = (List<Foo>)session.createQuery(
"from Foo as foo where foo.s = :str and foo.f = :val order by foo.i desc").
setParameter("str", <stringgoeshere>).setParameter("val", <floatgoeshere>).
list();
return fooList.get(0);
If I understand correctly, this method will have the N+1 selects problem (although as long as I'm only grabbing the first result from the list, N will hopefully be 1, but still). I assume that there's some way of doing this with uniqueResult(), but I don't quite understand how the aggregation functions would work in this case. The only examples I've been able to find of the "max()" are either in the where clause or the returned value.
How should I write this query to do it in a single select and grab the Foo instance with the highest i?
Thanks for the help!
If I understand correctly, this method will have the N+1 selects problem
I don't see how your query could result in n+1 selects. There is no reason for Hibernate to iterate over the results of the first query and to perform subsequent queries here.
I assume that there's some way of doing this with uniqueResult(), but I don't quite understand how the aggregation functions would work in this case.
If you want to retrieve a single result, it would be indeed smart to not retrieve the entire table. First, the query would be more efficient and second, you're not going to waste memory for nothing (and possibly even make the Session explode). What if your table contains a million of records?
How should I write this query to do it in a single select and grab the Foo instance with the highest i?
As just mentioned, retrieve only the wanted record, use setMaxResults(1) to limit the number of results (this will generate the proper SQL order depending on your database):
Foo foo = (Foo) session.createQuery(
"from Foo as foo where foo.s = :str and foo.f = :val order by foo.i desc")
.setParameter("str", <stringgoeshere>).setParameter("val", <floatgoeshere>)
.setMaxResults(1)
.uniqueResult();
return foo;