JPA NAMED Query issue - java

Getting below database excpetion, help required
service() for servlet catalogservice threw exception: java.lang.IllegalArgumentException: Named query not found: SELECT OMX_PLAN_ID, PLAN_ID,DECODE(plan_id,0,ser_input_total_amount,first_payment) first_paymenmt From (SELECT OMX_PLAN_ID, PLAN_ID,(SELECT DECODE(fraction,0,fixed_payment_amount, (( fraction/100) * :useinput_total_amount)) From TFN.VW_OMX_PAYMENT_PLAN_DETAILS i WHERE o.OMX_PLAN_ID=i.OMX_PLAN_ID AND i.OMX_PLAN_ID=:omxPlanId AND i.PAYMENT_ID=1) first_payment FM TFN.VW_OMX_PAYMENT_PLANS o ) WHERE OMX_PLAN_ID=:omxPlanId ORDER by 1
at org.hibernate.ejb.AbstractEntityManagerImpl.createNamedQuery(AbstractEntityManagerImpl.java:704) [hibernate-entitymanager-4.0.1.Final.jar:4.0.1.Fin
DAO Method
public TermPayment findFirstPaymentByTotalAndPlanId(int planId, double totalAmount) {
TypedQuery<TermPayment> query = entityManager.createNamedQuery("SELECT OMX_PLAN_ID, PLAN_ID,DECODE(plan_id,0,:user_input_total_amount,first_payment) first_paymenmt From (SELECT OMX_PLAN_ID, PLAN_ID,(SELECT DECODE(fraction,0,fixed_payment_amount, (( fraction/100) * :user_input_total_amount)) From TFN.VW_OMX_PAYMENT_PLAN_DETAILS i WHERE o.OMX_PLAN_ID=i.OMX_PLAN_ID AND i.OMX_PLAN_ID=:omxPlanId AND i.PAYMENT_ID=1) first_payment FROM TFN.VW_OMX_PAYMENT_PLANS o ) WHERE OMX_PLAN_ID=:omxPlanId ORDER by 1", TermPayment.class);
query.setParameter("omxPlanId", planId);
query.setParameter("user_input_total_amount", totalAmount);
return query.getSingleResult();
}
Return class
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class TermPayment {
#Id
#Column(name = "OMX_PLAN_ID")
Integer omxPlanId;
#Column(name = "PLAN_ID")
Integer planId;
#Column(name = "FIRST_PAYMENT")
Double firstPayment;
public Integer getOmxPlanId() {
return omxPlanId;
}
public void setOmxPlanId(Integer omxPlanId) {
this.omxPlanId = omxPlanId;
}
public Integer getPlanId() {
return planId;
}
public void setPlanId(Integer planId) {
this.planId = planId;
}
public Double getFirstPayment() {
return firstPayment;
}
public void setFirstPayment(Double firstPayment) {
this.firstPayment = firstPayment;
}
}

From the java.persistence.EntityManager javadoc:
/**
* Create an instance of <code>Query</code> for executing a named query
* (in the Java Persistence query language or in native SQL).
* #param name the name of a query defined in metadata
* #return the new query instance
* #throws IllegalArgumentException if a query has not been
* defined with the given name or if the query string is
* found to be invalid
*/
public Query createNamedQuery(String name);
so you should create your named query and only refer to it using createNamedQuery.

If you want to create a query from a string, you can use the createQuery(String query, Class type) method.
You can replace the method used in your DAO:
entityManager.createNamedQuery(...)
For this one:
entityManager.createQuery("select OMX_PLAN_ID, PLAN_ID ...", TermPayment.class)
Alternatively, you can create a NamedQuery adding a NamedQuery annotation in your Entity class or using xml. Afterthat, you could use the NamedQuery passing the NamedQuery name to the createNamedQuery() method.
#NamedQuery(name="MyQuery", query="select OMX_PLAN_ID, PLAN_ID ...")
entityManager.createNamedQuery(MyQuery, TermPayment.class);

You are trying to use NamedQuery api for creating dynamic queries which is wrong.
From doc
createNamedQuery method is used to create static queries, or queries that are defined in metadata by using the javax.persistence.NamedQuery annotation. The name element of #NamedQuery specifies the name of the query that will be used with the createNamedQuery method. The query element of #NamedQuery is the query:
#NamedQuery(
name="findAllCustomersWithName",
query="SELECT c FROM Customer c WHERE c.name LIKE :custName"
)
Here’s an example of createNamedQuery, which uses the #NamedQuery:
#PersistenceContext
public EntityManager em;
...
customers = em.createNamedQuery("findAllCustomersWithName")
.setParameter("custName", "Smith")
.getResultList();
You need to do something like,
entityManager.createQuery(
"SELECT c FROM Customer c WHERE c.name LIKE :custName")
.setParameter("custName", name)

