I'm having some problens on using the commando "IN" on my jpql, here's the case:
I have these classes:
public class Empresa{
#Id
#Column(nullable = false, unique = true)
#GeneratedValue
private Long id;
#JoinColumn(name = "id_tipo_empresa")
#OneToOne(cascade = CascadeType.DETACH)
private TipoEmpresa tipoEmpresa;
}
public class TipoEmpresa{
#Id
#Column(unique = true, nullable = false)
#GeneratedValue
private Long id;
private Long valor;
}
Then I have this service, that sends a List of "TipoEmpresa" to my EmpresaRepository's query:
SERVICE
public List<Empresa> getEmpresasByTipoEmpresaAndTipoStatus(List<TipoEmpresa>tiposEmpresa) {
List<Empresa> listaDeEmpresas=empresaRepository.getEmpresasByTipoEmpresa(tiposEmpresa);
return listaDeEmpresas;
}
REPOSITORY
#Query(name = "SELECT E FROM Empresa E WHERE E.tipoEmpresa IN(:tipoEmpresa)")
List<Empresa> getEmpresasByTipoEmpresaAndTipoStatus(#Param("tipoEmpresa") List<TipoEmpresa> tipoEmpresa);
As you can see I'm trying to get all "Empresa" that have a "tipoEmpresa" listed on my List, but when colling the service I get this error:
java.sql.SQLException: Operand should contain 1 column(s)
I don't know what to do anymore, tried everything I could think of, what am I doing wrong ?
hi Dude ! first things first, thanks for helping !
.
.
.
I tried g.Irani's suggestion, changed my code, that got like this:
Repository
#Query(name = "SELECT E FROM Empresa E WHERE E.tipoEmpresa.id IN :tipoEmpresa ")
List<Empresa> getEmpresasByTipoEmpresaAndTipoStatus(#Param("tipoEmpresa") List<Long> tipoEmpresa);
Service
//just a test...
List<Long> test =Arrays.asList(100l, 200l);
List<Empresa> listaDeEmpresas=empresaRepository.getEmpresasByTipoEmpresa(test);
But now I got this error message:
java.lang.IllegalArgumentException: Parameter value element [100] did not match expected type [br.com.entities.TipoEmpresa (n/a)]
Any thoughts about it ?
after IN in WHERE clause, you should present a list with 1 column.
tipoEmpresa in below query of your code is a class (with 2 columns)
#Query(name = "SELECT E FROM Empresa E WHERE E.tipoEmpresa IN(:tipoEmpresa)")
you can use
#Query(name = "SELECT E FROM Empresa E WHERE E.tipoEmpresa.id IN(:tipoEmpresaIDs)")
then prepare a list of tipoEmpresa IDs and set to :tipoEmpresaIDs
notice that, In JPA when using IN, you don't need ( ) after IN. (just use ... IN :tipoEmpresaIDs)
Related
I don't know what may be causing this error.
I need to sum all the values of column totalareceber from tb_oscolab in a way that each line contains a colaborador_id and its total value. The period must be within the dates from another table named tb_os as data_inicio.
This is my query in Spring boot and it seems to be correct:
public interface OSColabRepository extends JpaRepository<OSColab, Long> {
#Query(value = "SELECT colaborador_id, SUM(totalareceber) FROM tb_oscolab INNER JOIN tb_os ON tb_oscolab.os_id = tb_os.id WHERE tb_os.data_inicio BETWEEN :startDate AND :endDate GROUP BY colaborador_id", nativeQuery = true)
List<OSColab> getOSColabSumBetweenDates(#Param("startDate") Date startDate, #Param("endDate") Date endDate);
}
But it is returning:
SQL Error: 0, SQLState: S0022
java.sql.SQLException: Column 'id' not found.
But there is a column id in both tb_os and tb_oscolab
OS
#Id
#Column(name="id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
OSCOLAB
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
I found these articles, but I could not understand the solution very well as I'm a starter:
SQL Query returning column 'id' not found in Spring Boot
https://pt.stackoverflow.com/questions/473427/java-sql-sqlexception-column-id-not-found
Could someone please help me to identify the problem here?
You only select some columns, not the complete object of type OSColab which you exect as the result. Changing List<OSColab> getOSColabSumBetweenDates to List<Object[]> getOSColabSumBetweenDates
try doing this
#Query(value = "SELECT colaborador_id, SUM(totalareceber) FROM tb_oscolab INNER JOIN tb_os ON tb_oscolab.id = tb_os.id WHERE tb_os.data_inicio BETWEEN :startDate AND :endDate GROUP BY colaborador_id", nativeQuery = true)
I get following error message when trying to fire an SQl Statement....the exception:
QuerySyntaxException: unexpected token: a near line 1, column 127 [SELECT DISTINCT e FROM com.taqwaapps.entity.Event e, com.taqwaapps.entity.Appointment a WHERE e.eventId = a.event.eventIdĀ AND a.district.city.name = 'Jeddah' ]
It seems that following SQL Statement is not correct:
#Query("SELECT DISTINCT e "
+ "FROM Event e, Appointment a "
+ "WHERE e.eventId = a.event.eventIdĀ "
+ "AND a.district.city.name = 'Jeddah' ")
List<Event> getEventsFiltered();
My Objects are:
#Entity
public class Event {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long eventId;
}
and
#Entity
public class Appointment {
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "eventId")
private Event event;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "districtId")
private District district;
What is wrong?
I found the Problem:
In the past in school time I learned from my informatics Teacher that in SQL it can happen that there is an hidden character which we entered or which got entered but we dont see it. So just write the SQL again using plain writing
I have a User entity with skills property as a type List. I want to query the User table against a list of skills in such a way that if all the skills are present in skill column then only a match is found unless no.
I have used JPQL for this but it matches each element in the list one by one using the IN clause.
User Class
#Entity(name = "App_User")
//table name "user" is not allowed in postgres
public class User {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
#Column(name = "user_id", updatable = false, nullable = false)
#Setter(AccessLevel.NONE)
private UUID id;
#Column(name = "user_name")
#NotBlank(message = "Name is mandatory")
private String name;
#Column(name = "user_email")
#NotBlank(message = "Email is mandatory")
private String email;
// Current point balance of the user
#Column(name = "points")
private int points;
#ElementCollection(fetch = FetchType.EAGER)
#Column(name = "skills")
#NotEmpty
private List<String> skills = new ArrayList();
}
JPA query that I have used is
SELECT u FROM App_User u JOIN u.skills skill where skill in :skillList
If I want to match a list of skills like this Arrays.asList("skill1","skill2","skill3")then I want only those users in the result who have all of these skills, not one or two. Above used IN clause return the same result.
I have read that it is not possible to compare two lists in JPQL so how can I achieve this using CriteriaBuilder CriteriaQueryAPI?
You can do this
#Query(value = "SELECT u FROM User u LEFT JOIN u.skills sk WHERE sk IN :skillList"
+ " GROUP BY u HAVING COUNT( sk) = :skillListSize")
List<User> findBySkills(#Param("skillList") List<String> skills,
#Param("skillListSize") long skillListSize);
Here, group by user and then check group having all skills or not using size. So it will fetch all user having all skills those are given.
Or use this way
#Query(value = "SELECT u FROM User u LEFT JOIN u.skills sk GROUP BY u"
+ " HAVING SUM(CASE WHEN sk IN (:skillList) THEN 1 ELSE 0 END) = :skillListSize")
List<User> findBySkills(#Param("skillList") List<String> skills,
#Param("skillListSize") long skillListSize);
And if you want a solution for user having exact same skill not more than the given list then see this solution.
The problem that you want to solve is called Relational Division.
SELECT u.* FROM App_User u
INNER JOIN
(
SELECT skills FROM App_User WHERE skills IN (list values)
GROUP BY skills
HAVING COUNT(DISTINCT skills) = (size of list)
) w ON u.user_name = w.user_name
I have an Table and Pojo in my Spring Boot application like below.
#Entity
#Table(name = "attendance_summary")
public class AttendanceSummary implements Serializable {
#Id
#SequenceGenerator(name = "attendance_summary_id_seq",
sequenceName = "attendance_summary_id_seq",
allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "attendance_summary_id_seq")
#Column(name = "id", updatable = false)
public Integer id;
#Column(name = "emp_id", nullable = false)
public Integer empId;
#Column(name = "designation_id")
public Integer designationId;
#Column(name = "designation_category_id")
public Integer designationCategoryId;
#Column(name = "department_id")
public Integer departmentId;
......
}
Now I want have dynamic inputs for these fields. Meaning user might select
a list of empIds, designationIds.... or any combinations of them or even none of them.
If they select none of the fields I need to return all the rows from the table in the database.
But in jpa when we write methods in repository we have to specify the field names like
public interface AttendanceSummaryRepository extends JpaRepository<Integer,AttendanceSummary>{
List<AttendanceSummary> findByEmpIdAndDesignationId....(List<Integer> empIdList,List<Integer> designationIdList ... );
}
Which means if any of these parameters are null I will get an error or an exception and as a result I will miss some data.
where as in PHP or some other language like that I can just check the value of the desired filters and just dynamically add a where clause in the query.
query= "Select * from attendance_summary where ";
if(empIdList != null)
query = query + " emp_id in empIdList "
if(designationIdList != null)
query = query + " designation_id in designationIdList "
.....
//You get the idea.
Is there any way to do so with jpaDataMethods and if yes how. Any detailed explanation / link to resources are much appreciated.
Sorry for poor english and if I couldn't explain my problem properly.
Take a look at Criteria API. It allows you to create dynamic queries.
In your example, something similar to this could work:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<AttendanceSummary> query = cb.createQuery(AttendanceSummary.class);
Root<AttendanceSummary> root = query.from(AttendanceSummary.class);
List<Predicate> predList = new ArrayList<>();
if (empIdList != null) {
predList.add(root.get('empId').in(empIdList));
}
if (designationIdList != null) {
predList.add(root.get('designationId').in(designationIdList));
}
// ... You get the idea.
Predicate[] predicates = new Predicate[predList.size()];
predList.toArray(predicates);
query.where(predicates);
return entityManager.createQuery(query).getResultList();
You can achieve this by using #Query annotation. Please refer spring guide for more details.
#Query(value = "from attendance_summary where (emp_id in (?1) or ?1 is null) and (designation_id in (?2) or ?2 is null)" )
Query Detail:
SELECT * #implicit so removed
FROM attendance_summary
WHERE ( emp_id IN (?1) #true when IDs are not null, thus apply filter
OR ?1 IS NULL #true when user input null, return all rows )
AND ( designation_id IN (?2) #true when IDs are not null, thus apply filter
OR ?2 IS NULL #true user input null, return all rows)
Example Project on github with spring-boot, jpa & h2. Look for SchoolController & SchoolRepo classes, applying the same logic, the endpoints \school will filter the result for input Ids & \allschool will return everything as input is null.
One component of my application logs the parameters that an User send with an http URL. The mapping is as follow:
public class ActivityLog {
#Column(name = "id")
private Long id;
#OneToMany(mappedBy="activityLog", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
protected List<ActivityLogParameter> activityLogParameters = new ArrayList<ActivityLogParameter>();
}
public class ActivityLogParameter {
#Column(name = "id")
private Long id;
#Column(name = "key", length=10)
protected String key;
#Column(name = "value", length=50)
protected String value;
#ManyToOne(fetch = FetchType.LAZY, cascade={CascadeType.MERGE})
#JoinColumn(name="activity_log_id")
protected ActivityLog activityLog;
}
Let assume with every URL always 2 parameters are being passed: U and L
I need to create a query using hibernate's Criteria (mandatory from specification) so that it returns me all the ActivityLogs where both parameters matche a certain value. i.e.: U=aaa and L=bbb
I tried like this:
Criteria criteria = getCurrentSession().createCriteria(ActivityLog.class, "al");
// create alias
criteria = criteria.createAlias("activityLogParameters", "alp",JoinFragment.LEFT_OUTER_JOIN);
// create transformer to avoid duplicate results
criteria = criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
criteria = criteria.setFetchMode("al.activityLogParameters", FetchMode.SELECT);
//filters
criteria = criteria.add(Restrictions.or(Restrictions.eq("alp.value", UValue), Restrictions.ne("alp.key", "L")));
criteria = criteria.add(Restrictions.or(Restrictions.eq("alp.value", LValue), Restrictions.ne("alp.key", "U")));
But here I'm stuck. I tried to add projections like distinct and group by on this but it's not enough to have a correct result.
I'm trying also to use this criteria as a sub criteria, so to count the number of rows for any ActivityLog and keep only the records that have count(*) = 2 (all parameters match the condition) but I can't find how to do it with Subqueries.
Any idea on how to solve the above problem? In SQL I would do something like this:
select activity_log_id from (
select count(*) as ct, activity_log_id
from activity_log_parameter alp inner join activity_log al on alp.activity_log_id=al.id
where (alp.value='visitor' or alp.key<>'U')
and (alp.value='room1' or alp.key<>'L')
group by activity_log_id
) as subq
where subq.ct = 2
Thanks
Solved using a sub query
DetachedCriteria subquery = DetachedCriteria.forClass(ActivityLogParameter.class, "alp")
.createAlias("activityLog", "al",JoinFragment.LEFT_OUTER_JOIN)
.setProjection(Projections.projectionList()
.add(Projections.count("id"), "alpId"));
subquery = subquery.add( Property.forName("al.id").eqProperty("mainAl.id") );
subquery = subquery.add(Restrictions.or(Restrictions.eq("alp.value", UValue), Restrictions.ne("alp.key", "L")));
subquery = subquery.add(Restrictions.or(Restrictions.eq("alp.value", LValue), Restrictions.ne("alp.key", "U")));
Criteria criteria = getCurrentSession().createCriteria(type, "mainAl");
criteria = criteria.add(Subqueries.eq(new Long(2), subquery));