Hibernate HQL Map query - java

i have a class:
#Entity
#Table(name = "days")
public class DayEntry {
#Expose
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
.
.
#Expose
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "day_item_hours")
#MapKeyJoinColumn(name = "item_id")
#Column(name = "hours")
private Map<Item, Double> itemsHours;
.
.
}
I need to get from DB all DayEntries which contain specific Item in itemHours field.
I tried:
private final String FIND_BY_ACTIVITY = "from DayEntry d where :activity in index(d.itemsHours)";
#Override
public Collection<DayEntry> findByActivity(Item activity) {
Query query = entityManager.createQuery(FIND_BY_ACTIVITY);
query.setParameter("activity", activity);
return query.getResultList();
}
And get this:
java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: ( near line 1, column 75 [from ua.siemens.dbtool.model.timesheet.DayEntry d where :activity in index(d.itemsHours)]] with root cause
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: ( near line 1, column 75 [from ua.siemens.dbtool.model.timesheet.DayEntry d where :activity in index(d.itemsHours)]
Not sure how to handle this.
Thanks!
UPDATE
For the moment i ended up with plain SQL Query:
private final String FIND_BY_ACTIVITY = "SELECT * FROM days where id = (SELECT DayEntry_id from day_item_hours where day_item_hours.item_id = :activityId)";
#Override
public Collection<DayEntry> findByActivity(Item activity) {
Query query = entityManager.createNativeQuery(FIND_BY_ACTIVITY_ID, DayEntry.class);
query.setParameter("activityId", activity.getId());
return query.getResultList();
}
but still i wonder how to do it with HQL.

Related

Map values returned from Hibernate Native Query to a model

I am quite new to Hibernate. I am using it along-with my Spring Boot project. I want to write native SQL Queries due to the complex joins involved.
On execution of the queries the values returned is in the form of a list and there is no simple way to map them to a HashMap.
Right now I am using the below approach which works but is heavily dependent on the sequence of the values returned from the query list.
Is there any simple and efficient way to map this result.
DAO
#Transactional
public List<Object> getAllUsers() {
Session currentSession = entityManager.unwrap(Session.class);
String queryString = "select ts.id as id, ts.remarks, concat(s.first_name, ' ',s.last_name) as studentName,\n" +
"ts.start_date as startDate, ts.end_date as endDate, ts.status,\n" +
"ts.created_at as createdAt, ts.created_by as createdBy, \n" +
"ts.updated_at as updatedAt, ts.updated_by as updatedBy\n" +
"from TRN_STATUS ts join types lt on ts.type_id = lt.id join users s on s.id = ts.id \n" +
"join category ct on ts.cat_id = ct.id\n" +
"where ts.tenant_id = 1";
NativeQuery query = currentSession.createNativeQuery(queryString);
List<Object> result = (List<Object>) query.getResultList();
currentSession.close();
return result;
}
Service
public List<Map<String, Object>> getCount(Optional<String> userId, String userType, String limit, String offset) {
List<Object> users= userDAO. getAllUsers();
List<Map<String, Object>> userList = new ArrayList<>();
Iterator userItr = users.iterator();
ObjectMapper userMapper = new ObjectMapper();
Map<String, Object> map;
UserModel obj = new UserModel();
while(userItr.hasNext()) {
Object[] resobj = (Object[]) leaveItr.next();
obj.setId(String.valueOf(resobj[0]));
obj.setStartDate(String.valueOf(resobj[1]));
obj.setEndDate(String.valueOf(resobj[2]));
obj.setDescription(String.valueOf(resobj[3]));
obj.setLeaveType(String.valueOf(resobj[4]));
obj.setCategoryId(String.valueOf(resobj[5]));
obj.setCategoryName(String.valueOf(resobj[6]));
obj.setStatus(String.valueOf(resobj[7]));
obj.setDesignation(String.valueOf(resobj[8]));
obj.setActionBy(String.valueOf(resobj[9]));
obj.setStudentName(String.valueOf(resobj[10]));
obj.setImgUrl(String.valueOf(resobj[11]));
map = userMapper.convertValue(obj, Map.class);
userList.add(map);
logger.info("Value displayed was: "+ map);
}
return userList;
}
UserModel
import lombok.Data;
public #Data class UserModel {
private String id;
private String startDate;
private String endDate;
private String description;
private String leaveType;
private String categoryId;
private String categoryName;
private String status;
private String designation;
private String actionBy;
private String studentName;
private String imgUrl;
private String createdDate;
private String hostelName;
private String blockName;
private String roomName;
}
There is way simple and efficient way to map this result
Instead of creating a method and then mapping the required returned value to a bean class, Just create a Entity class that contains all the fields mapped with the fields which are returned in the query .
Use #NamedNativeQuery for providing the query and #SqlResultSetMapping for mapping the fields which are to be returned after executing the query.
For example
#NamedNativeQuery(
name = "getTenders",
query = "SELECT t.id, t.created_at, CONCAT(u.first_name, ' ' , u.last_name) as created_by, t.modified_at, CONCAT(u2.first_name, ' ' , u2.last_name) as modified_by, t.tenders_abbreviation, t.tenders_full_name FROM abc.tenders v\n" +
"LEFT JOIN abc.users u on u.id = v.created_by LEFT JOIN rbac.users u2 on u2.id = v.modified_by",
resultSetMapping = "tendersMappings"
)
#SqlResultSetMapping(name = "tendersMappings", entities = {
#EntityResult(entityClass = TendersEntity.class, fields = {
#FieldResult(name = "id", column = "id"),
#FieldResult(name = "created_at", column = "created_at"),
#FieldResult(name = "modified_at", column = "modified_at"),
#FieldResult(name = "tenders_abbreviation", column = "tenders_abbreviation"),
#FieldResult(name = "tenders_full_name", column = "tenders_full_name"),
#FieldResult(name = "created_by", column = "created_by"),
#FieldResult(name = "modified_by", column = "modified_by")
})
})
#Entity
#Table(name = "tenders", schema = "abc")
public class TendersEntity {
private static final long serialVersionUID = 1L;
#Id
private Integer id;
#Column(name = "created_at")
private String created_at;
#Column(name = "created_by")
private String created_by;
#Column(name = "modified_at")
private String modified_at;
#Column(name = "modified_by")
private String modified_by;
#Column(name = "tenders_abbreviation")
private String tenders_abbreviation;
#Column(name = "tenders_full_name")
private String tenders_full_name;
public TendersEntity() {}
// setters and getters
}
//DAO class
#Repository
#Transactional
public class TendersDaoImpl implements TendersDao {
#Autowired
EntityManager manager;
#Override
public List<TendersEntity> getVendors() {
List<TendersEntity> dataList = new ArrayList<>();
Query query = manager.createNamedQuery("getTenders");
try {
dataList.addAll(query.getResultList());
} catch (Exception ex) {
…….// exception code
}
return dataList;
}
}
You can use SQL Transformers for this:
With native sql returning non-entity beans or Map's is often more useful instead of basic Object[]. With result transformers that is possible.
Example:
List resultWithAliasedBean = s.createSQLQuery(
"SELECT st.name as studentName, co.description as courseDescription " +
"FROM Enrolment e " +
"INNER JOIN Student st on e.studentId=st.studentId " +
"INNER JOIN Course co on e.courseCode=co.courseCode")
.addScalar("studentName")
.addScalar("courseDescription")
.setResultTransformer( Transformers.aliasToBean(StudentDTO.class))
.list();
StudentDTO dto =(StudentDTO) resultWithAliasedBean.get(0);
Reference:
https://docs.jboss.org/hibernate/core/3.3/reference/en/html/querysql.html#d0e13904

