How to filter #OneToMany child collection in JPA - spring-data-jpa

I wonder if it is possible to filter the child list in a #OneToMany mapping in Spring JPA.
For example, let's say I have a "Manager" entity.
#Entity
#Table(name = "manager")
data class Manager(
#Id
#Type(type = "pg-uuid")
#Column
var id: UUID = UUID.randomUUID(),
#JsonManagedReference
#OneToMany(mappedBy = "manager", cascade = [CascadeType.ALL], orphanRemoval = true)
var employees: List<Employee> = listOf()
)
and an "Employee" entity
#Entity
#Table(name = "employee")
data class Employee(
var firstName: String,
var lastName: String,
var startDate: OffsetDateTime
#JsonBackReference
#ManyToOne
#JoinColumn(name = "manager_id")
var manager: Manager,
)
Is it possible to retrieve a list of "Managers" who have a list of "Employees" attached to them that begin before or after a certain startDate?
I don't want to retrieve the list of "Managers" and all their "Employees" and then apply the filter.
I expect a list of managers to have a filtered list of employees attached to them after querying the database.

Related

Having abstract data type for attribute in JPA

I have following entities -:
#MappedSuperclass
abstract class Enterprise {
}
Two classes implementing Enterprise
#Entity
class Vendor() : Enterprise {
#OneToMany
#JoinColumn(name="contactPerson_id")
lateinit var contactPerson: List<ContactPerson>
}
#Entity
class Customer() : Enterprise {
#OneToMany
#JoinColumn(name="contactPerson_id")
lateinit var contactPerson: List<ContactPerson>
}
And for ContactPerson Class we have reference for each type of Enterprise
#Entity
#Table(name = "CONTACT")
class ContactPerson {
#JoinColumn(name = "vendor_id")
#ManyToOne
var vendor: Vendor
#JoinColumn(name = "vendor_id")
#ManyToOne
var customer: Customer
#Id
val id: Int? = null
}
But instead of keeping two reference i want to have one reference of abstract type Enterprise something like this
#Entity
#Table(name = "CONTACT")
class ContactPerson {
#JoinColumn(name = "vendor_id")
#ManyToOne
var enterprise: Enterprise
#Id
val id: Int? = null
}
But this give me error "Many To One' attribute type should not be 'Mapped Superclass"
which i understand is due to the fact that Enterprise is not an Entity
and will not be mapped to a table which is exactly what i want .
How can i achieve this without having an Entity superclass

jpa OneToMany / ManyToOne how to save data

I'm learnig jpa and have a general question to saving data.
Suppose we have two entities:
Parent entity: Person
#JsonIgnore
#OneToMany(mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Car> cars = new HashSet<Car>();
Child entity: Car
#ManyToOne(fetch = FetchType.EAGER)
#JoinTable(name = "customer_car", joinColumns = #JoinColumn(name = "car_id"),
inverseJoinColumns = #JoinColumn(name = "person_id"))
private Person person;
The question is: What is the correct way to save data:
Person-Controller: I should use a #PostMapping to post a person with many cars.
Car-Controller: I should use a #PostMapping to post a car with many persons.
Option 1 and option 2 are possible

OneToOne mapping issue for non primary field

I am trying to map a non primary key column between 2 tables using one-to-one mapping through JPA.
The OneToOne is not performing the join on the mentioned column rather it is picking up the Id field.
Below is the table structure:
Person table
id (PK)
name
college
College table
id (PK)
clg_name
location
Location table
id (PK)
loc_name
I need to provide OneToOne mapping between College and Location using the columns location and loc_name respectively. I have tried using the #NaturalId, #MapsId and by providing the reference column name. Still it uses the id field
//person
#Entity
#Table(name = "PERSON", schema = "DETAILS")
#SecondaryTables({
#SecondaryTable(name = "COLLEGE", schema = "DETAILS")
})
class Person{
Person(){
this.college = new College();
}
#Id
#Column(name = "ID", nullable = false)
private Long id;
#Column(name = "NAME", nullable = false)
private String name;
#Column(name = "COLLEGE_NAME", table = "COLLEGE", nullable = false)
private String college;
#OneToOne(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER, mappedBy = "person")
#JoinColumn(name = "ID")
private College college;
//getter setters
}
//college
#Entity
#Table(name = "COLLEGE", schema = "DETAILS")
class College{
College(){
}
#Id
#MapsId
#OneToOne()
#JoinColumn(name = "ID")
private Person person;
#OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER, mappedBy = "college")
#JoinColumn(referencedColumnName = "LOC_NAME")
private Location location;
#Column(name = "LOCATION", nullable = false)
private String loc;
//getter setters
}
//location
#Entity
#Table(name = "LOCATION", schema = "DETAILS")
class Location{
Location(){}
#Id
#Column(name = "ID")
private Long collegeId;
#MapsId
#OneToOne()
#JoinColumn(name = "LOC_NAME", referencedColumnName ="LOCATION", nullable = false, unique = true)
private College college;
#Column(name = "LOC_NAME", nullable = false)
private String locName;
//getter setters
}
In the above code, I am facing in OneToOne mapping issue using the location name columns. I am querying the Person object from the JPA repository by querying "from Person p where p.id = :id".
The generated JPA queries in logs for 1to1 mapping appears to be
select from details.college college0_ left outer join details.location location1_ on college0_.id=location1_.locName where college0_.id=?
Error:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet] with root cause
Caused by: java.sql.SQLSyntaxErrorException: ORA-01722: invalid number
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:447)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:396)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:951)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:513)
If I remove #MapsId from Location then I get below error:
org.hibernate.AnnotationException: A Foreign key refering has the wrong number of column. should be 0

