JPA: How to filter an association? - jpa

My model:
#Entity
class Person {
#Id
long id;
#OneToMany
Set<Employment> employments = new HashSet<Employment>();
}
#Entity
class Employment {
#Id
long id;
#ManyToOne
Company company;
Date from;
Date until;
}
It's an association between a Person and a Company that's restricted by a time interval.
I'm looking for a JPA criteria query to select all Persons and their employments at a given time.
The expected result is a List<Person> containing all people, where the collection employments of each Person contains only employments that match certain criteria (or no emplyments at all).
#Where is not a sensible approach because the filter criteria for employments is variable, e.g. I would want to select all employments of a person at any given time.
Is this even a reasonable thing to do? Any suggestions how to do this differently?

No, it's not a reasonable thing to do. An entity is supposed to represent the data that is in the database, and not the date returned by a particular query.
I would simply add a ManyToOne association from Employment to Person, and search for employments. If you need the set of persons, just iterate through the employments and add each employment's person to a Set. If you want the persons associated with their employments at this date, you could use a custom class, or a MultiMap<Person, Employment>.
String hql = "select employment from Employment employment"
+ " left join fetch employment.person"
+ " where :date between employment.startDate and employment.endDate";
List<Employment> employments = session.createQuery(hql)
.setDate("date", date)
.list();

Related

JaVers, SpringDatat, JPA: Querying for Entity Update inside a Collection

I'm new to Stackoverflow, so I will make my best to conforms with usage. I was wondering if there were a way to get a complete list of changes/snapshots of a given Entity. For now it works well with edition of Singular Properties, as well as Addition and Deletion to Collection Property. But I'm unable to find when a Child Entity in the Collection Property was updated.
Given two Entities, and a LinkEntity:
#Entity
class Person {
#Id
Long id;
#OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
Set<LinkAddress> addresses;
}
#Entity
class Address {
#Id
Long id;
#OneToMany(mappedBy = "address")
Set<Address> persons;
}
#Entity
class LinkPersonAddress {
#Id
Long id;
#ManyToOne
#ShallowReference
Person person;
#ManyToOne
#ShallowReference
Address address;
String linkType;
}
My use case is following. I get a specific Person by Id #1, and then mutate the type of specific Address (ie. HOME --> WORK). I save the Person back with the modified Set and let JPA Cascade my changes. Although all Spring Data Repositories for Person, Address, and LinkPersonAddress are annotated with #JaversSpringDataAuditable, I cannot retrieve this "update" using Javers QueryBuilder with the class Person and Id #1. It makes sense as I should query the class LinkPersonAddress instead, but how can I specify that I want only the changes from LinkPersonAddress relevant to Person with Id #1.
PS: Please apologize any typos in code snippets, as I didn't write it in my Dev Environment.
Let's start from the mapping. You did it wrong, Address is a classical ValueObject (see https://javers.org/documentation/domain-configuration/#value-object) not Entity. Because:
Address doesn't have its own identity (primary key genereted by a db sequence doesn't count)
Address is owned by the Person Entity. Person with its Addresses forms the Aggregate.
When you correct the mapping, you can use ChildValueObjects filter, see https://javers.org/documentation/jql-examples/#child-value-objects-filter

Spring data JPA JPQL query on child property

