JPA : Many to Many query help needed - java

I have four entities that are involved in a query that I'm having a little trouble with. The relationship is as follows : Exchange----*Contract*----*Combo----*Trade and the (simplified) entities are as follows:
#Entity
public class Exchange implements Serializable {
#Id(name="EXCHANGE_ID")
private long exchangeId;
#Column
private String exchangeShortName;
}
#Entity
public class Contract implements Serializable {
#Id
private long contractId;
#Column
private String contractName;
#ManyToOne
#JoinColumn(name="EXCHANGE_ID")
private Exchange exchange;
#ManyToMany
#JoinTable(name="CONTRACT_COMBO",
joinColumns = { #JoinColumn(name="CONTRACT_ID") },
inverseJoinColumns = {#JoinColumn(name="COMBO_ID")})
private Set<Combo> combos;
#Column(name = "ACTIVE_FLAG")
private String activeFlag;
}
#Entity
public class Combo implements Serializable {
#Id
#Column(name="COMBO_ID")
private Integer id;
#ManyToMany
#JoinTable(name="CONTRACT_COMBO",
joinColumns = { #JoinColumn(name="COMBO_ID") },
inverseJoinColumns = {#JoinColumn(name="CONTRACT_ID")})
private Set<Contract> legs;
#OneToMany(mappedBy = "combo")
private Set<Trade> trades;
}
#Entity
public class Trade implements Serializable {
#Id
#Column(name="TRADE_ID")
private long tradeId;
#Column(name="REFERENCE")
private String reference;
#ManyToOne
#JoinColumn(name="COMBO_ID")
private Combo combo;
}
I want to get a list of all trades for a particular exchange which I can't quite get to work with MEMBER OF. Any help would be appreciated.

Try this
select distinct t
from Trade t
join t.combo c
join c.legs l
join l.exchange e
where e.exchangeShortName = 'whatever'

Not really optimized, but I think this should do the trick:
Long exchangeId = Long.valueOf(5324623L);
List<Trade> trades = em.createQuery("select T from Trade T where T in " +
"(select distinct C from Combo c where c member of " +
"(select e.combos from Exchange e where e.id = :id) " +
")").setParameter("id", exchangeId).getResultList();

Related

Select from table using parameters from related tables

I have 3 tables (Entities) (postgreSQL):
#Entity
#Table(name = "application", schema = "applications")
public class Application implements Serializable {
#Id
#GeneratedValue
#Column(name = "application_id")
private long applicationId;
#Column(length = 400)
#Size(min=50, max=400)
private String applicationDescribing;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "customerId")
private Customer Customer;
.....
Also I have the abstract class User with child class Customer, which has its own table in DB.
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Long userId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "localitiesId")
private Localities userLocality;
.....
#Entity
#AttributeOverride(name="userId", column=#Column(name="customerId"))
public class Customer extends User {
#Column(length = 8)
private String userType;
.....
User and also Customer table has many to one related table Locality, connected via id.
#Entity
#Table(name = "localities", schema = "resource")
public class Localities implements Serializable {
#Id
#GeneratedValue
#Column(name = "id")
private long localitiesId;
#Column(name = "region", length = 50)
private String region;
....
I try to realize seaching via Application, using (String) keyWord and locality_id (long) locality.
First approach:
#Query(value = "SELECT u FROM Application u WHERE u.applicationDescribing LIKE %:keyWord% " +
"AND userLocality IN (SELECT c FROM User c WHERE c.userLocality IN " +
"(SELECT l FROM Localities l WHERE l.localitiesId = :locality))")
List<Application> getApplicationsByKeyWordsAndRegion(#Param("keyWord") String keyWord, #Param("locality") long locality);
Second approach (recomended by richyen):
#Query(value = "SELECT a FROM Application a " +
"JOIN Customer c ON a.customerid = c.userid " +
"JOIN Localities L ON c.localitiesid = L.id " +
"WHERE a.applicationDescribing LIKE %:keyWord% AND L.id = :locality")
List<Application> getApplicationsByKeyWordsAndRegion(#Param("keyWord") String keyWord, #Param("locality") long locality);
Both dont work. As a result I need List <Application>, which have "keyWord" in describing field and locality in Application.Customer.locality_id. Please help
Is this what you're looking for?
SELECT a.*
FROM application a
JOIN (SELECT * FROM customer c UNION ALL SELECT * FROM executor e) as u on a.customer_id=u.id
JOIN locality l on u.locality_id=l.id
WHERE a.description like '%<keyWord>%'
AND l.region = <region>;
I guess one question I have for you is: how do you differentiation between Customer ID and Executor ID?
Correct answer:
SELECT app FROM Application app
INNER JOIN app.Customer customer
INNER JOIN customer.userLocality
locality WHERE customer.userLocality.id
= :locality AND app.applicationDescribing
LIKE CONCAT('%',:keyWord,'%')

Loading DTO with collection

#Entity
#Table(name = "person")
public class Consignment implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "person_id")
private String personId;
#Column(name = "person_name")
private String personName;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "person")
#Column(name = "cars_owned")
private Set<Cars> casrsowned = new HashSet<>();
}
#Entity
#Table(name = "cars")
public class Cars implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "cars_id")
private String carsId;
#ManyToOne
#JoinColumn(name = "person")
private Person person;
#OneToOne
private CarsDetail carsDetail;
}
#Entity
#Table(name = "carsDetail")
public class CarsDetail implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "brand")
private String brand;
#Column(name = "color")
private String color;
#Column(name = "model")
private String model;
}
class CarModelDTO {
String personName;
List<String> models;
}
In the above relation, want to return CarModelDTO
JPA query where,
#Query("Select CarModelDTO(p.personName, p.casrsowned.carsDetail.model) from Person as p where p`enter code here`.id = :id"))
public CarModelDTO getCarmodelOwnedByAperson(#Param("id") Long id);
I tried multiple ways but it gives
org.hibernate.QueryException: illegal attempt to dereference collection
As I have already described Retrieve List from repository interface to DTO list you should go through the following step :
first create a constructor using the fields you want to be returned from the query output
in you query you should create new instance of your dto and pass the field from db to new instalnce :
so you need these changes:
1. In the constructor:
You should not use a list as List<String> models; as you should consider that your dto as a result row of DB. so you need to have a simple String model;
public CarModelDTO (String name,String model){
this.name=name;
this.model=model;
}
2. In the #Query:
you should use multi inner join appropriately
you should also append your package name to CarModelDTO in the query (here i used com.example you should change it)
#Query("Select com.example.CarModelDTO(p.personName, d.model ) from Person as p inner join p.carsowned c inner join c.carDetail d where p`enter code here`.id = :id"))
public CarModelDTO getCarmodelOwnedByAperson(#Param("id") Long id)

How to join four tables with hibernate

I have a four tables which I need to join. These four tables are:
product
service
service_type
service_duration
A product has a service, and a service has a type and duration.
The user chooses the product, the type, the service, and the duration of the service.
In the case that a service is not available for the chosen type and duration I would like to be able to get the service which has a type of lower priority (priority is attribute in the service_type table).
In php(Laravel) I am able to join like so:
DB::table('product')
->join('product_service', 'product_service.product_id', '=', 'product.id')
->join('service', 'service.id', '=', 'product_service.service_id')
->join('service_type', 'service.type_id', '=', 'service_type.id')
->join('service_duration', 'service.duration_id', '=', 'service_duration.id')
->where('product.id', id)
->where('service_type.priority', '<', priority)
->where('service_duration.duration', duration)
->where('maintenance.active', 1)
->orderBy('service_type.priority', 'desc')
->select('service.*')
->first();
How is it possible to do this with Hibernate Entity Criteria ? I want to start of by joining from the product side but in the end select the service.
I have my relations defined like so:
Product class
#Entity
#Table(name = "product")
public class product implements Serializable {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "id.product", cascade=CascadeType.ALL)
private Set<ProductService> services = new HashSet<ProductService>();
}
ProductService Class:
#Entity
#Table(name = "product_service")
#AssociationOverrides({ #AssociationOverride(name = "id.service", joinColumns = #JoinColumn(name = "service_id")),
#AssociationOverride(name = "id.product", joinColumns = #JoinColumn(name = "product_id"))})
public class ProductService implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private ProductServicePK id = new ProductServicePK();
#Column(name = "price")
private Float price;
#Column(name = "code")
private String code;
}
ProductServicePK Class:
#Embeddable
public class ProductServicePK implements Serializable {
private static final long serialVersionUID = 1L;
#ManyToOne
private Product product;
#ManyToOne
private Service service;
}
Service class:
#Entity
#Table(name = "service")
public class Service implements Serializable {
#Id
#Column(name = "id")
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "type_id")
private ServiceType type;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "duration_id")
private ServiceDuration duration;
}
So the object that 'holds' reference to the other is the product object. So I am not sure how to get the service for that product which has a priority lower than the one selected.
I want to do this with criteria or HQL.
Your Hibernate Detached Criteria will like:
DetachedCriteria criteria = DetachedCriteria.forClass(Product.class, "product")
.criteria.createAlias("product.product_service", "productService")
.createAlias("productService.service","service")
.createAlias("service.service_type","serviceType")
.createAlias("service_duration","serviceDuration")
.add(Restrictions.eq("product.id", id))
.add(Restrictions.gt("serviceType.priority", priority))
.add(Restrictions.eq("serviceDuration.duration", duration))
.setProjection(add your productService projection here)
getExecutableCriteria(session).SetMaxResults(1) ;
But i am not getting your point, why you are using from query on product instead of product_service?, because you need product_service details.
you can you use querycriteria.html, here is the doc of it->https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/querycriteria.html
or you can use hql ->https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/queryhql.html for achieve your result.
Apparently the JPA Criteria API does not support what I need when using the composite PK pattern. I found this mentioned somewhere here. So I found the best solution was using HQL:
Query q = session.createQuery("select s "
+ "from Service s "
+ "join s.productService as ps "
+ "join s.type as t "
+ "where s.duration = :durationId "
+ "and ps.id.product.id = :productId "
+ "and t.priority <= :priorityValue "
+ "order by t.priority desc");
q.setLong("durationId", durationId);
q.setInteger("priorityValue", priorityValue);
q.setLong("productId", productId);
return (Service) q.setMaxResults(1).uniqueResult();

