Subquery - JPA - Include Subquery as part of the main selection - sql

I have an issue to add the result of a subquery as part of the selection.
This is how my data model looks like:
#Entity
#Table(name = "flow")
public class Flow {
#Id
Long id;
#OneToMany(mappedBy="flow", fetch = FetchType.LAZY)
Set<Subscription> subscribers;
... other internal fields
//extra fields that come from other tables
#Transient //as it is not part of the model sometimes must be null
Long numberOfActiveSubscribers;
}
#Entity
#Table(name = "subscriptions")
public class Subscription {
#Id
Long id;
#ManyToOne
#JoinColumn(name = "idflow")
Flow flow;
#OneToMany(name = "user")
User user;
#Column(name = "active")
boolean active;
}
I need to implement a query using jpa specifications that will include in its definition the number of user that have active subscriptions to the flow. So that I will be able to use pagination over that, including sort by this field that is not part of the original flow table. The SQL query I came out with look like this:
SELECT f.*,
(SELECT count(sf.id)
FROM subscription sf
WHERE sf.active = true
AND sf.idf = f.id) as numberofactivesubscribers,
FROM flow f;
I would like to us it in a findAll method like so:
this.repository.findAll(new Specification<Flow>() {
#Override
public Predicate toPredicate(Root<Flow> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
Subquery<Long> sq = query.subquery(Long.class);
Root<Subscription> subsRoot = sq.from(Subscription.class);
sq.select(cb.count(subsRoot.get("id")))
.where(cb.equal(subsRoot.get("flow"), flowRoot),
cb.equal(subsRoot.get("active"), true));
//I DONT KNOW HOW TO INCLUDE THIS AS A PART OF THE MAIN SELECT OF THIS QUERY
}
}, pagination);
But as you can see I don't know how to include the subquery as part of the select method as I did in my SQL query.

Related

Spring Data JPA Query for inner join table throwing error

Spring DATA JPA question... I am trying to write a query to access the data in my sql join table.
I have my join table set up as follows:
#Entity
#Table(name="WritingCompany")
public class WritingCompany {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "companyId")
private Long id;
// private String companyName;
#ManyToMany
#JoinTable(name = "Join-WritingCo-Carrier",
joinColumns = #JoinColumn(name="writingCo"),
inverseJoinColumns = #JoinColumn(name="carrier")
)
private Set<CarrierAppointment> carriers;
//...getters and setters
}
public class CarrierAppointment {
// ...
#ManyToMany(
cascade=CascadeType.ALL,
mappedBy = "companyName"
)
private Set<WritingCompany> companies;
public Set<WritingCompany> getCompanies() {
return companies;
}
public void setCompanies(Set<WritingCompany> companies) {
this.companies = companies;
}
//...
}
I am unsure which class repository I need to write the query for... I am trying to find all the writingCo names from the join table that all have the same carrier id that matches one specific carrier.
The Join Table is set up through writing company but I feel like it should be accessed through CarrierAppointment Repository since I am matching carrier Id's.
This is what I've tried in the CarrierAppointmentrepository and it throws this error (unexpected token: Join near line 1, column 94):
#Query("Select companyName FROM WritingCompany INNER JOIN CarrierAppointment ON Join-WritingCo-Carrier.carrier= CarrierAppointment.CarrierAppointmentId")
List<CarrierAppointment> findCarrierAppoinmentFromJoin(CarrierAppointment carrier);
I've also tried:
#Query("SELECT writingCo FROM Join-WritingCo-Carrier WHERE carrier= CarrierAppointment.CarrierAppointmentId")
List<CarrierAppointment> findCarrierAppoinmentFromJoin(CarrierAppointment carrier);
Then I tried this in the writingCompanyRepository which also throws a similar error:
#Query("Select companyName FROM WritingCompany INNER JOIN CarrierAppointment ON Join-WritingCo-Carrier.carrier= CarrierAppointment.CarrierAppointmentId")
List<WritingCompany> findAllWithDescriptionQuery(CarrierAppointment carrier);
I am having a hard time understanding what this query is saying. Do I ever need to access the columns from the sql join table, or am I just querying around the join table by asking for each class that is making up the join columns in the join table? What is the right part of the statement, after INNER JOIN stating ? Could someone please provide a deeper explanation of why the query is written so I can figure out why it's not working? I've been reading a lot of inner join examples and just can't seem to figure it out.