JPA Named query with INNER JOIN condition is failing

I'm trying to create a Named query with the inner join condition in the Entity class.
However the application is failing to start with an exception.
Entity:
#Entity
#Table(name = "fooentry")
#NamedQuery(name="query1", query="SELECT foo.id, foo.name, foo.type, foo.action, foo.createdDateTime FROM FooEntity foo " +
"INNER JOIN (SELECT name, type, MAX(createdDateTime) AS recenttime FROM FooEntity GROUP BY name, type) recententry " +
"ON (foo.name = recententry.name AND foo.type = recententry.type) AND createdDateTime = recenttime AND isExported = false",
lockMode=PESSIMISTIC_WRITE)
public class FooEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "name")
#NotNull
private String msisdn;
#Column(name = "type")
#NotNull
private String iccid;
#Column(name = "action")
#NotNull
private Long inventoryActionId;
#NotNull
#Column(name = "is_exported")
private Boolean isExported;
#Column(name = "created_date_time")
#NotNull
private LocalDateTime createdDateTime;
//setter and getter
}
Exception:
Application is failing to start with the following exception.
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.HibernateException: Errors in named queries:
query1 failed because of: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: ( near line 1, column 161
You can try query like below. It should return expected result
SELECT foo.id, foo.name, foo.type, foo.action, foo.createdDateTime
FROM FooEntity foo
WHERE
foo.isExported = false AND
EXISTS(
SELECT recententry.name, recententry.type, MAX(recententry.createdDateTime) AS recenttime
FROM FooEntity recententry
WHERE recententry.name = foo.name AND recententry.type = foo.type
GROUP BY recententry.name, recententry.type
HAVING recententry.recenttime = foo.createdDateTime
)

ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper [http-nio-8080-exec-3] (logExceptions:131) [] - The column name is not valid

trying to run a native query however I'm getting The column name sb_modelid is not valid. when attempting to map the return object to my model object in java? I have verified all the column names are correct.
1) Why is it referring to my column name as sb_modelid and not sbModelID?
2) Why is not being mapped to my POJO correctly?
Thanks!
Model Object:
package com.dish.wfm.linkingTickets.model.repository;
public class Model {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "sbModelID")
private Long sbModelId;
#Column(name = "modelID")
private String modelID;
#Column(name = "serialNumber")
private String serialNumber;
#Column(name = "serviceContractNumber")
private String serviceContractNumber;
}
Repo:
#Repository
public interface SBModelRepo extends JpaRepository<Model, Long> {
#Query(value = "select m.sbModelID, m.modelID, m.serialNumber, m.serviceContractNumber from sb.Model m where m.modelID = ?1 and m.serialNumber = ?2", nativeQuery = true)
Model findTopByModelIDAndSerialNumber(String modelID, String serialNumber);
}

