Hibernate Projection - Getting Back Same Object - java

Let's say I have a Person JPA entity:
#Entity
public class Person {
#Id
private Long id;
#Column(name="name")
private String name;
#Column(name="age")
private Integer age;
#Column(name="hobbies")
private List<String> hobbies;
public Person() {};
}
Using a Criteria Query, is it possible to retrieve a List<Person>, but only include each Person's name?
Looking at Java Persistence with Hibernate, I see that there's a Criteria#setResultTransformer method.
I think that I could transform my results to a ReducedPerson class that only contained a name. However, I'd like to select only the Person's name, but still get Person objects back.
Is this possible?

You're correct, you'll need to set a ResultTransformer. For what I know in this area of hibernate, which is not much, the transformers provided by Hibernate are strict and will fail if a value is missing, so I think you'll need to create your own instance of ResultTransformer. I would suggest to look at the code from AliasToBeanResultTransformer and make a lenient version of it.

You can do this using Constructor Expressions:
http://en.wikibooks.org/wiki/Java_Persistence/JPQL#Constructors
You can use your existing Person class (instances returned from your query being, of course, unmanaged) but would however need to a constructor taking the name.
SELECT NEW sample.Person(p.name) FROM Person p

Single Projection
You use an existing ResultTransformer like this
Criteria query = session.createCriteria(Person.class)
.setProjection(Projections.property("name").as("name"))
.setResultTransformer(Transformers.aliasToBean(Person.class));
List personNames = query.list();
Multiple Projections
The example List above now contains only Person objects with their name. Regullary, you want also to retrieve the id for the persons at least. You can setup multiple properties with this
ProjectionList colProjection = Projections.projectionList();
colProjection.add(Projections.property("id"), "id");
colProjection.add(Projections.property("name"), "name");
Criteria query = session.CreateCriteria(Person.class)
.setProjection(colProjection)
.setResultTransformer(Transformers.aliasToBean(Person.class));
List persons = query.list();
Key in this approach is to set the same aliases as their original property names, so we dont need to build our own ResultTransformer.

Related

Join operation with mongodb using query criteria

I have An Order POJO with a list of products as follows:
#Data
#Document
#AllArgsConstructor
#NoArgsConstructor
public class Order {
#Id
#Generated
private String id;
private String label;
private Collection<Product> products;
private OrderStatus orderStatus;
public Order(String label, List<Product> products, OrderStatus orderStatus) {
this(null, label, products, orderStatus);
}
}
I'm trying to use filters on the order document, and I'm using query and criteria to do so.
It works just fine for the primitive fields, for example:
query.addCriteria(Criteria.where(orderField).in(valueList));
mongoTemplate.find(query,Order.class);
But now I'm trying to find all the orders containing a product with a given Id.
I guess what I'm trying to do is to perform join on the order collection and on the product collection so that a product matches the Id I want to filter by.
I know I can to it easily by using mongorepository, but I'm trying to find a way to do so with criteria.
Latter on I want to be able to perform 'and' and 'or' operation on my filters.
So I don't wanna mix mongoRepository with query and criteria filtering implementations.
I want through the criteria java doc
and these answer: Spring Data, MongoDB. How to get nested object (non-document) from document's array
and tried to to it like this:
query.addCriteria(Criteria.where("products.id").is(((OrderProductExpression) expression).getValueId()));
Any help would be very appriciated
Ok So I figured it out- might be helpful for someone out there one day-
The problem was that I used #DBRef in my db schema.
Removed this annotation and it worked like a charm.

java jpa : list and sublist

I have a java object that I have persisted using JPA. This object has a member 'friendsList' that is a list of strings. I have used the #CollectionOfElements hibernate annotation on this object. The result of this is that I have two tables, one that persists my object, and another that persists the lists of each of these objects.
How can I retrieve (using jpa) all objects whose lists contain 'hello' or 'world' ?
Following is working fine : it will also create a Parent_names table for maintaining relationships. Don't annotate with #OneToMany
#ElementCollection(targetClass = String.class)
private List<String> names;
Or I think you can try a workaround :
Wrap string inside a class.
class FriendsName{
private String name;
//getter setter
}
Then,
class Parent{
#OneToMany
List<FriendsName> friendsName;
}
And you can do JPQL like:
select p from Parent p where p.friendsName.name in ('hello','world')

Querying an entity with filters on a OneToMany attribute gives a weird result

