Can I query in hibernate via DTO/Entity not by single fields - java

I have a question regarding querying in hibernate. If there is a way to search without specifying an where cause explicitly?
So what I mean is: let's say I have a search form with 10 columns that are bound to my dto fields. So the user can fill some of them and left the rest as nulls. And now I would like to search only by fields that are specified (filled) and left the nulls behind (they doesn't matter).
So the query would be like this:
select e
from entity e
where e.entity = e.searchedCriteriaEntityGivenInDTO
Or a better example via jpg: I'd like to have all sample entities without specifying "where number, where name, where firstanme", but over my dto by "where dtoFields". As mentioned the nulls should be ignored.
Thanks a lot in advance.
[EDIT]: Thanks to Dragon I have a great solution how to do it, but I have one more question: What about I have 2 row's, I can search? My query should look like:
select e
from example e,
where (e.entity = example) OR (e.entity = example2);
I tried to put the
session.createCriteria(MyEntity.class).add(Example.create(myEntityExample))
into an OR-Predicate but it seems it does not work.
Any suggestions?

No, you can't do it with DTOs, but you can use a prototype (example) entity instance for it:
MyEntity myEntityExample = new MyEntity();
myEntityExample.setNumber(12);
myEntityExample.setName("AA");
myEntityExample.setFirstName("BB");
List<MyEntity> results = session.createCriteria(MyEntity.class)
.add(Example.create(myEntityExample))
.list();

Related

How can I obtain the first row from a SELECT operation using JPQL?

I have a database with data about some tests, I want to order them decently using their attribute DATE and take only the first one of all of them. I mean, the equivalent TOP 1 of SQL in JPQL.
Thanks for your help!
In spring jpa you can do something like this
Foo findTopByOrderByDateDesc(); //This will return 1st item
List<Foo> findTop10ByOrderByDateDesc(); //This will return top 10 item
For reference Spring Jpa Doc
You normally set that on the Query object before triggering the fetch:
entityManager.createQuery("...")
.setMaxResults(1)
.getResultList();
With the use of Spring Data Jpa syntax you would use something like:
Optional<Test> findFirstByOrderByDateDesc();
Or using Pageable:
Page<Test> test = repository.findAll(
PageRequest.of(0, 1, Sort.by(Sort.Direction.DESC, "date")));
Mostly common
Foo findFirstByOrderByDateDESC();
Using #Query with nativeQuery = true
#Query(value="SELECT 1 * FROM "TableName" ORDER BY "DATE in db" DESC LIMIT 1", nativeQuery = true)
Foo findFirstByOrderByDateDESC(Long id); // name can be random
In Spring Data JPA, use the keywords first or top, both are semantically the same, i prefer top, because there are 2 less letters to type ;-).
Foo findTopByOrderByDateDesc(); //Top 1 assumed
Foo findFirstByOrderByDateDesc(); //First 1 assumed
If you want to find the first X rows, where X is a number, use:
List<Foo> findTopXByOrderByDateDesc();
List<Foo> findFirstXByOrderByDateDesc();
If you want to remember to deal with null returns, wrap the result in an Optional:
Optional<Foo> findTopByOrderByDateDesc();
Optional<Foo> findFirstByOrderByDateDesc();
The question specifically asks how to do it in JPQL. The answer is that you can't. You either need to use a JPA query derived from a method name (as in jumping_monkey's answer) or use a native query.

Restrict JPA polymorphic query with Criteria API [duplicate]

