I have the following SQL query in T-SQL:
SELECT a.ReportDate,
a.Value,
a.Quantity,
a.ID,
a.Code
FROM AQF.fCalc(#data) a
#data is a varbinary. I'm trying to replicate this query using JPA. (I'm actually using Spring Data JPA 1.10.2.) I have the following defined in MyRepository:
#Query("SELECT a "
+ "FROM function('AQF.fCalc', :data) a "
)
List<MyClass> getCalcData(#Param("data") byte[] data);
MyClass is as follows:
#Entity
#Data // using lombok to create setters, getters, etc.
public class MyClass {
#Id
private Long id;
private Date reportDate;
private BigDecimal value;
private BigDecimal quantity;
private String code;
}
When I call getCalcData, I receive the following error:
antlr.NoViableAltException: unexpected token: function
at org.hibernate.hql.internal.antlr.HqlBaseParser.fromRange(HqlBaseParser.java:1501) [hibernate-core-5.0.9.Final.jar:5.0.9.Final]...
How can I call the function AQF.fCalc (and pass in the byte array) using JPA?
Update 1
I've now tried using a native query too, but now I get an error stating:
java.sql.SQLException: An error occurred while getting new row from user defined Table Valued Function :
System.Xml.XmlException: Unexpected end of file has occurred. The following elements are not closed: Data, Report. Line 24, position 804.
Here's the test code for the native query in MyRepository:
#Query(value = "SELECT a.code "
+ "FROM AQF.fCalc(?1) a",
nativeQuery = true)
List<String> getCalcData(byte[] data);
Update 2
I was able to resolve the error noted in "Update 1" above. The byte array of data in the getCalcData was corrupted. Once I resolved that issue, the error no longer appeared. So, now I can run as a native query. However, I would still like to know how to use JPQL to make this function call.
You can use StoredProcedureQuery. From the javadoc
Interface used to control stored procedure query execution.
Refer this sample code.
Assume your SQL function name is sales_tax in this example.
EntityManager em = factory.createEntityManager();
// Create call stored procedure
em.getTransaction().begin();
StoredProcedureQuery storedProcedure = em.createStoredProcedureQuery("sales_tax");
// set parameters
storedProcedure.registerStoredProcedureParameter("subtotal", Double.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter("tax", Double.class, ParameterMode.OUT);
storedProcedure.setParameter("subtotal", 1f);
// execute SP
storedProcedure.execute();
// get result
Double tax = (Double)storedProcedure.getOutputParameterValue("tax");
System.out.println("Tax is: " + tax);
em.getTransaction().commit();
em.close();
Related
I am trying to sort my table's content on the backend side, so I am sending org.springframework.data.domain.Pageable object to controller. It arrives correctly, but at the repository I am getting org.hibernate.hql.internal.ast.InvalidPathException. Somehow the field name I would use for sorting gets an org. package name infront of the filed name.
The Pageable object logged in the controller:
Page request [number: 0, size 10, sort: referenzNumber: DESC]
Exception in repository:
Invalid path: 'org.referenzNumber'","logger_name":"org.hibernate.hql.internal.ast.ErrorTracker","thread_name":"http-nio-8080-exec-2","level":"ERROR","level_value":40000,"stack_trace":"org.hibernate.hql.internal.ast.InvalidPathException: Invalid path: 'org.referenzNumber'\n\tat org.hibernate.hql.internal.ast.util.LiteralProcessor.lookupConstant(LiteralProcessor.java:111)
My controller endpoint:
#GetMapping(value = "/get-orders", params = { "page", "size" }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<PagedModel<KryptoOrder>> getOrders(
#ApiParam(name = "searchrequest", required = true) #Validated final OrderSearchRequest orderSearchRequest,
#PageableDefault(size = 500) final Pageable pageable, final BindingResult bindingResult,
final PagedResourcesAssembler<OrderVo> pagedResourcesAssembler) {
if (bindingResult.hasErrors()) {
return ResponseEntity.badRequest().build();
}
PagedModel<Order> orderPage = PagedModel.empty();
try {
var orderVoPage = orderPort.processOrderSearch(resourceMapper.toOrderSearchRequestVo(orderSearchRequest), pageable);
orderPage = pagedResourcesAssembler.toModel(orderVoPage, orderAssembler);
} catch (MissingRequiredField m) {
log.warn(RESPONSE_MISSING_REQUIRED_FIELD, m);
return ResponseEntity.badRequest().build();
}
return ResponseEntity.ok(orderPage);
}
the repository:
#Repository
public interface OrderRepository extends JpaRepository<Order, UUID> {
static final String SEARCH_ORDER = "SELECT o" //
+ " FROM Order o " //
+ " WHERE (cast(:partnerernumber as org.hibernate.type.IntegerType) is null or o.tradeBasis.account.retailpartner.partnerbank.partnerernumber = :partnerernumber)"
+ " and (cast(:accountnumber as org.hibernate.type.BigDecimalType) is null or o.tradeBasis.account.accountnumber = :accountnumber)"
+ " and (cast(:orderReference as org.hibernate.type.LongType) is null or o.tradeBasis.referenceNumber = :orderReference)"
+ " and (cast(:orderReferenceExtern as org.hibernate.type.StringType) is null or o.tradeBasis.kundenreferenceExternesFrontend = :orderReferenceExtern)"
+ " and (cast(:dateFrom as org.hibernate.type.DateType) is null or o.tradeBasis.timestamp > :dateFrom) "
+ " and (cast(:dateTo as org.hibernate.type.DateType) is null or o.tradeBasis.timestamp < :dateTo) ";
#Query(SEARCH_ORDER)
Page<Order> searchOrder(#Param("partnerernumber") Integer partnerernumber,
#Param("accountnumber") BigDecimal accountnumber, #Param("orderReference") Long orderReference,
#Param("orderReferenceExtern") String orderReferenceExtern, #Param("dateFrom") LocalDateTime dateFrom,
#Param("dateTo") LocalDateTime dateTo, Pageable pageable);
}
Update:
I removed the parameters from the sql query, and put them back one by one to see where it goes sideways. It seems as soon as the dates are involved the wierd "org." appears too.
Update 2:
If I change cast(:dateTo as org.hibernate.type.DateType) to cast(:dateFrom as date) then it appends the filed name with date. instead of org..
Thanks in advance for the help
My guess is, Spring Data is confused by the query you are using and can't properly append the order by clause to it. I would recommend you to use a Specification instead for your various filters. That will not only improve the performance of your queries because the database can better optimize queries, but will also make use of the JPA Criteria API behind the scenes, which requires no work from Spring Data to apply an order by specification.
Since your entity Order is named as the order by clause of HQL/SQL, my guess is that Spring Data tries to do something stupid with the string to determine the alias of the root entity.
I have created an SQL statement using myBatis SQL builder, but I am to finding documentation on how to execute the string once its created. Currently, I have the following:
CONTROLLER:
#GetMapping("/simpleQuery/{schema}/{fromTable}")
public List <Object> getAllDataFromTable (#PathVariable String schema, #PathVariable String fromTable){
//return queryMapper.getSimpleQueryResults(schema, fromTable);
return queryService.selectDataFromTable(schema, fromTable);
}
SERVICE - where SQL string gets built
public List<Object> selectDataFromTable(String schema, String fromTable){
String selectDataFromTable = new SQL(){{
SELECT ("*");
FROM (schema + "." + fromTable);
}}.toString();
return queryMapper.getSimpleQueryResults(selectDataFromTable);
}
System.out.println(selectDataFromTable);
I've printed the SQL string out to the console and it looks like this:
SELECT *
FROM DB.SOMETABLENAME
UPDATE: I have also tried building the sql string like:
String selectDataFromTable = "SELECT * FROM " + schema +"." + fromTable;
So that the query is all one line but I still get the same results/error
MAPPER - sql string gets exectued
#Select("#{sql}")
List <Object> getSimpleQueryResults(String sql);
The query get executed but an error returns:
Invalid SQL type: sqlKind = UNINITIALIZED
So how do I get the SQL string to execute using mybatis in Springboot.
NOTE - I'm using annotation not XML.
Thank you
I need to update the record in the database and try use this code, but i see this error: java.lang.IllegalArgumentException: org.hibernate.QueryException: could not resolve property: company_id_active of: account.User [update account.User h set h.company_id_active =: ActiveCompanyId where h.user_id =:userId]
User.java
#ManyToOne
#JoinColumn(name = "company_id_active")
#JsonManagedReference
private Company companyActive;
...
Geter,Seter
UserRepositoryImpl.java
public boolean updateActiveCompanyID(int userId, int ActiveCompanyId) {
try {
String SQL= "update User h set h.company_id_active =: ActiveCompanyId where h.user_id =:userId";
Query query = entityManager.createQuery(SQL);
query.setParameter("ActiveCompanyId", ActiveCompanyId);
query.setParameter("userId", userId);
query.executeUpdate();
return true;
} catch (Exception ex) {
MyLogger.logException(ex);
return false;
}
}
Home.java
....
userRepository.updateActiveCompanyID(49,11);
....
by entityManager.createQuery(SQL) you are creating a HQL query and not native query you should use field name corresponding to the definition in the user class. so instead of using company_id_active in your query string, you should use companyActive as :
String SQL= "update User h set h.companyActive= :ActiveCompanyId where h.user_id =:userId";
And also make sure user_id is also your field name in the class.
How to select a record from a table using WHERE clause and comparing instances (patient)
public History findHistory(Patient patient) { History model=null;
Session sesion=util.HibernateUtil.getSessionFactory().getCurrentSession();
String sql="FROM History h WHERE h.patients=" + patient;
try{
sesion.beginTransaction();
model=(History) sesion.createQuery(sql).uniqueResult();
sesion.beginTransaction().commit();
}
catch(Exception e){
sesion.beginTransaction().rollback();
}
return model;
}
That throws a queryException #1562
e.queryString="FROM entidad.Historia h WHERE h.pacientes=entidad.Paciente#3ad3a221"
e.detailMessage="unexpected char: '#'"
The problem with your code is that concatenating patient like you do will just append patient.toString(), which in your case is the default implementation (i.e. classname#hashcode) and it is no use for Hibernate to find out which data to retrieve in the DB.
You need to bind the parameter, first:
String sql = "FROM History h WHERE h.patients = :patient";
Then
model = (History) sesion.createQuery(sql)
.setParameter("patient", patient)
.uniqueResult();
Edit:
SQLGrammarException: could not execute query can occurs for various reason. Try to run the generated query in SqlDeveloper (or any other tool) and see what your DB says. In your case, the last part and .=? cause the error. The cross join is Harming too. I suspect your mapping is incomplete and Hibernate can't find how to join History and Patient. Try to add something like this in History entity:
#ManyToOne
#JoinColumn(name = "patient")
private Patient patient;
Here is my code which uses jdbcTemplate
String SQL = "select branch from branchTable where branch_name = '" + branch + "'";
ArrayList<String> branchList = (ArrayList<String>) jdbcTemplateObject.query(SQL, new RowMapper() {
public Object mapRow(ResultSet resultSet, int i) throws SQLException {
return resultSet.getString("city_desc");
}
});
return branchList;
Now i want to be able to use preparedstatement with a query like "select branch from branchTable where branch_name = ?"
How can i do that with jdbcTemplate ?
Examples i searched show demonstration on how to use it with update or insert query, but not with select query..
Please help.
JdbcTemplate has another query() method which takes arguments of the prepared statement as parameter:
jdbcTemplateObject.query(SQL, new Object[] {branchName}, new RowMapper() {...});
Note that:
SQL should be named sql
You should use List and not ArrayList. Nothing in the javadoc guarantees that an ArrayList is returned. And you shouldn't care about the concrete type of list returned.
You should use a RowMapper<String> and not a raw RowMapper.