Related

Variables in Spring Data JPA native query

Using Spring Dat JPA, I need to query my database and return a range of OrderEntitys based on a startAmt and a endAmt of amounts. I'm not sure if I should map these two variables to entity OrderEntity, as fields in some type of separate class/entity/model, or simply declare them in my native query. Perhaps I should be using a service that implements EntityManager.createNativeQuery()?
Would like to do something like :
#Repository
public interface OrderRangeRepository extends JpaRepository<OrderEntity, OrderEntityID> {
#Query(value = "SELECT * FROM Orders WHERE Amount BETWEEN startAmt AND endAmt;" , nativeQuery=true)
List<OrderEntity> findOrdersBy(int startAmt, int endAmt);
}
If I were to use EntityManager.createNativeQuery() in a service, perhaps something like below :
#Service
public class OrderRangeService {
#Autowired
EntityManager entityManager;
public List<OrderEntity> findAmountsBetween() {
List<OrderEntity> amountsBetween = entityManager.createNativeQuery("SELECT * FROM Orders WHERE Amount BETWEEN ?1 AND 2?;")
.setParameter(1, "startAmt")
.setParameter(2, "endAmt")
.getResultList();
return amountsBetween;
}
}
You can achieve this with Spring Data JPA without defining a native query.
#Repository
public interface OrderRangeRepository extends JpaRepository<OrderEntity, OrderEntityID> {
List<OrderEntity> findByAmountBetween(int startAmt, int endAmt);
}
If you want to use the native query change it to
#Query(value = "SELECT * FROM Orders WHERE Amount BETWEEN :startAmt AND :endAmt" , nativeQuery=true)
List<OrderEntity> findOrdersBy(#Param("startAmt") int startAmt, #Param("endAmt") int endAmt);
You can invoke the query in a service by doing
#Service
public class OrderRangeService {
#Autowired
OrderRangeRepository orderRangeRepository ;
public List<OrderEntity> findAmountsBetween(int startAmt, int endAmt) {
List<OrderEntity> amountsBetween = orderRangeRepository.findByAmountBetween(startAmt, endAmt);
return amountsBetween;
}
}
Finally, from your controller, you should autowire the OrderRangeService and invoke the findAmountsBetween service method
#Autowired
OrderRangeService orderRangeService;
#GetMapping("/amountsFromAndTo")
#ResponseBody
public String getAmounts(#RequestParam int startAmt, #RequestParam int endAmt) {
List<OrderEntity> orderEntityL = orderRangeService.findAmountsBetween(startAmt, endAmt);
return orderEntityL.toString();
}
1. Named Parameters
Each parameter annotated with #Param must have a value string matching
the corresponding JPQL or SQL query parameter name. A query with named
parameters is easier to read and is less error-prone in case the query
needs to be refactored.
#Query(value = "SELECT * FROM Orders WHERE Amount BETWEEN :startAmt AND :endAmt;" , nativeQuery=true)
List<OrderEntity> findOrdersBy(#Param("startAmt") int startAmt, #Param("endAmt") int endAmt);
}
2. Indexed Query Parameters
Spring Data will pass method parameters to the query in the same order
they appear in the method declaration
#Query(value = "SELECT * FROM Orders WHERE Amount BETWEEN ?1 AND ?2;" , nativeQuery=true)
List<OrderEntity> findOrdersBy(int startAmt, int endAmt);

Returned object from Spring Data Jpa query has null values

I'm trying to get object of custom type from JPA Repository
VisitRepository.java
#Repository
public interface VisitRepository extends JpaRepository<Visit, Long>, JpaSpecificationExecutor<Visit> {
#Query(value = "select client_id , count(*) from visit where (DATE(jhi_date) between :startDate and :endDate) group by client_id",nativeQuery = true)
List<IIntegerReportData> findByDate(#Param("startDate") String startDate, #Param("endDate") String endDate);
IIntegerReportData.java
package com.mycompany.hiptest.repository;
public interface IIntegerReportData {
Long getId();
Integer getValue();
}
ClientRating.java
public List<ClientsRatingDTO> findAllSorted(String startDate, String endDate, Long fieldNum) {
List<IIntegerReportData> visitReport = visitRepository.findByDate(startDate, endDate);
log.debug("visitReport:" + visitReport.size());
for (IIntegerReportData visit : visitReport
) {
log.debug("value: " + visit.getValue());
}
In debug i get visitReport.size() = 27 (that is correct records count), but
visit.getValue() is NULL for each rows, although there are not null values in this field for each rows.
What's wrong?
You could use NativeQuery Annotation:
Have a look at:
https://www.baeldung.com/spring-data-jpa-query
When returning a custom object from a native query, the result column names must match the names of the custom interface, otherwise they'll just have null values. E.g.:
#Repository
public interface MyRepository extends JpaRepository<MyEntity, Long> {
#Query(value = "SELECT "\"id\" FROM \"my_entity\"", nativeQuery = true)
List<IdNative> findAllIdNative();
interface IdNative {
Long getEntityId();
}
}
Here, getEntityId() will always return null because the result table of the query has no entityId column. To fix, either change the query to match the method:
SELECT "id" AS "entityId" FROM "my_entity"
Or, change the interface method to match the column name:
Long getId();

Hibernate to execute multiple native sql statements

How can I execute Multiple SQL statements in a single sql query using hibernate native sql.
String sql = "SELECT * FROM user; SELECT * FROM product;";
UserVO valueObject = new UserVO();
databaseObject.select(sql, valueObject);
Database Object
public List select(String sql, Object valueObject) throws Exception {
Session session = Entitlement.getSessionFactory().openSession();
session.beginTransaction();
List list = session.createSQLQuery(sql).setProperties(valueObject).list();
session.close();
return list;
}
Use union to form a query which has same returning data
(Select EMPLOYEEID as id, EMPLOYEE_NAME as name, "EMPOYEE" as type) UNION (SELECT PRODUCTID as id, Product_NAME as name, "PRODUCT" as type)
form an Entity to hold it
class EntityDetail {
String id;
String name;
String type;
}
I have added an additional column value type to simply identify from which table row is coming from. And yes you will need to form a proper Entity with all valid annotations the above Entity is just for example.
just a lateral approach.
public List<List<Object[]>> execute(String sqls, Object valueObject) throws Exception {
String[] queries = sqls.split(";");
List<List<Object[]>> result = new ArrayList<>();
for(int i=0; i<queries.length; i++) {
result.add(this.select(queries[i], valueObject));
}
return result;
}

Trying to use SQL Function in hibernate

I am trying to call My-SQL function which is returning calculated value based on input parameters. I am using native queries for this purpose, I am able to get all fields except calculated field. I am always getting NULL as value for calculated field. Moreover If I execute same query in SQL Client, it gives proper result.
Test.class
#NamedNativeQueries({
#NamedNativeQuery(
name = "getTestData",
query = "Select test_id, BillType, Description, Material, Netvalue1, "
+ "NetValue(billType, Netvalue1) as Sales_Rs "
+ "from test1",
resultClass = Test.class
)
})
#Entity
#Table(name="TEST1")
public class Test {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name="TEST_ID")
private Integer test_id;
#Column(name="Billtype")
private String Billtype;
#Column(name="Description")
private String Description;
#Column(name="Material")
private String Material;
#Column(name="Netvalue1")
private Double Netvalue1;
#Transient
private Double Sales_Rs;
// getters and setters
.
.
.
};
Here is how I am calling native query:
#SuppressWarnings("unchecked")
#Override
public List<Test> getAllTestData() throws MyAppException {
List<Test> result;
try {
Session session = sessionFactory.getCurrentSession();
Query query = session.getNamedQuery("getTestData");
result = query.list();
} catch (DataAccessException e) {
throw new MyAppException("Could not add a new record :", e);
}
return result;
}
Here is Stored function:
CREATE FUNCTION NetValue(billType CHAR(30), Netvalue1 DOUBLE) RETURNS double
BEGIN
Declare RetValue DOUBLE;
SET RetValue =
CASE billType
WHEN 'A' THEN 0.10 * Netvalue1
WHEN 'B' THEN 0.05 * Netvalue1
WHEN 'C' THEN 0.025 * Netvalue1
ELSE Netvalue1
END;
RETURN RetValue;
END;
You declared field Double Sales_Rs as transient, therefore it is not handled by Hibernate.
See this thread for two solutions:
a) Use a Hibernate specific mapping to calculate Sales_RS by a formula which calls your database function:
#Formula("NetValue(billType, Netvalue1)")
private Double Sales_Rs;
b) use the JPA #Postload annotation to calculate Sales_RS after the object has been loaded:
#Transient
private Double Sales_Rs;
#PostLoad
private void onLoad() {
Sales_Rs = ... // same logic as in your db function
}
In both cases you can drop the native query and use simple hql:
#NamedQueries({
#NamedQuery(
name = "getTestData",
query = "from Test"
)})