Hibernate Joined inheritance: Query records by ID ONLY from child table

I have a hierarchy of classes like next:
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#Table(name="Person")
public class Person implements Serializable{
#Id
#Column(name = "PersonID", unique = true, nullable = false)
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
}
#Entity
#Table(name="Student")
#PrimaryKeyJoinColumn(name="PersonID")
public class Student extends Person{
}
#Entity
#Table(name="Bachelor")
#PrimaryKeyJoinColumn(name="PersonID")
public class Bachelor extends Student{
#OneToMany(mappedBy = "bachelor", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<BachelorExam> exams;
}
#Entity
#Table(name="Exam")
public class Exam implements Serializable {
#Id
#Column(name = "ExamID", unique = true, nullable = false)
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
}
#Entity
#Table(name="BachelorExam")
public class BachelorExam implements Serializable {
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "PersonID_FK", referencedColumnName = "PersonID")
private Bachelor bachelor;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "ExamID_FK", referencedColumnName = "ExamID")
private Exam exam;
}
I want to get users (regular student or bachelor) from appropriate table by ID using generic method like next:
<T extends Person> T getStudentById(Long studentId);
This method can be something like
public <T extends Person> T getUserById(Long personId) {
List<Class<?>> studentTypes = new LinkedList<>();
studentTypes.add(Student.class);
studentTypes.add(Bachelor.class);
for (Class aClass : studenTypes) {
List<T> results = getDatabaseProvider().getDataFromDatabase(String.format("select u %s as u " +
"where u.userId = '%d'", aClass.getName(), userId));
return results.get(0);
}
}
The problem is that when I save a bachelor object in database, hibernate also saves bachelor's id to 'Student' table so when I get data from database going through whole list of inherited classes, query returns record from table Bachelor and also record from table Student, because both contain required student ID.
I've tried to use InheritanceType Table_Per_class but in this case hibernate doesn't create foreign key for bachelor in table BachelorExam.
How can I receive only records from table Bachelor by id?
Thanks!