Hibernate/JPA JPQL to wrong SQL when querying Map<String,String> field

This is my Entity configuration
#Entity
#NamedQuery(name = "Payment.findByEmail", query = "SELECT p FROM Payment p JOIN p.additionalAuthData a " +
"WHERE KEY(a) = 'email' AND VALUE(a) = ?1 AND (p.paymentType = 4 OR p.paymentType = 10)")
public class Payment {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#Column(name = "payment_type")
private Integer paymentType;
/** other properties, getters and setters */
#ElementCollection
#CollectionTable(name = "additional_auth_data")
#MapKeyJoinColumn(name = "id", referencedColumnName = "id")
#MapKeyColumn(name = "field")
#Column(name = "data_value")
private Map<String, String> additionalAuthData;
}
The NamedQuery findByEmail("test#example.com") generates the following SQL
select -- all fields ...
from payment payment0_ inner join additional_auth_data additional1_ on payment0_.id=additional1_.id
where
additional1_.field='email' and (select additional1_.data_value from additional_auth_data additional1_ where payment0_.id=additional1_.id)='test#example.com' and (payment0_.payment_type=4 or payment0_.payment_type=10)
which is wrong: it may work if you have only one row but it blows up otherwise. H2 complains Scalar subquery contains more than one row and PostgreSQL more than one row returned by a subquery used as an expression. In fact, query's where condition compares a scalar value ('test#example.com') with a subquery.
The correct SQL should be:
select -- all fields
from payment payment0_ inner join additional_auth_data additional1_ on payment0_.id=additional1_.id
where additional1_.field='payerEmail' and additional1_.data_value='test#example.com' and (payment0_.payment_type=4 or payment0_.payment_type=10)
Is the HSQL correct? Is there a way to instruct Hibernate to generates a clever, better SQL? Is this a Hibernate bug?
Note: Hibernate shipped with Spring Boot Starter 1.3.7.RELEASE
Edit:
Using an #Embeddable class
#ElementCollection
#JoinTable(name = "additional_auth_data", joinColumns = #JoinColumn(name = "id"))
#MapKeyColumn(name = "field")
#Column(name = "data_value")
private Set<AdditionalData> additionalAuthData;
#Embeddable
public static class AdditionalData {
#Column(name = "field", nullable = false)
private String field;
#Column(name = "data_value")
private String dataValue;
protected AdditionalData() {
}
public AdditionalData(String field, String dataValue) {
this.field = field;
this.dataValue = dataValue;
}
/** Getters, setters; equals and hashCode on "field" */
}
#NamedQuery(name = "Payment.findByEmail", query = "SELECT p FROM Payment p JOIN p.additionalAuthData a " +
"WHERE a.field = 'email' AND a.dataValue = ?1 AND (p.paymentType = 4 OR p.paymentType = 10)")
solves the problem, and the SQL is correct, but it looks just plain wrong, like shooting a fly with a bazooka...
It generates correct SQL without value().
Use just a=?1
But I would expect is should generate it simple also with it.

