Join operation with mongodb using query criteria - java

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.

Related

JPA Projection #OneToMany not working 'Unable to locate appropriate constructor'

I have a large entity-class with many, many fields and a projection-class which should be a part of the large one.
Everything works fine, except the #OneToMany field. The #OneToMany field should be a list of addresses, but when converting it to the projection-class I always get the error "Unable to locate appropriate constructor [...] Expected arguments are: long, [...], ***.entity.Address".
The converter is searching for a single address Object instead of a List of Address Objects, and I don't understand why. I use lombok #Data and #AllArgsConstructor, so Getter and Setter should be there.
#Entity
#Data
public class House implements Serializable {
#Id
#Column(name = "ID", precision = 5)
private Long id;
#OneToMany
#JoinColumn(name = "HouseID")
private List<Address> identAdressen;
}
/// ----------------
#Data
#AllArgsConstructor
public class HouseView {
private Long objnr;
private List<Address> identAdressen;
}
When I remove the "List" in the HouseView-class it works as long as there are only single addresses, but when there are multiple it crashes too.
I don't get it why he tries to find a HouseView-Constructor with an single address-object, when in both classes there are Lists of Addresses.
Let trying not using lombok and see if it works
Spring Data Projections do not support collections but I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(House.class)
public interface HouseView {
#IdMapping
Long getObjnr();
Set<AddressView> getIdentAddressen();
#EntityView(Address.class)
interface AddressView {
#IdMapping
Long getId();
String getName();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
HouseView a = entityViewManager.find(entityManager, HouseView.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<HouseView> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
What you can do is to write custom query on HouseRepository as
#Query("SELECT new com.packagename.HouseView(hos) from House hos")
then house view class can be as follows
public class HouseView {
private Long objnr;
private List<Address> identAdressen;
public HouseView(House house) {
identAdressen = house.identAdressen;
}
}
Hope it will work :) You can add more fields into constructor from join tables if required. It might help others coming late on post.

Spring Data JPA sort by join table #OrderColumn?

I'm using Spring Data JPA to query a User entity that has a collection of UserGroups. The ordering of these groups is significant, so I've declared an #OrderColumn on the relationship:
#Entity
public class User {
#Id
private String id;
#ManyToMany
#OrderColumn
private List<UserGroup> groups;
}
I need to query the groups for a particular user, and I want them to come back in the order specified by the #OrderColumn in the join table. I have this query method in my UserGroupRepository.
public Page<UserGroup> findByUsersIdContains(String userId, Pageable pageable);
That query does not honor the ordering I want. Is there something I can add to the method name, or specifying in the Sort definition within the Pageable, to order by the order column in the join table?
You should provide a column name which should be used for ordering the relationship. This answer shows how the annotation #OrderColumn could be used. Even though the example is for a #OneToMany relationship, this ordering should also work for #ManyToMany.

How to Convert a JQPL Query into a DTO?

I need to know if it's possible for me to convert my JQPL query result into a DTO.
The query result is a Array of Arrays like this Json:
[
[
ModuleID: number,
ModuleName: string,
ToolId: number,
ToolName: string,
Enabled: boolean
],
]
And I want to convert into this DTO:
public class ModuleDTO {
private Long ModuleID;
private String ModuleName;
private List<ToolsDTO> Tools;
}
public class ToolsDTO {
private Long ToolId;
private String ToolName;
private Boolean Enabled;
}
You can see that the last three are children of the module, that means that in the search there may be repeated modules, but all children must be within the same list.
This is a perfect use case for Blaze-Persistence Entity Views.
Blaze-Persitence is a query builder on top of JPA which supports many of the advanced DBMS features on top of the JPA model. I created Entity Views on top of it to allow easy mapping between JPA models and custom interface defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure the way you like and map attributes(getters) via JPQL expressions to the entity model. Since the attribute name is used as default mapping, you mostly don't need explicit mappings as 80% of the use cases is to have DTOs that are a subset of the entity model.
You didn't specify an entity model so I'm going to assume some things here. A mapping could look as simple as the following
#EntityView(Module.class)
interface ModuleDTO {
#IdMapping
Long getModuleId();
String getModuleName();
List<ToolsDTO> getTools();
}
#EntityView(Tools.class)
interface ToolsDTO {
#IdMapping
Long getToolId();
String getToolName();
Boolean getEnabled();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
ModuleDTO dto = entityViewManager.find(entityManager, ModuleDTO.class, id);
But the Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
It will only fetch the mappings that you tell it to fetch

Hibernate Projection - Getting Back Same Object

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.

jpa criteria query duplicate values in fetched list

I'm observing what I think is an unexpected behaviour in JPA 2 when fetching a list attribute with a criteria query.
My query is as follows (an extract of it):
CriteriaBuilder b = em.getCriteriaBuilder();
CriteriaQuery<MainObject> c = b.createQuery(MainObject.class);
Root<MainObject> root = c.from(MainObject.class);
Join<MainObject, FirstFetch> firstFetch = (Join<MainObject, FirstFetch>) root.fetch(MainObject_.firstFetch);
firstFetch.fetch(FirstFetch_.secondFetch); //secondFetch is a list
c.select(root).distinct(true);
(So let's say I'm fetching a list as a property of the property of an object.)
The thing is when the query returns multiple results, secondFetch values are duplicated as many times as rows are returned. Each firstFetch should have just one secondFetch but has n instead.
The only particularity i see in this case is all MainObjects happen to have the same FirstFetch instance.
So my guess is the join is being crossed, which is normal, but then JPA fails to assign its secondFetch object to each one of the firstFetchs.
Mappings shouldn't be too special, the're more or less like this
#Entity
#Table(name="mainobject")
public class MainObject{
//...
private FirstFetch firstFetch;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="mainObject_column")
public FirstFetch getFirstFetch() {
return firstFetch;
}
}
and
#Entity
#Table(name="firstFetch")
public class FirstFetch{
//...
private List<SecondFetch> secondFetch;
#OneToMany(mappedBy="secondFetch")
public List<SecondFetch> getSecondFetch() {
return secondFetch;
}
}
& finally
#Entity
#Table(name="secondFetch")
public class SecondFetch {
//....
private FirstFetch firstFetch; //bidirectional
#ManyToOne
#JoinColumn(name="column")
public FirstFetch getFirstFetch() {
return firstFetch;
}
}
I've been looking for some sort of distinct sentence to apply to the fetch but there's none (would have been a 'patch' anyway...)
If i change
List<SecondFetch>
for
Set<SecondFetch>
i'll get the expected result thanks to Sets' Keys, so I do feel this is kind of a misbehaviour in JPA's lists.
I'm not an expert, though, so i could perfectlly be making some mistake in the mappings or query.
Any feeback is very welcome to help clear this out.
Thanks.
I had the exact same problem though I was using JPA criteria API to do the query.
After some research I found a solution which you already mentioned (but was not available, since your not using criteria API): Using distinct.
With JPA criteria it would look like this:
CriteriaQuery<FirstFetch> query = cb.createQuery(FirstFetch.class);
Root<AbschnittC> root = query.from(FirstFetch.class);
root.fetch(FirstFetch_.secondFetch, JoinType.LEFT);
query.distinct(true);
Without using query.distinct(true); the resultset was multiplied with the amount of objects in the secondFetch list.
Hibernate does have something like DISTINCT_ROOT_ENTITY which sound more adequate than just setting a query distinct. But I have not further investigated this. I am also using Hibernate as the JPA provider. Maybe setting the query distinct in JPA ends up using the same code as Hibernates DISTINCT_ROOT_ENTITY would?

Categories

Resources