I have problem with SQL Query.
I have entity like this:
#Enumerated(value = EnumType.STRING)
#Column(name = "STATUS")
private OrderStatus orderStatus;
#OneToMany(
targetEntity = Location.class,
fetch = FetchType.LAZY,
mappedBy = "order")
private List<Location> locations = new ArrayList<>();
I need to get objects by status and by locations in List locationsId.
Problem is because even this query dosnt find any objects:
"SELECT o FROM Order WHERE o.orderStatus = ?1"
List<Order> findOrderByOrderStatus(OrderStatus status)
Can anybody help me to write properly pql, hql or sql query ?
Thanks
Related
I am working with Spring Data JPA and Entity Graphs.
I have the following Entity structure:
Result entity has a list of SingleQuestionResponse entities, and the SingleQuestionResponse entity has a set of Answer entities (markedAnswers).
public class Result {
...
#OneToMany(cascade = CascadeType.PERSIST)
#JoinColumn(name = "result_id", nullable = false)
private List<SingleQuestionResponse> responses;
...
}
public class SingleQuestionResponse {
...
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "singlequestionresponses_answers",
joinColumns = #JoinColumn(name = "single_question_response_id"),
inverseJoinColumns = #JoinColumn(name = "answer_id")
)
private Set<Answer> markedAnswers;
...
}
and Answer just has simple-type fields.
Now, I would like to be able to fetch Result, along with all responses, and the markedAnswers in one query. For that I annotated the Result class with:
#NamedEntityGraph(name = "graph.Result.responsesWithQuestionsAndAnswersEager",
attributeNodes = #NamedAttributeNode(value = "responses", subgraph = "responsesWithMarkedAnswersAndQuestion"),
subgraphs = {
#NamedSubgraph(name = "responsesWithMarkedAnswersAndQuestion", attributeNodes = {
#NamedAttributeNode("markedAnswers"),
#NamedAttributeNode("question")
})
}
)
an example of usage is:
#EntityGraph("graph.Result.responsesWithQuestionsAndAnswersEager")
List<Result> findResultsByResultSetId(Long resultSetId);
I noticed, that calling the findResultsByResultSetId method (and other methods using this entity graph) results in responses (SingleQuestionResponse entities) being multiplied by the number of markedAnswers. What I mean by that is that result.getResponses() returns more SingleQuestionResponse objects than it should (it returns one response object per each markedAnswer).
I realize this is due to Hibernate making a Cartesian product with the join, but I have no idea how to fix it.
Can you help please? Thanks
You have to use the DISTINCT operator. With Spring Data JPA, this can be done by naming the method findDistinctResultsByResultSetId
I have the following setup:
#Entity
public class Function {
private String name;
#OneToMany(mappedBy = "function", cascade = CascadeType.ALL, orphanRemoval = true)
#Where(clause = "type = 'In'") // <=== seems to cause problems for CriteriaBuilder::size
private Set<Parameter> inParameters = new HashSet<>();
#OneToMany(mappedBy = "function", cascade = CascadeType.ALL, orphanRemoval = true)
#Where(clause = "type = 'Out'") // <=== seems to cause problems for CriteriaBuilder::size
private Set<Parameter> outParameters = new HashSet<>();
}
#Entity
public class Parameter {
private String name;
#Enumerated(EnumType.STRING)
private ParameterType type;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "function_id")
private Function function;
}
The overall problem I am trying to solve is find all functions that have outParameters with an exact dynamic set of names. E.g. find all function with outParameters whose names are exactly ('outParam1', 'outParam2')
This seems to be an "exact relational division" problem in SQL, so there might be better solutions out there, but the way I've gone about doing it is like this:
List<String> paramNames = ...
Root<Function> func = criteria.from(Function.class);
Path outParams = func.get("outParameters");
Path paramName = func.join("outParameters").get("name");
...
// CriteriaBuilder Code
builder.and(
builder.or(paramNames.stream().map(name -> builder.like(builder.lower(paramName), builder.literal(name))).toArray(Predicate[]::new)),
builder.equal(builder.size(outParams), paramNames.size()));
The problem I get is that the builder.size() does not seem to take into account the #Where annotation. Because the "CriteriaBuilder code" is nested in a generic Specification that should work for any type of Entity, I am not able to simply add a query.where() clause.
The code works when a function has 0 input parameters, but it does not work when it has more. I have taken a look at the SQL that is generated and I can see that it's missing:
SELECT DISTINCT
function0_.id AS id1_37_,
function0_.name AS name4_37_,
FROM
functions function0_
LEFT OUTER JOIN parameters outparamet2_ ON function0_.id = outparamet2_.function_id
AND (outparamet2_.type = 'Out') -- <== where clause added here
WHERE (lower(outparamet2_.name)
LIKE lower(?)
OR lower(outparamet2_.name)
LIKE lower(?))
AND (
SELECT
count(outparamet4_.function_id)
FROM
parameters outparamet4_
WHERE
function0_.id = outparamet4_.function_id) = 2 -- <== where clause NOT added here
Any help appreciated (either with a different approach to the problem, or with a workaround to builder.size() not working).
The where annotation is in the function entity, in the subquery you have not used that entity so the operation is correct, try using the function entity as root of the subquery, or to implement the where manually.
For the next one, it would be recommended that you include the complete Criteria API code to be more precise in the answers.
Good Morning,
I have the following table:
_____________________
Name Status Num
A Good 6
B Bad 6
C Bad 7
I want to select all rows where "Status = 'Good' AND Num = '6'" OR "Status = 'Bad' AND Num = '7'"
So I would select rows with Names A and C from the above reference data.
I am hoping to be able to pass in two equal sized lists (ordered in the way I desire the query to be constructed), but have been unable to figure this out. The standard queries (SelectXByStatusAndNum) query generates SQL using 'IN' statements, and returns all 3 rows in the above data instead of just two.
Any insight appreciated
I believe the best way is to use a #Query annotation in the repository:
#Entity
#Table(name = "table_name")
public class TableName {
#Id
#Basic(optional = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "status")
private String status;
#Column(name = "num")
private Integer num;
/**
* getters/setters, etc
**/
public interface TableNameRepository extends CrudRepository<TableName, Long> {
#Query("select t from TableName t where (status = :status1 and num = :num1 or status = :status2 and num = :num2)")
List<TableName> findByStatusAndNumOrStatusAndNum(#Param("status1") String status1,
#Param("num1") Integer num1, #Param("status2") String status2, #Param("num2") Integer num2);
}
It's a way to get values for a given parameters. In case of collections as parameters there is no in-box case due to RDBMS concept. You just can write some java code based on the key-valued parameter and collect results:
public interface TableNameRepository extends CrudRepository<TableName, Long> {
List<TableName> findByStatusAndNumIn(String status, Collection<Integer> nums);
}
List<TableName> result = new ArrayList<>();
List<TableName> itemGood = findByStatusAndNumIn("Good", numsGood);
List<TableName> itemBad = findByStatusAndNumIn("Bad", numsBad);
result.addAll(itemGood);
result.addAll(itemBad);
Instead of equal size lists, I suggest creating an object with both status and num and provide a list of those - I called it CustomCriteria below. Since there is no straightforward way to handle this with standard spring data query methods I believe the best way is to do this with criteria builder in a custom repository that builds the list of OR's from a list of AND predicates based on the criteria entries in the provided list. For example:
public List<YourTable> findMatching(List<CustomCriteria> customCriteriaList) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<YourTable> criteriaQuery = criteriaBuilder.createQuery(YourTable.class);
Root<YourTable> itemRoot = criteriaQuery.from(YourTable.class);
List<Predicate> predicates = new ArrayList<>(customCriteriaList.size());
for (CustomCriteria customCriteria : customCriteriaList) {
Predicate predicateForNum
= criteriaBuilder.equal(itemRoot.get("num"), customCriteria.getNum());
Predicate predicateForStatus
= criteriaBuilder.equal(itemRoot.get("status"), customCriteria.getStatus());
predicates.add(criteriaBuilder.and(predicateForNum, predicateForStatus));
}
Predicate finalPredicate = criteriaBuilder.or(predicates.toArray(new Predicate[predicates.size()]));
criteriaQuery.where(finalPredicate);
return entityManager.createQuery(criteriaQuery).getResultList();
}
I'm trying to improve the update performance of an application using Hibernate 4.3.
I have a list of people and towns, like so
#Table(name = "PERSON", schema = "COUNTY", catalog = "")
#Entity
public class PersonBean{
#MapsId("town")
#ManyToOne
#JoinColumn(name = "TOWN_ID")
private Long townID;
#Column(name = "NAME")
#Basic
private String personName;
#Column(name = "UPDATE_DATE")
#Basic
private Date updateDate;
...
}
...
#Table(name = "TOWNS", schema = "COUNTY")
#Entity
public class TownBean {
#Column(name = "ID")
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "town_id_gen")
#SequenceGenerator(name = "town_id_gen", sequenceName = "TOWN_SEQ")
private Long id;
#Column(name = "NAME")
#Basic
private String name;
... (more simple properties) ...
#OneToMany(fetch = FetchType.LAZY, mappedBy = "people", cascade = CascadeType.ALL, orphanRemoval = true)
#BatchSize(size = 50)
private List<PersonBean> people;
When the update of these items is done simply by calling
repository.saveAndFlush(town);
Some of these towns are pretty big, 20,000 people in a town is not unusual.
In the DAO and resource, all updates occur at a town level... so for example, to change the name of one person in the town, you would PUT a new town resource with the complete new list of names.
In the database, this requires the update of all the person rows referencing that town to change the updateDate, and Hibernate does this by issuing one update query for each person, which can mean 20,000 queries. (The #BatchSize annotation applies only to reading, sadly.) This kills system performance.
I think I can optimize this by replacing the repository.saveAndFlush(town) operation with a custom transaction logic that opens a transaction, flushes the list of people in groups using batching, and then writes the town object...
Is there a smarter way I can reduce the number of update queries Hibernate sends (by batching or otherwise) without changing the system behavior? Maybe there's some cleverer way using custom SQL, Named Entity Graphs or something else?
I'm trying to convert a simple sql statement to jpql but vainly. I have an entity "Banque" that has many "Compte" and i'm trying to get all the "Compte" for a specific "Banque" using id_Banque.
This is the sql statement :
SELECT COMPTE.* FROM COMPTE INNER JOIN BANQUE ON COMPTE.ID_BANQUE=BANQUE.ID_BANQUE;
============================Entity Banque=========================
#Entity
#Table(name="BANQUE")
public class Banque implements Serializable{
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="banque_seq_gen")
#SequenceGenerator(name="banque_seq_gen", sequenceName="BANQUE_SEQ", allocationSize = 1, initialValue = 1)
private Long id_banque;
#OneToMany(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY, mappedBy = "Banque")
private Set<Compte> comptes;
public Banque(){
}
}
============================Entity Compte=========================
#Entity
#Table(name="COMPTE")
public class Compte implements Serializable{
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="compte_seq_gen")
#SequenceGenerator(name="compte_seq_gen", sequenceName="COMPTE_SEQ", allocationSize = 1, initialValue = 1)
private Long id_compte;
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
#JoinColumn(name = "ID_BANQUE")
private Banque Banque;
public Compte(){
}
}
And the following method help me to get all the comptes (Entity compte) associated to a specific Banque (from id_banque) :
public Set<Compte> getComptesFromIdBanque(Long id_banque){
EntityManager em=new JPAContainerFactory().createEntityManagerForPersistenceUnit("addressbook");
Query query = em.createQuery("SELECT c FROM Compte c INNER JOIN c.Banque b WHERE b.id_banque = :id_banque");
query.setParameter("id_banque", id_banque);
Set<Compte> comptes = new HashSet<>(query.getResultList());
return comptes;
}
This is an illustration of the two tables and what i'd like to have :
Then a display this list of comptes in a ComboBox :
compteCombo.addItems(saut.getComptesFromIdBanque(banque.getId_banque()));
compteCombo.setItemCaptionPropertyId("numero");
Unfortunately this doesn't show any item in the comboBox.
I hope i will find a pertinent answer as i searched for this in vain. It'll help me a lot.
Thanks.
Your JPA query seems fine to me (although as #Davids mentioned it could be simplified to SELECT c FROM Compte c WHERE c.banque.id_banque = :id_banque).
My guess is your ComboBox caption is not set up properly to show anything.
First, write a test to make sure your JPA function returns the right data. Then play around with the setItemCaptionMode of ComboBox to get the right setting. Assuming your Compete class has a getNumero() method, you can set up your Combobox like this:
compteCombo = new ComboBox( "Caption", saut.getComptesFromIdBanque(banque.getId_banque()) );
compteCombo.setItemCaptionMode( ItemCaptionMode.PROPERTY );
compteCombo.setItemCaptionPropertyId( "numero" );