JPA Criteria seems to ignore join on condition

I've a User and Contact entities in my app and I need to every user can add some private comment about every contact and that comment must be available only for that user. So I create new entity - PrivateInfo. Here's the code.
User class:
#Entity
#Table(name = "users")
#XmlAccessorType(XmlAccessType.FIELD)
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String login;
// other fields
}
Contact class:
#Entity
#Table(name = "contacts")
#XmlAccessorType(XmlAccessType.FIELD)
public class Contact implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#OneToMany(fetch = LAZY, cascade = ALL, mappedBy = "contact")
private Set<PrivateInfo> privateInfo;
// etc.
}
PrivateInfo class:
#Entity
#Table(name = "private_info")
#XmlAccessorType(XmlAccessType.FIELD)
public class PrivateInfo implements Serializable {
#EmbeddedId
private PrivateInfoKey pk;
#Column(name = "additional_info")
private String additionalInfo;
#ManyToOne(fetch = FetchType.EAGER)
#MapsId("contactId")
private Contact contact;
}
#Embeddable
public class PrivateInfoKey implements Serializable {
#Column(name = "contact_id")
private Long contactId;
#Column(name = "user_id")
private Long userId;
}
I'm using Spring Data repositories with JpaSpecificationExecutor for querying so here's my attempt to write specification for getting all contacts with private info for specific user.
public static Specification<Contact> withPrivateInfo(final long userId) {
return new Specification<Contact>() {
#Override
public Predicate toPredicate(Root<Contact> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Join<Contact, PrivateInfo> joinPrivateInfo = root.join(Contact_.privateInfo, JoinType.LEFT);
joinPrivateInfo.on(cb.equal(
joinPrivateInfo.get(PrivateInfo_.pk).get(PrivateInfoKey_.userId), userId
));
return cb.conjunction(); // translates in sql like '... where 1 = 1'
}
};
}
However, when I call
contactRepository.findAll(withPrivateInfo(1));
I'm receiving contacts and each of them contains in privateInfo field all users information about this contact (not only for user with id = 1, as expected). Seems like join on condition ignored.
Any suggestions how to achieve my goal? Maybe with another entities structure. Is this possible with JPA/Criteria?

Categories