Hibernate createNativeQuery returns duplicate rows

I have 2 database tables Customer and Items with 1 -> many relation. To fetch data from database i am using the following query.
select customer.id, customer.name, items.itemName, items.itemPrice from testdb.customer INNER JOIN items ON items.customer_Id = customer.id
I have an entity class Customers
#Entity
public class Customer{
#Id
private int id;
#Column
private String name;
#Column
private String itemName;
#Column
private int itemPrice;
public Customer() {}
//Getter and setter are here
.......
}
in Service class i have the following code.
#GET #Path("/getCustomerInfo")
#Produces(MediaType.APPLICATION_JSON)
public List getCustomerInfo() {
CustomerDao dao = new CustomerDao();
return dao.getBuildingsCustomerInfo();
}
in my DAO class i have the following code
public List<Customer> getCustomerInfo(){
Session session = SessionUtil.getSession();
String queryString = "the above mentioned query";
List<Customer> customerInfo = session.createNativeQuery(queryString, Customer.class) ;
session.close();
return customerInfo;
}
I am getting the following JSON response from the service
[id:1, name:"Alfred", itemName:"jeans", itemprice:10],[id:1, name:"Alfred", itemName:"jeans", itemprice:10],[id:2, name:"James", itemName:"watch", itemPrice:20 ],[id:2, name:"James", itemName:"watch", itemPrice:20 ], [id:2, name:"James", itemName:"watch", itemPrice:20 ]
The number of results are 5 which is correct But 2nd result is a copy of 1st, 4th and 5th are copies of 3rd. In 2nd, 4th and 5th results the itemName and the itemPrice should be different.
if I use createSQLQuery(queryString); instead of createNativeQuery(queryString, Customer.class); I am getting the correct result but without entity attribut names.
[1, "Alfred", "jeans", 10],[1, "Alfred", "shirt", 15],[2, "James", "watch", 20], [2, "James", "coffee", 25], [2, "James", "drinks", 30]
I have seen number of articles but could not find the solution. I have to use createNativeQuery() not createSQLQuery() because I need to map the entity class attributes. Please let me know if i am doing something wrong.
Your data structure is wrong on the Java side and not corresponding to the database relation. In the relation you describe you need to have a list of items:
#Entity
public class Customer implements Serializable {
// ... the fields you have so far
// assuming the parent field on the other side is called customer
// you may also want to set the cascade and orphanRemoval properties of the annotation
#OneToMany(mappedBy = "customer")
#JsonManagedReference // assuming you're using Jackson databind JSON
private List<Item> items;
}
And on the Item side:
#Entity
public class Item implements Serializable {
#Id
private int id;
#JsonBackReference
#ManyToOne
#JoinColumn(name = "customer_Id")
private Customer customer;
}
Then if you really the JSON data strucutred that way, you need a third Entity class to use as a ResultSetMapping.
#Entity
#SqlResultSetMapping(
name = "CustomerItem",
entities = #EntityResult(entityClass = CustomerItem.class)
)
#NamedNativeQueries({
#NamedNativeQuery(
name = "CustomerItem.getAll",
resultSetMapping = "CustomerItem"
query = "select customer.id as cid, items.id as iid, customer.name,"
+ " items.itemName, items.itemPrice from testdb.customer INNER JOIN"
+ " items ON items.customer_Id = customer.id"
)
})
public class CustomerItem implements Serializable {
#Id
private int cid;
#Id
private int iid;
#Column
private String name;
#Column
private String itemName;
#Column
private int itemPrice;
... getters and setters
}
Then you can use the native query in named variant, which should offer some slight optimizations.
List<CustomerItem> lst = em.createNamedQuery("CustomerItem.getAll", CustomerItem.class)
.getResultList();
The use of #SqlResultSetMapping is so that the returned entities are not monitored for changes, but you can still use the defined entity for the result. I believe that by JPA specification it should also work without it, but in Hibernate it doesn't. Could be a bug, or a planned, but not implemented feature, or I could just be misinterpreting the JPA usage, but this workaround does work with Hibernate 5+.
Not sure about the exact reason behind duplicates but SELECT DISTINCT will solve your issue as it will take only distinct records.
Refer using-distinct-in-jpa
I solve this issue by using #SqlResultSetMapping