Hibernate SQL Query result Mapping/Convert TO Object/Class/Bean

1 2: select (table.*)/(all column) is OK
String sql = "select t_student.* from t_student";
//String sql = "select t_student.id,t_student.name,... from t_student"; //select all column
SQLQuery query = session.createSQLQuery(sql);
query.addEntity(Student.class);//or query.addEntity("alias", Student.class);
//query.list();[Student#..., Student#..., Student#...]
query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP); //or other transformer
query.list(); //[{Student(or alias)=Student#...},{Student=Student#...}]
3: select some column(not all of), is Error
String sql = "select t_student.id,t_student.name.t_student.sex from t_student";
SQLQuery query = session.createSQLQuery(sql);
query.addEntity(Student.class);
query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
query.list(); //Exception:invalid column/no column
I want "3" to work ok, and let the result can be mapped to Student.class.
Like: Student[id=?, name=?, sex=?, (other field are null/default)]
I've no idea for this error, help me please!
You can go further and add
.setResultTransformer(Transformers.aliasToBean(YOUR_DTO.class));
and automatically map it to your custom dto object, see also Returning non-managed entities.
For example:
public List<MessageExtDto> getMessagesForProfile2(Long userProfileId) {
Query query = getSession().createSQLQuery(" "
+ " select a.*, b.* "
+ " from messageVO AS a "
+ " INNER JOIN ( SELECT max(id) AS id, count(*) AS count FROM messageVO GROUP BY messageConversation_id) as b ON a.id = b.id "
+ " where a.id > 0 "
+ " ")
.addScalar("id", new LongType())
.addScalar("message", new StringType())
......... your mappings
.setResultTransformer(Transformers.aliasToBean(MessageExtDto.class));
List<MessageExtDto> list = query.list();
return list;
}
I want "3" to work ok, and let the result can be mapped to Student.class
That's possible using
Query createNativeQuery(String sqlString, String resultSetMapping)
In the second argument you could tell the name of the result mapping. For example:
1) Let's consider a Student entity, the magic is going to be in the SqlResultSetMapping annotation:
import javax.persistence.Entity;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.Table;
#Entity
#Table(name = "student")
#SqlResultSetMapping(name = "STUDENT_MAPPING", classes = {#ConstructorResult(
targetClass = Student.class, columns = {
#ColumnResult(name = "name"),
#ColumnResult(name = "address")
})})
public class Student implements Serializable {
private String name;
private String address;
/* Constructor for the result mapping; the key is the order of the args*/
public Student(String aName, String anAddress) {
this.name = aName;
this.address = anAddress;
}
// the rest of the entity
}
2) Now you can execute a query which results will be mapped by STUDENT_MAPPING logic:
String query = "SELECT s FROM student s";
String mapping = "STUDENT_MAPPING";
Query query = myEntityManager.createNativeQuery(query, mapping);
#SuppressWarnings("unchecked")
List<Student> students = query.getResultList();
for (Student s : students) {
s.getName(); // ...
}
Note: I think it's not possible to avoid the unchecked warning.
There is only two ways.
You can use 1st or 2nd snippet. According to Hibernate documentation you must prefer 2nd.
You can get just a list of object arrays, like this:
String sql = "select name, sex from t_student";
SQLQuery query = session.createSQLQuery(sql);
query.addScalar("name", StringType.INSTANCE);
query.addScalar("sex", StringType.INSTANCE);
query.list();
I had same problem on HQL Query. I solved the problem by changing the transformer.
The problem caused the code written to transform as Map. But it is not suitable for Alias Bean. You can see the error code at below. The code written to cast result as map and put new field to the map.
Class : org.hibernate.property.access.internal.PropertyAccessMapImpl.SetterImpl
m
Method: set
#Override
#SuppressWarnings("unchecked")
public void set(Object target, Object value, SessionFactoryImplementor factory) {
( (Map) target ).put( propertyName, value );
}
I solved the problem to duplicate the transformer and change the code.
You can see the code in the project.
Link : https://github.com/robeio/robe/blob/DW1.0-migration/robe-hibernate/src/main/java/io/robe/hibernate/criteria/impl/hql/AliasToBeanResultTransformer.java
Class:
import java.lang.reflect.Field;
import java.util.Map;
import io.robe.hibernate.criteria.api.query.SearchQuery;
import org.hibernate.HibernateException;
import org.hibernate.transform.AliasedTupleSubsetResultTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AliasToBeanResultTransformer extends AliasedTupleSubsetResultTransformer {
private static final Logger LOGGER = LoggerFactory.getLogger(AliasToBeanResultTransformer.class);
private final Class resultClass;
// Holds fields of Transform Class as Map. Key is name of field.
private Map<String, Field> fieldMap;
public AliasToBeanResultTransformer(Class resultClass) {
if ( resultClass == null ) {
throw new IllegalArgumentException( "resultClass cannot be null" );
}
fieldMap = SearchQuery.CacheFields.getCachedFields(resultClass);
this.resultClass = resultClass;
}
#Override
public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
return false;
}
#Override
public Object transformTuple(Object[] tuple, String[] aliases) {
Object result;
try {
result = resultClass.newInstance();
for ( int i = 0; i < aliases.length; i++ ) {
String name = aliases[i];
Field field = fieldMap.get(name);
if(field == null) {
LOGGER.error(name + " field not found in " + resultClass.getName() + " class ! ");
continue;
}
field.set(result, tuple[i]);
}
}
catch ( InstantiationException e ) {
throw new HibernateException( "Could not instantiate resultclass: " + resultClass.getName() );
} catch ( IllegalAccessException e ) {
throw new HibernateException( "Could not instantiate resultclass: " + resultClass.getName() );
}
return result;
}
}
After created new Transformer You can use like below.
query.setResultTransformer(new AliasToBeanResultTransformer(YOUR_DTO.class));
You can mapped it automatically:
Your Model Student.java
public class Student {
private String name;
private String address;
}
Repository
String sql = "Select * from student";
Query query = em.createNativeQuery(sql, Student.class);
List ls = query.getResultList();
so it will automatically mapped the result with the Student class

Categories