I am using spring-data jpa. When querying parent object with child object property,I was expecting parent with aggregated child objects.I have OneToMany relation between User and Phone. Just typing some part of code.
#Query(select u from User u inner join u.phone ph where ph.active=:active)
Page<User> getAllUsers(#Param("active") int active);
#Entity
User{
#OneToMany(fetch=FetchType.LAZY)
List<Phone> phone;
}
#Entity
Phone{
#ManyToOne
User user;
}
My query returns multiple User object based on active phone quantity. I was expecting one User object and all aggregated phone object in the list as part of User object. Is my assumption is wrong or am I doing something wrong?
Try:
#Query(select distinct u from User u inner join u.phone ph where ph.active=:active)
Page<User> getAllUsers(#Param("active") int active);

How to create association one to many and many to one between two entities in gorm?

I'm new in Grails. I have a problem with generation association many to one and one to many between two tables. I'm using postgresql database.
Employee.groovy
class Employee {
String firstName
String lastName
int hoursLimit
Contact contact
Account account
Unit unit
char isBoss
static hasMany = [positionTypes:PositionType, employeePlans: EmployeePlan]
}
EmployeePlan.groovy
class EmployeePlan {
AcademicYear academicYear
HourType hourType
int hours
float weightOfSubject
Employee employee
static belongsTo = [SubjectPlan]
}
I'd like to have access from employee to list of employeePlans and access from EmployeePlan to Employee instance. Unfortunately GORM generates only two tables Employee and EmployeePlan with employee_id. I don't have third table which should have two columns employee_id and employee_plan_id. Could you help me ?
I think your setup is correct, as you write from Employee class you can access to a collection of EmployeePlan (take care, that if you don't explicitly define EmployeePlan like a List, it will be a Set by default) and from EmployeePlan you can access Employee.
If you need List, you can define it like that:
class Employee {
String firstName
String lastName
int hoursLimit
Contact contact
Account account
Unit unit
char isBoss
//explicitly define List
List<EmployeePlan> employeePlans
static hasMany = [positionTypes:PositionType, employeePlans: EmployeePlan]
}
But back to your question. You'd like to have join table between Employee and employeePlan, but why? Its not necessary, since you have bidirectional mapping with sets (unordered), grails will not create a join table. Can you explain why do you need it? In grails the references will be auto-populated, so I don't see any issue here.
If need to preserve order of employeePlans, then define it as List, shown above, and grails will create a join table with corresponding indexes.
have you read the ref-doc? it gives you the answer immediately:
class Person {
String firstName
static hasMany = [addresses: Address]
static mapping = {
table 'people'
firstName column: 'First_Name'
addresses joinTable: [name: 'Person_Addresses',
key: 'Person_Id',
column: 'Address_Id']
}
}

Category tree structure with node count using JPA

I have a category tree structure, using JPA with eclipse link.
Each category in the tree includes items, and I want to select all categories along with their items count.
The code I inherited used to calculate the number of items offline, and update the category, and use the cool JPA trick for selecting the categories like so:
#Entity
#Table(name = "categories")
#NamedNativeQuery(name = "topLevel", query = "SELECT categories.* FROM categories WHERE categories.parent_id IS NULL")
public class Category {
#Id
private Long id;
private String name;
private Integer item_count;
private Long parent_id;
#JoinColumn(name = "parent_id")
private List<Categories> categories;
}
When running the native query - I get all the categories populated in the tree structure.
However, now I have a new filter that doesn't allow the user to see all the items in the category. So I can no longer calculate the items offline.
I tried to use a join so I can calculate it online, like this:
#NamedNativeQuery(name = "topLevel", query = "SELECT categories.*, count(*) as item_count FROM categories, items_to_categories WHERE categories.id = items_to_categories.category_id and some_user_specific_query and categories.parent_id IS NULL GROUP BY categories.id")
but this doesn't populate the lower levels with the right items count.
Since I am using Eclipse Link, I can't use a formula field that is not supported in this JPA implementation.
Any ideas on how to change my model in order to get it to work?
Thanks in advance!
If you want an Item count you can either query for it, such as using JPQL. This will return an Object[] including the Category and its Integer item count. If you want to use a native SQL query, then you will need to use a SqlResultSetMapping.
If you want the item count in Category, then you could define a database VIEW that makes this appear as a column and map to the view.
Another option is to map the items of the Category, then to get the item count, just access the size() of the items in Java (you could use join or batch fetching to load the items efficiently).

JPA OneToMany relations and performace

I have two entities: parent Customer and child Order.
Each Customer has 1,000,000 Orders for example, so it is not needed in any given time to load a Customer with all Orders but I want to have this ability to make join query on these two entities in JPA.
So because of this, I must create #OneToMany relationship for making join queries.
My question is: how to get query without making joinColumn because even in Lazy mode it is possible to load 1,000,000 objects!
I just want to get query on these object with where restrictions like native join.
If you don't want the #OneToMany relationship implicitly set in your Customer class than you don't have to. You can execute JPQL queries (in very precise manner) without the marked relationship.
Assume you have:
#Entity
public class Customer {
// all Customer-related fields WITHOUT #OneToMany relationship with Order
}
#Entity
public class Order {
#ManyToOne
private Customer owner;
}
Then if you want to get all Orders for particular Customer you can execute a simple JPQL query like that:
// Customer customer = ...
// EntityManager em = ...
String jpql = "SELECT o FROM Order o WHERE o.owner = :customer";
TypedQuery<Order> query = em.createQuery(jpql, Order.class);
query.setParameter("customer", customer);
List<Order> orders = query.getResultList();
In this way you can execute the code only when you're really sure you want to fetch Customer's orders.
I hope I've understood your problem correctly.
EclipseLink has support for QueryKeys, that allow you to define fields or relationships for querying that are not mapped. Currently there in no annotation support for query keys, but you can define them using the API and a DescriptorCustomizer.
Also you do not need the OneToMany to query on it, just use the inverse ManyToOne to query,
i.e.
Select distinct c from Customer c, Order o where o.customer = c and o.item = :item
Or,
Select distinct o.customer from Order o join o.customer c where o.customer = c and o.item = :item