How to map ONE-TO-MANY native query result into a POJO class using #SqlResultSetMapping

Im working in a backend API using Java and MySql, and I'm trying to use #SqlResultSetMapping in JPA 2.1 for mapping a ONE-TO-MANY native query result into a POJO class, this is the native query:
#NamedNativeQuery(name = "User.getAll”, query = "SELECT DISTINCT t1.ID, t1.RELIGION_ID t1.gender,t1.NAME,t1.CITY_ID , t2.question_id, t2.answer_id FROM user_table t1 inner join user_answer_table t2 on t1.ID = t2.User_ID“,resultSetMapping="userMapping")
And, here is my result SQL mapping:
#SqlResultSetMapping(
name = "userMapping",
classes = {
#ConstructorResult(
targetClass = MiniUser.class,
columns = {
#ColumnResult(name = "id"),
#ColumnResult(name = "religion_id"),
#ColumnResult(name = "gender"),
#ColumnResult(name = "answers"),
#ColumnResult(name = "name"),
#ColumnResult(name = "city_id")
}
),
#ConstructorResult(
targetClass = MiniUserAnswer.class,
columns = {
#ColumnResult(name = "question_id"),
#ColumnResult(name = "answer_id")
}
)
})
And, here is the implementation of the POJO classes: (I just removed the constructor and the getters/setter)
MiniUser class
public class MiniUser {
String id;
String religionId;
Gender gender;
List<MiniUserAnswer> answers;
String name;
String city_id;
}
and the MiniUserAnswer class
public class MiniUserAnswer {
String questionId;
String answerId;
}
My goal is to execute this Query and return a list of MiniUser, and in each MiniUser: a list of his “answers", which is a list of MiniUserAnswer.
after running this code, I got this error:
The column result [answers] was not found in the results of the query.
I know why, it's because there is no “answers" field in the query select statement.
So, how can I accomplish something like this, considering the performance? This answers list may reach 100.
I really appreciate your help, Thanks in advance!
The query "SELECT DISTINCT t1.ID, t1.RELIGION_ID t1.gender, t1.NAME, t1.CITY_ID, t2.question_id, t2.answer_id" does not return a parameter called answers.
To obtain the result you are looking for I would use:
Option 1 (Criteria Builder)
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<UserTableEntity> cq = cb.createQuery(UserTableEntity.class);
Root<UserTableEntity> rootUserTable = cq.from(UserTableEntity.class);
Join<UserTableEntity,UserAnswerTableEntity> joinAnswerTable = rootUserTable.join(rootUserTable_.id) // if the relationship is defined as lazy, use "fetch" instead of "join"
//cq.where() NO WHERE CLAUSE
cq.select(rootUserTable)
entityManager.createQuery(cq).getResultList();
Option 2 (Named query, not native)
#NamedQuery(name = "User.getAll”, query = "SELECT t1 FROM UserTableEntityt1 join fetch t1.answers)
Option 3 (Entity subgraph, new in JPA 2.1)
In User Entity class:
#NamedEntityGraphs({
#NamedEntityGraph(name = "graph.User.Answers", attributeNodes = #NamedAttributeNode("answers"))
})
In DAO set hints in the entity manager:
EntityGraph graph = this.em.getEntityGraph("graph.User.Answers");
Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);