JPA Annotations - How to retrieve all entities with a specific column value

Let say I have an entity object Customer with an "OneToMany" relation to Order. I want that when ever a "Customer" get loaded, only his orders with the Id = 1234, 5678 get loaded to.
Any ideas?
#Entity
#Table(name = "Customer")
public class Customer extends TraceableJPA {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "customer_id")
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "Customer", targetEntity = Order.class)
#Column(name = "order_id", value = {"1234","5678"} (?))
#OrderBy("isrtdate ASC")
#BatchSize(size = 20)
private List<Order> orders = new ArrayList<Order>();
Hibernate
If you use hibernate Session and its abilites , you can always use #FilterJoinTable mechanism.
Check THIS article for more information.
Yet it is not global, you have to predefine this filter and then explicitly configure Session object to use it.
JPA
JPA in its standard has NO SUCH FUNCTIONALITY, for global relations filtering.
You can always filter it in your queries : )

one side set in many-to-many relation

I have three database tables: Customer, Product and PurchaseOrder (for mapping). I am using openjpa for peristence in java rest application.
To all of the tables I have corresponding entities:
Customer
#Entity
#Table(name = "customer")
#XmlRootElement
#NamedQueries({...})
public class Customer implements Serializable {
...
#OneToMany(cascade = CascadeType.ALL, mappedBy = "customerId")
private Collection<PurchaseOrder> purchaseOrderCollection;
Product
#Entity
#Table(name = "product")
#XmlRootElement
#NamedQueries({...})
public class Product implements Serializable {
...
#OneToMany(cascade = CascadeType.ALL, mappedBy = "productId")
private Collection<PurchaseOrder> purchaseOrderCollection;
PurchaseOrder
#Entity
#Table(name = "purchase_order")
#XmlRootElement
#NamedQueries({..})
public class PurchaseOrder implements Serializable {
...
#Id
#Basic(optional = false)
#Column(name = "order_num")
private Integer orderNum;
#JoinColumn(name = "customer_id", referencedColumnName = "customer_id")
#ManyToOne(optional = false)
private Customer customer;
#JoinColumn(name = "product_id", referencedColumnName = "product_id")
#ManyToOne(optional = false)
private Product product;
What is the best way to get all the customers who ordered a product with specific id?
I could create namedQuery, I could build criteria with joins etc. But i think there could be a better way how to make use of the mapping entity (what would be point of this entity otherway?). Something like setting the productId to the purchaseOrder entity and then fetch all the customers via purchaseOrderCollection in customer entity? But i cannot figure it out. Is there other way than custom/named query or criteria building?
Thanks.
ok I figured it out, it can be this way
long productId = //get the id
Product product = entityManager.find(Product.class, productId);
Collection<PurchaseOrder> purchaseOrderCollection = product.getPurchaseOrderCollection();
if (purchaseOrderCollection != null) {
List<Integer> customers = new ArrayList<>(product.getPurchaseOrderCollection().size());
for (PurchaseOrder purchaseOrder : product.getPurchaseOrderCollection()) {
customers.add(purchaseOrder.getCustomerId());
}
return customers;
} else {
return Collections.EMPTY_LIST; // or null;
}
feel free to offer better sollution :)