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)
Related
I'm sending a very simple query to the database, but I'm getting an error. It feels like I'm missing something very simple. I guess it wouldn't allow me to create it because the word order is a keyword on the h2 db, so I put it in quotation marks within the table annotation.
#Query(value = "select * from `ORDER` o where o.basket_id= :basketId ", nativeQuery = true)
Optional<Order> getOrderByBasketId(Long basketId);
#Entity
#Getter
#Setter
#Table(name = "`ORDER`")
public class Order extends BaseExtendedModel{
private BigDecimal price;
#Enumerated(EnumType.STRING)
private OrderStatus orderStatus;
#OneToOne
private Customer customer;
#OneToOne(cascade = CascadeType.MERGE)
private Basket basket;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "order")
private OrderAddress orderAddress;
}
{
"errorMessage": "could not prepare statement; SQL [select * from `ORDER` o where o.basket_id= ? ]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement"
}
The problem is easier to identidy when you have a look at the logs. You'll see an entry like this:
org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "ORDER" not found; SQL statement:
So let's see what SQL statements are executed. So we add the following to application.properties
spring.jpa.show-sql=true
Assuming you let spring boot create your tables, you will see the following:
Hibernate: drop table if exists "order" CASCADE
Hibernate: create table "order" ...
And when we hit the repository method we see
select * from `ORDER` o where o.name= ? [42102-200]
So why did it create the table in lowercase, even though we specified #Table(name = "`ORDER`")?
The default for spring.jpa.hibernate.naming.physical-strategy is org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy, which
replaces dots with underscores
changes CamelCase to snake_case
lower-cases table names.
But we want it to take the names we use in #Table. That works when setting the property to spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl.
Yor native query will need the matching casing though.
You need to use index parameters
#Query(value = "select * from `ORDER` o where o.basket_id= ?1", nativeQuery = true)
Optional<Order> getOrderByBasketId(Long basketId);
or named parameters
#Query(value = "select * from `ORDER` o where o.basket_id= :basketId", nativeQuery = true)
Optional<Order> getOrderByBasketId(#Param("basketId") Long basketId);
I have the following entity (some columns omitted for brevity):
#Entity
#Table(name = "INSTRUCTION")
public class Instruction {
#Id
#Column(name = "ID", nullable = false, unique = true)
public Long id;
#Column(name = "CURRENT_STATUS", nullable = false)
private InstructionState currentStatus;
#Column(name = "SUBTYPE", nullable = false)
private InstructionAction subtype;
//Getters & Setters
}
I want to write the following query in JPA to retrieve a count of the instructions grouped by their CURRENT_STATUS and SUBTYPE. I know that the following SQL works:
SELECT CURRENT_STATUS, SUBTYPE, COUNT(*) count
FROM (SELECT ID, CURRENT_STATUS, SUBTYPE
FROM INSTRUCTION
WHERE VALUE_DATE= '17-JUN-21'
AND LAST_UPDATED >= '16-JUN-21'
AND LAST_UPDATED < '17-JUN-21'
GROUP BY ID, CURRENT_STATUS, SUBTYPE)
GROUP BY CURRENT_STATUS, SUBTYPE;
I want to take the result from this query and map it to a new object call InstructionCount:
public class InstructionCount {
private InstructionState status;
private InstructionAction subType;
private Integer count;
public InstructionCount(final InstructionState status, final InstructionAction subType, final Integer count) {
this.status = status;
this.subType = subType;
this.count = count;
}
//Getters and setters
}
The Problem
I have come up with the following query in JPA for this in my repository class:
#Query(value = "SELECT new com.modles.InstructionCount(CURRENT_STATUS status, SUBTYPE subType, COUNT(*) count) \n" +
"FROM (SELECT ID, CURRENT_STATUS, SUBTYPE \n" +
"\t\tFROM LTD_RTGS_CASHINSTRUCTION \n" +
"\t\tWHERE VALUE_DATE= :valueDate \n" +
"\t\tAND LAST_UPDATED >= :lastUpdatedFrom \n" +
"\t\tAND LAST_UPDATED < :lastUpdatedTo \n" +
"\t\tGROUP BY ID, CURRENT_STATUS, SUBTYPE)\n" +
"GROUP BY CURRENT_STATUS, SUBTYPE", nativeQuery = true)
List<InstructionCount> findInstructionCounts(#Param("valueDate") LocalDate valueDate, #Param("lastUpdatedFrom") LocalDateTime lastUpdatedFrom, #Param("lastUpdatedTo") LocalDateTime lastUpdatedTo);
The issue is that this does not work, and I have found it is because I cannot use the approach of mapping it to a new object using the SELECT new com.modles.InstructionCount with nativeQuery = true. However when I try to remove the nativeQuery = true part and run my test I get the following error:
I also notice that the SQL gets syntax errors in the IDE, highlighted on the second SELECT statement, so I presume there is an issue with this format when not using nativeQuery.
Can anyone help with how I can resolve this issue? I know this can work, because when I remove the SELECT new com.modles.InstructionCount part, it will just return a list of Object[] with the correct values I'm expecting, but I would much prefer to map this to the correct object as part of the query. Alternatively, if there is a way to write this query using the Specification API, I would also use that approach!
Below query should just work just fine and there is no need of your inner/sub query
SELECT CURRENT_STATUS, SUBTYPE, COUNT(ID)
FROM INSTRUCTION
WHERE VALUE_DATE= '17-JUN-21'
AND LAST_UPDATED >= '16-JUN-21'
AND LAST_UPDATED < '17-JUN-21'
GROUP BY CURRENT_STATUS, SUBTYPE;
This should works seemlessly while returning the result as
InstructionCount
------->Edit------------->
#Query(value = "SELECT new com.modles.InstructionCount(currentStatus, subtype, count(id)) FROM Instruction WHERE valueDate= :valueDate AND lastUpdatedFrom >= :lastUpdatedFrom AND lastUpdatedTo < :lastUpdatedTo GROUP BY id, currentStatus, subtype")
List<InstructionCount> findInstructionCounts(#Param("valueDate") LocalDate valueDate, #Param("lastUpdatedFrom") LocalDateTime lastUpdatedFrom, #Param("lastUpdatedTo") LocalDateTime lastUpdatedTo);
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.
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)
I have two entities in MySQL as below. The primary key of nnm_tran is a composite of id and source. The primary key of bargains is actually a foreign key link to the nnm_tran table
I'm trying to use JPA inheritance to represent these.
nnm_tran entity
#Entity
#Table(name = "nnm_tran")
#IdClass(CommonTransactionKey.class)
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "bargain_flag", discriminatorType = DiscriminatorType.CHAR)
#DiscriminatorValue("N")
public class CommonTransaction {
#Id
#Column(name = "id", nullable = false)
private String transactionId;
#Column(name = "plan_number", nullable = false)
private String planNumber;
#Column(name = "tran_date")
private LocalDateTime transactionDatetime;
#Column(name = "bargain_flag")
private String bargainFlag;
...
}
bargains entity
#Data
#EqualsAndHashCode(callSuper = true)
#Entity
#Table(name = "bargains")
#DiscriminatorValue("B")
#PrimaryKeyJoinColumns({ #PrimaryKeyJoinColumn(name = "nnm_tran_id", referencedColumnName = "id"), #PrimaryKeyJoinColumn(name = "nnm_tran_source", referencedColumnName = "source") })
public class Bargain extends CommonTransaction implements Serializable {
#Column(name = "unit_price")
private BigDecimal unitPrice;
#Column(name = "client_price")
private BigDecimal clientPrice;
...
}
I think so far this is all hooked up correctly. My problem comes when I attach a spring-data repository with a custom query.
Repository
public interface CommonTransactionRepository extends CrudRepository<CommonTransaction, CommonTransactionKey> {
#Query("select t from CommonTransaction t left join IoPlan p ON t.planNumber = p.planNumber "
+ "where (p.planNumber is NULL or p.planNumber = '') "
+ "and t.transactionDatetime between ?1 and ?2 "
+ "and t.cancelled = false")
public Iterable<CommonTransaction> findOrphanedTransactionsByTranDate(LocalDateTime fromDate, LocalDateTime toDate);
...
}
When this gets proxied and the method is executed it generates the SQL statement
SELECT DISTINCT nnm_tran.bargain_flag FROM nnm_tran t1 LEFT OUTER JOIN io_plan t0 ON (t1.plan_number = t0.plan_number) WHERE ((((t0.plan_number IS NULL) OR (t0.plan_number = ?)) AND (t1.tran_date BETWEEN ? AND ?)) AND (t1.CANCELLED = ?))
The issue with this is that the nnm_tran table is aliased to t1 but the discriminator column is referencing the full table name nnm_tran.bargain_flag The result is a lovely
UnitOfWork(17171249)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'nnm_tran.bargain_flag' in 'field list'
Question here is, am I doing something wrong or is this a bug in spring-data and/or eclipselink?
Versions: spring-data 1.7.2, Eclipselink 2.5.2, MySQL 5.6.28
Using #manish's sample app as a starting point I started layering back on the complexity that was missing and quickly stumbled across the thing causing the rogue SQL. It was down to the join I had performed in the JPQL
NOTE: If you've come here from the future then ignore the remainder of this answer and instead use #Chris's comment instead.
Most of the time I don't need to look at or even think about the IoPlan table that can be seen in the #Query
#Query("select t from CommonTransaction t left join IoPlan p ON t.planNumber = p.planNumber "
+ "where (p.planNumber is NULL or p.planNumber = '') "
+ "and t.transactionDatetime between ?1 and ?2 "
+ "and t.cancelled = false")
and so this table is not a part of the CommonTransaction entity as a field. Even the result of this query doesn't really care because it's looking only as a one off for CommonTransaction with no associated join in the IoPlan table.
When I added the join back in to the sample app from #manish it all broke in the same way my app has in EclipseLink, but broke in a different way for Hibernate. Hibernate requires a field for you to join with, which if you ask me defeats the purpose of writing the join in the #Query. In fact in Hibernate you have to define the join purely in JPA so you might as well then use dot notation to access it in the JPQL.
Anyway, going along with this idea I tried adding a dummy field to hold an IoPlan in my CommonTransaction entity and it almost worked. It defaulted some of the join logic but it was closer
SELECT DISTINCT t1.bargain_flag FROM nnm_tran t1 LEFT OUTER JOIN io_plan t0 ON ((t0.ID = t1.IOPLAN_ID) AND (t1.plan_number = t0.plan_number)) WHERE ((((t0.plan_number IS NULL) OR (t0.plan_number = ?)) AND (t1.tran_date BETWEEN ? AND ?)) AND (t1.CANCELLED = ?))
In this case t1.IOPLAN_ID and t0.ID don't exist. So I ended up defining the entire join in my CommonTransaction entity
#OneToOne
#JoinColumn(insertable = false, updatable = false, name = "plan_number", referencedColumnName = "plan_number")
private IoPlan ioPlan;
and voila, it started working. It's not pretty and now I have a redundant join condition
LEFT OUTER JOIN io_plan t1
ON ((t1.plan_number = t0.plan_number) AND (t0.plan_number = t1.plan_number))
but I can fix that. It's still annoying that I have to define a field for it whatsoever, I don't actually want or need it there, not to mention that the result from this query is returning CommonTransaction entities that have no IoPlan so the field will be permanently null.