I'm relativley new to relational databases and I have some problems concerning the creation of queries. First I want to explain the situation shortly. I have several entity classes. All of them extend AbstractEntity or EntityProperty. So entities can have properties and properties have owning entities, so there is a bidirectional relation.
Now let's say ConcreteEntity extends AbstractEntity and I want to create queries like this: Get all entities of type ConcreteEntity which has at least on property with a name contained in the given list propertyNames. Until now I have the following working criteria query:
CriteriaQuery<AbstractEntity> cq = cb.createQuery(AbstractEntity.class);
Root<EntityProperty> property = cq.from(EntityProperty.class);
Join<EntityProperty, AbstractEntity> entity = property.join(EntityProperty_.owningEntities);
cq.where(property.get(EntityProperty_.name).in((Object[]) propertyNames));
cq.select(entity);
But now I want only those entities of type ConcreteEntity. How could I achieve this?
In JPQL I wrote "SELECT entity FROM EntityProperty property JOIN property.owningEntities entity" and here I also have no idea how to write it in the way that only a specific type is returned...
Thanks for answers in advance!
EDIT: moved the second question to criteria query: indistinct result lists and removed distinct in the code (that didn't work)
I know this is an old question but just in case someone stumbles upon the same problem, here is how it can be solved.
You can easily filter by entity type like this:
Predicate p = cb.equal(entity.type(), cb.literal(ConcreteEntity.class));
where entity can be a Path (Root and Join included), cb is a CriteriaBuilder object. So in your case it would be something like this:
CriteriaQuery<AbstractEntity> cq = cb.createQuery(AbstractEntity.class);
Root<EntityProperty> property = cq.from(EntityProperty.class);
Join<EntityProperty, AbstractEntity> entity = property.join(EntityProperty_.owningEntities);
cq.where(cb.and(
property.get(EntityProperty_.name).in((Object[]) propertyNames),
cb.equal(entity.type(), cb.literal.ConcreteEntity.class)
));
cq.select(entity);
The only way I found until now was to create an enumeration with a value for each class The resulting criteria query is
CriteriaQuery<AbstractEntity> cq = cb.createQuery(AbstractEntity.class);
Root<EntityProperty> property = cq.from(EntityProperty.class);
SetJoin<EntityProperty, AbstractEntity> entity =
property.join(EntityProperty_.owningEntities);
cq.where(property.get(EntityProperty_.name).in((Object[]) propertyNames),
entity.get(AbstractEntity_.entityType).in(suitableSubTypes));
cq.select(entity);
List<AbstractEntity> resultList = em.createQuery(cq).getResultList();
As you can see, every entity now has the attribute entityType. I also have to create the collection suitableSubTypes every time. Another problem is that the returned type is List<AbstractEntity>. What I wanted was a method signature like
public static <T extends AbstractEntity> List<T>
getEntities(Class<T> entityClass, String... propertyNames)
but for now I have
public static List<AbstractEntity>
getEntities(Collection<AbstractEntityType> suitableSubTypes,
String... propertyNames)
So I still hope there exists a better solution...

Can #Query annotation in Spring Data JPA take in a list of enums?

I've been trying to figure out how to take an input of a list of enums for my sql query. This is what it looks like:
#Query(value = "Select * from employees where city in :cities", nativeQuery = true)
List<Employee> findByCities(#Param("cities") List<City> cities);
I understand that for simple queries we can rely on the JPA Criteria API but I want to know if I can actually do it this way instead. Because if so, i can create more complicated queries (such as joins with another table) if I could have this flexibility of specifying the list.
Yes spring-data-jpa's #Query can take a list of enums.
This is my repository method
#Query("Select u from User u where u.userType in :types")
List<User> findByType(#Param("types") List<UserType> types);
This is my repository call
userRepository.findByType(Arrays.asList(AppConstant.UserType.PRINCIPLE)))
And this is the query logs
SELECT user0_.id AS id1_12_,
user0_.date_created AS date_created2_12_,
...
...
FROM users user0_
WHERE user0_.user_type IN ( ? )
Hope this helps.
PS: I tested this in my local machine with positive results.
Update 1
The same doesn't work with nativeQuery=true. I tested it on my local system and it doesn't seem to be working with native queries. However with JPQL it works fine as mentioned in the above answer.
May be this answer will help.

JPA - Setting entity class property from calculated column?

I'm just getting to grips with JPA in a simple Java web app running on Glassfish 3 (Persistence provider is EclipseLink). So far, I'm really liking it (bugs in netbeans/glassfish interaction aside) but there's a thing that I want to be able to do that I'm not sure how to do.
I've got an entity class (Article) that's mapped to a database table (article). I'm trying to do a query on the database that returns a calculated column, but I can't figure out how to set up a property of the Article class so that the property gets filled by the column value when I call the query.
If I do a regular "select id,title,body from article" query, I get a list of Article objects fine, with the id, title and body properties filled. This works fine.
However, if I do the below:
Query q = em.createNativeQuery("select id,title,shorttitle,datestamp,body,true as published, ts_headline(body,q,'ShortWord=0') as headline, type from articles,to_tsquery('english',?) as q where idxfti ## q order by ts_rank(idxfti,q) desc",Article.class);
(this is a fulltext search using tsearch2 on Postgres - it's a db-specific function, so I'm using a NativeQuery)
You can see I'm fetching a calculated column, called headline. How do I add a headline property to my Article class so that it gets populated by this query?
So far, I've tried setting it to be #Transient, but that just ends up with it being null all the time.
There are probably no good ways to do it, only manually:
Object[] r = (Object[]) em.createNativeQuery(
"select id,title,shorttitle,datestamp,body,true as published, ts_headline(body,q,'ShortWord=0') as headline, type from articles,to_tsquery('english',?) as q where idxfti ## q order by ts_rank(idxfti,q) desc","ArticleWithHeadline")
.setParameter(...).getSingleResult();
Article a = (Article) r[0];
a.setHeadline((String) r[1]);
-
#Entity
#SqlResultSetMapping(
name = "ArticleWithHeadline",
entities = #EntityResult(entityClass = Article.class),
columns = #ColumnResult(name = "HEADLINE"))
public class Article {
#Transient
private String headline;
...
}
AFAIK, JPA doesn't offer standardized support for calculated attributes. With Hibernate, one would use a Formula but EclipseLink doesn't have a direct equivalent. James Sutherland made some suggestions in Re: Virtual columns (#Formula of Hibernate) though:
There is no direct equivalent (please
log an enhancement), but depending on
what you want to do, there are ways to
accomplish the same thing.
EclipseLink defines a
TransformationMapping which can map a
computed value from multiple field
values, or access the database.
You can override the SQL for any CRUD
operation for a class using its
descriptor's DescriptorQueryManager.
You could define a VIEW on your
database that performs the function
and map your Entity to the view
instead of the table.
You can also perform minor
translations using Converters or
property get/set methods.
Also have a look at the enhancement request that has a solution using a DescriptorEventListener in the comments.
All this is non standard JPA of course.

Java-Hibernate-Newbie: How do I acces the values from this list?

I have this class mapped
#Entity
#Table(name = "USERS")
public class User {
private long id;
private String userName;
}
and I make a query:
Query query = session.createQuery("select id, userName, count(userName) from User order by count(userName) desc");
return query.list();
How can I access the values returned by the query?
I mean, how should I treat the query.list()? As a User or what?
To strictly answer your question, queries that specify a property of a class in the select clause (and optionally call aggregate functions) return "scalar" results i.e. a Object[] (or a List<Object[]>). See 10.4.1.3. Scalar results.
But your current query doesn't work. You'll need something like this:
select u.userName, count(u.userName)
from User2633514 u
group by u.userName
order by count(u.userName) desc
I'm not sure how Hibernate handles aggregates and counts, but I'm not sure if your query is going to work at all. You're trying to select a aggregate (i.e. the "count(userName)"), but you don't have a "group by" clause for userName.
If the query does in fact work, and Hibernate can figure out what to do with it, the results you get back will most likely be a raw Object[], because Hibernate will not be able to map your "count(userName)" data into any field on your mapped objects.
Overall, when you get into using aggregates in queries, Hibernate can get a little more tricky, since you're no longer mapping tables/columns directly into classes/fields. It might be a good idea to read up more on how to do aggregates in Hibernate, from their documentation.

Categories

Resources