I use EclipseLink for 9 months and so far no problem. Since I have the
need to query an entity with a OneToMany attribute, it's all the contrary.
It gives me a strange result.
I have simplified my entities until the maximum but the problem remains.
I will explain my need which is ultra simple : I have two entities :
Person which has a bidirectional relation with Address.
Person has potentially several Addresses but an Address belongs to one and
only Person.
In Classes, it gives that :
#Entity
public class Person implements Serializable {
#Id
private Long id;
#OneToMany(mappedBy = "person", fetch = FetchType.LAZY)
private Set<Address> addresses;
// Getter and setter
...
}
#Entity
public class Address implements Serializable {
#Id
private String idAddress;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "idPerson", referencedColumnName = "idPerson")
private Person person;
// Getter and setter
...
}
I want to query personne with their adresses. All that with some conditions
on personne and adresse.
My simplified query :
select pers FROM Person pers join pers.addresses address
where pers.matricule=:matricule
and address.date=:dateContract
When I execute it, I retrieve the right person but with all addresses
linked (with foreign key) with this person. Even the addresses which don't
match with the dateContract condition.
It seems that it's a problem related to the use of filtering on a oneToMany
attribute in my query. The problem is solved if i do several requests but
it will give low performances as I have several requests like this.
I have tried with the oneToMany in eager initialization and with a
fetch-join query hint but i have got the same result.
Thank you for having read me :)
PS : I have written the code manually, so a little typo is not impossible
David
Your query only returns persons. Once you get the persons, you're calling getAddresses(), which lazily loads the addresses of the person - all of them. In short, the query limits the set of returned persons, but since it only returns persons, the addresses are lazy-loaded using another query when accessing the set of addresses.
What you want to do is return the persons with some of their addresses in a single query. To do that, you need to use the fetch keyword:
select distinct pers FROM Person pers
join fetch pers.addresses address
where pers.matricule = :matricule
and address.date = :dateContract
Be very careful, though: this query returns an incorrect view of the person entity. You should make sure not to modify the collection of addresses of the returned persons (although since the addresses association is mapped by the Address.person association and there is no cascade, you should not have problems in this particular case).

Hibernate : many to many linker table, best OO design

If I have 3 tables, with the expected normal columns : Customer, CustomerProductLinker and Product.
And I want in my Java code to do this :
Customer customer = myService.getCustomer(id); //calls hibernate session
List<Product> customerProducts = customer.getProducts();
What would my 3 entities look like and the respective collections within each, specifically the getProducts() method ? Or is it better to use HQL and a named query for this ?
I am creating the databse tables from the java code (using the create option in hibernate conf), so the table desgin can be altered if preferred.
Try #ManyToMany relationship using #JoinTable. A customer has a set (or a list) of products. A product has a set (or a list) of customers.
#Entity
public class Customer {
#ManyToMany(cascade= CascadeType.ALL)
#JoinTable(name="customer_product",
joinColumns={#JoinColumn(name="customer_id")},
inverseJoinColumns={#JoinColumn(name="product_id")})
private Set<Product> products = new HashSet<Product>();
...
#Entity
public class Product {
#ManyToMany
#JoinTable(name="customer_product",
joinColumns={#JoinColumn(name="product_id")},
inverseJoinColumns={#JoinColumn(name="customer_id")})
private Set<Customer> customers = new HashSet<Customer>();
...
I would set up the entities like wannik suggested. Try to keep it simple. If you start using named queries you are doing more work and you are just covering an specific case.

Hibernate polymorphic query

I have two classes, Person and Company, derived from another class Contact. They are represented as polymorphically in two tables (Person and Company). The simplified classes look like this:
public abstract class Contact {
Integer id;
public abstract String getDisplayName();
}
public class Person extends Contact {
String firstName;
String lastName;
public String getDisplayName() {
return firstName + " " + lastName;
}
}
public class Company extends Contact {
String name;
public String getDisplayName() {
return name;
}
}
The problem is that I need to make a query finding all contacts with displayName containing a certain string. I can't make the query using displayName because it is not part of either table. Any ideas on how to do this query?
Because you do the concatenation in the Java class, there is no way that Hibernate can really help you with this one, sorry. It can simply not see what you are doing in this method, since it is in fact not related to persistence at all.
The solution depends on how you mapped the inheritance of these classes:
If it is table-per-hierarchy you can use this approach: Write a SQL where clause for a criteria query, and then use a case statement:
s.createCriteria(Contact.class)
.add(Restrictions.sqlRestriction("? = case when type='Person' then firstName || ' '|| lastName else name end"))
.list();
If it is table per-concrete-subclass, then you are better of writing two queries (since that is what Hibernate will do anyway).
You could create a new column in the Contact table containing the respective displayName, which you could fill via a Hibernate Interceptor so it would always contain the right string automatically.
The alternative would be having two queries, one for the Person and one for the Company table, each containing the respective search logic. You may have to use native queries to achieve looking for a concatenated string via a LIKE query (I'm not a HQL expert, though, it may well be possible).
If you have large tables, you should alternatively think about full-text indexing, as LIKE '%...%' queries require a full table scan unless your database supports full text indexes.
If you change displayName to be a mapped property (set to the name column in Company and to a formula like first||' '||last in Person), then can query for Contract and Hibernate will run two queries both of which now have a displayName. You will get back a List of two Lists, one containing Companies and one containing Persons so you'll have to merge them back together. I think you need to query by the full package name of Contract or set up a typedef to tell Hibernate about it.

Categories

Resources