JPA/Hibernate Native Queries do not recognize Parameters with EmbeddedId

I am using Hibernate/JPA to execute native MySql queries. I want to get the value of the counter.
For example the following query crash:
"SELECT sizeList FROM myCounter WHERE myColA=?1 AND myColB=?2 AND LIMIT 1"
Error:
java.lang.IllegalArgumentException: Parameter with that name [1] did not exist
Sources:
Method:
#Override
public Long getSizeList(String myColA, String myColB) {
Query q = em.createNativeQuery("MyCounter.getSizeList");
q.setParameter(1, myColA);
q.setParameter(2, myColB);
return (Long) q.getSingleResult();
}
Entity:
#Entity
#Table(name = "myCounter")
#XmlRootElement
#NamedNativeQueries({
#NamedNativeQuery(name = "MyCounter.getSizeList", query = "SELECT sizeList FROM myCounter WHERE myColA=?1 AND myColB=?2 AND LIMIT 1", resultClass = MyCounter.class)})
public class MyCounter implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected MyCounterPK myCounterPK;
#Column(name = "sizeList")
private BigInteger sizeList;
public PostListCounterPTTESLTTT() {
}
...
public BigInteger getSizeList() {
return sizeList;
}
}
Embeddable:
#Embeddable
public class MyCounterPK implements Serializable {
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 255)
#Column(name = "myColA")
private String myColA;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 50)
#Column(name = "myColB")
private String myColB;
public MyCounterPK() {
}
...
}
Does anyone know how to use setParameter() in this case ?
I tried with:
//case 1:
"SELECT sizeList FROM myCounter WHERE myColA=?1 AND myColB=?2 AND LIMIT 1"
q.setParameter(1, myColA); //the same error
//case 2:
"SELECT sizeList FROM myCounter WHERE myColA=?1 AND myColB=?2 AND LIMIT 1"
q.setParameter("1", myColA); //the same error
//case 3:
"SELECT sizeList FROM myCounter WHERE myColA=:myColA AND myColB=:myColB AND LIMIT 1"
q.setParameter("myColA", myColA); //the same error
Instead of
Query q = em.createNativeQuery("MyCounter.getSizeList");
Try this:
Query q = em.createNamedQuery("MyCounter.getSizeList");

Categories

Resources