OneToMany + JoinTable + OrderColumn: Order is written correctly but not read

I have an entity that can consist of itself:
class Group {
// ...
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinTable(
name = "group_group",
joinColumns = #JoinColumn(name = "id"),
inverseJoinColumns = #JoinColumn(name = "parentgroup_id")
)
#OrderColumn
private List<Group> groups = new ArrayList<Group>();
#ManyToOne
private Group parentGroup;
public List<Group> getGroups() {
return groups;
}
public Group getGroup() {
return parentGroup;
}
}
I can create a group and add two child groups:
Group parent = new Group();
Group child1 = new Group();
Group child2 = new Group();
parent.getGroups().add(child1);
parent.getGroups().add(child2);
parent = groupRepository.save(parent);
// parent.getGroups().get(0).getId() == child1.getId()
// parent.getGroups().get(1).getId() == child2.getId()
But this seems to be a coincidence. I am able to update the order (e.g. using Collections.sort) and the join table rows are updated correctly.
It does not matter how I load the parent group, the child groups are always in the order of creation. The executed SQL query is:
SELECT t1.ID, t1.PARENTGROUP_ID FROM GROUP t0, GROUP t1 WHERE ((t1.PARENTGROUP_ID = ?) AND (t0.ID = t1.PARENTGROUP_ID))
There is no ORDER BY which seems wrong. How can I persuade EclipseLink to add it?
The problem was that I tried to load the child group entities using a CrudRepository. The repository ignores the #OrderColumn annotation. I fixed it by fetching the parent group and using getGroups().
Try using the #OrderBy which does not rely on the database. You will need to provide a field from Group to order by (note field not column).
#OrderBy("fieldNameGoesHere DESC")
private List<Group> groups = new ArrayList<Group>();
OrderColumn will maintain the order of the list within the database, which carries additional performance and maintenance overhead. OrderBy puts the onus on the persistence provider to maintain the order of the entities, however the order will not persist across persistence contexts.
For more information, checkout this blog post I created regarding #OrderBy.

NHibernate Polymorphic Query on a Collection

I'm trying to write a query in NHibernate. I don't really care if I use the Criteria API or HQL, I just can't figure out how to write the query.
Here's my model:
public class LogEntry { public DateTime TimeCreated { get; set; } }
public class Note : LogEntry { public string Content { get; set; } }
public class Workflow { public IList<LogEntry> Log { get; set; } }
I want the query to return all Workflows that which contain a Note with specific words in the Content of the note.
In pseudo-SQL, I'd write this like:
select w.*
from Workflow w
join w.Log l where l is class:Note
where (Note)l.Content like '%keyword%'
I'm not sure about the Criteria API, but HQL seems to handle polymorphic queries quite well, even when searching on a property that only exists in a specific sub-class. I would expect the following to work:
from Workflow w join w.Log l where l.class = Note and l.Content like '%keyword%'
I don't know if there is a better way, but I use subqueries for this:
from Workflow w
join w.Log l
where l in (
select n
from Note n
where n.Content like '%keyword%'
)
(if this doesn't work, write l.id in (select n.id...)
In criteria, you can directly filter properties that are only available on a subclass, but you shouldn't, because it only filters for the first subtype it finds where this property is defined.
I use subqueries as well:
DetachedCriteria subquery = DetachedCriteria.For<Note>("n")
.Add(Expression.Like("n.Content", "%keyword%"))
.SetProjection(Projections.Property("n.id"));
IList<Workflow> workflows = session.CreateCriteria<Workflow>("w")
.CreateCriteria("w.Log", "l")
.Add(Subqueries.PropertyIn("l.id", subquery))
.List<Workflow>();