use like for integer value in namedquery Eclipselink - java

when use namedQuery in entity class get error
#NamedQuery(name = "Classification.search", query = "SELECT c FROM Classification c WHERE c.id LIKE :value")
Method for call namedQuery
public List<Classification> search(String value) {
Query query = getEntityManager().createNamedQuery("Classification.search", Classification.getClass()).setParameter("value", "%"+value+"%");
query.setMaxResults(10);
return query.getResultList();
}
java.lang.IllegalArgumentException: You have attempted to set a value of type class java.lang.String for parameter value with expected type of class java.lang.Integer from query string SELECT c FROM Classification c WHERE c.id LIKE :value.
but when use this method is work without Error.
public List<Classification> findLimited(String _clasif, int maxResult) {
String querySt = "SELECT c FROM Classification c WHERE c.id LIKE '%" + _clasif + "%'";
Query query = em.createQuery(querySt);
query.setMaxResults(maxResult);
List<Classification> classif;
classif = query.getResultList();
if (classif != null) {
return classif;
}
return new ArrayList<>();
}
i use eclipselink 2.6 with JPA

As per the BNF for JPQL, "LIKE" is for use with String values only. Use with non-String values would only be a vendor extension, and hence vendor-dependent. Whether it is part of a named query or criteria or string-based JPQL is irrelevant.
like_expression ::= string_expression [NOT] LIKE pattern_value [ESCAPE escape_character]

i solve that by this code.
public List<Classification> search(String value) {
Query query = getEntityManager().createQuery(getNamedQueryCode(entityClass, "Classification.search").replace(":value", value));
query.setMaxResults(10);
return query.getResultList();
}
this method get namedQuery string from entity class by nameKey and class.
private String getNamedQueryCode(Class<? extends Object> clazz, String namedQueryKey) {
NamedQueries namedQueriesAnnotation = clazz.getAnnotation(NamedQueries.class);
NamedQuery[] namedQueryAnnotations = namedQueriesAnnotation.value();
String code = null;
for (NamedQuery namedQuery : namedQueryAnnotations) {
if (namedQuery.name().equals(namedQueryKey)) {
code = namedQuery.query();
break;
}
}
if (code == null) {
if (clazz.getSuperclass().getAnnotation(MappedSuperclass.class) != null) {
code = getNamedQueryCode(clazz.getSuperclass(), namedQueryKey);
}
}
//if not found
return code;
}

Related

Is there a way to check for null ZonedDateTime in JPQL?

In some E2E tests I'm faced with a problem. Let's say, I have the following JPQL query:
Query query = entityManager.createQuery(
" select d from Document d left join d.audit da " +
" where " +
" (cast(:startDate as java.time.ZonedDateTime)) is null " +
" or truncate_for_minutes(da.dateCreate, 'UTC') >= " +
" truncate_for_minutes(:startDate, 'UTC')")
.setParameter("startDate", ZonedDateTime.now());
In the query string I use named parameter startDate which can be null. The query above works. But if I pass null, the following exception is thrown:
Caused by: org.postgresql.util.PSQLException:
ERROR: cannot cast type bytea to timestamp without time zone
Without type casting the following exception is thrown:
Caused by: org.postgresql.util.PSQLException:
ERROR: could not determine data type of parameter $1
Without check for null the following exception is thrown:
Caused by: org.postgresql.util.PSQLException:
ERROR: function pg_catalog.timezone(unknown, bytea) does not exist
No function matches the given name and argument types.
You might need to add explicit type casts.
I use this query in Spring Data repository by using #Query string. Function truncate_for_minute(...) - is just a small customization for the PostgreSQL function date_trunc(...).
I know that I can implement custom repository and build query string dynamically, but why I can't check for null ZonedDateTime in JPQL string? Maybe there is a way to do it?
My environment:
Java 11
PostgreSQL 11.3 on x86_64-pc-linux-musl, compiled by gcc (Alpine 8.3.0) 8.3.0, 64-bit
Hibernate 5.3.7 Final
Another solution is to use Criteria API and build the query dynamically.
#Repository
#RequiredArgsConstructor
public class CustomDocumentRepositoryImpl implements CustomDocumentRegistry {
private final EntityManager em;
#Override
public Page<Document> findDocumentsForExpertByFilter(SearchDocumentCriteria criteria,
Pageable pageable) {
final String AUDIT_TABLE = "...";
final String USER_TABLE = "...";
final String ID_FIELD = "id";
final String FIRST_NAME_FIELD = "...";
final String LAST_NAME_FIELD = "...";
final String MIDDLE_NAME_FIELD = "...";
final String WORKSTATION_FIELD = "...";
final String DATE_CREATE_FIELD = "...";
final String LIKE_MASK = "%%%s%%";
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Document> query = cb.createQuery(Document.class);
Root<Document> root = query.from(Document.class);
Path<ZonedDateTime> dateCreatePath =
root.get(AUDIT_TABLE).get(DATE_CREATE_FIELD);
Path<String> lastNamePath =
root.get(AUDIT_TABLE).get(USER_TABLE).get(LAST_NAME_FIELD);
Path<String> firstNamePath =
root.get(AUDIT_TABLE).get(USER_TABLE).get(FIRST_NAME_FIELD);
Path<String> middleNamePath =
root.get(AUDIT_TABLE).get(USER_TABLE).get(MIDDLE_NAME_FIELD);
root.fetch(AUDIT_TABLE, JoinType.LEFT)
.fetch(USER_TABLE, JoinType.LEFT);
Predicate documentIdsPredicate;
List<Long> documentIds = criteria.getIds();
if (isNull(documentIds) || documentIds.isEmpty()) {
documentIdsPredicate = cb.isNotNull(root.get(ID_FIELD));
} else {
documentIdsPredicate = root.get(ID_FIELD).in(criteria.getIds());
}
Predicate startDatePredicate;
ZonedDateTime startDate = criteria.getStartDate();
if (isNull(startDate)) {
startDatePredicate = cb.isNotNull(dateCreatePath);
} else {
startDatePredicate = cb.greaterThanOrEqualTo(dateCreatePath, startDate);
}
Predicate endDatePredicate;
ZonedDateTime endDate = criteria.getEndDate();
if (isNull(endDate)) {
endDatePredicate = cb.isNotNull(dateCreatePath);
} else {
endDatePredicate = cb.lessThanOrEqualTo(dateCreatePath, endDate);
}
Predicate lastNamePredicate = cb.like(cb.upper(lastNamePath),
format(LIKE_MASK, criteria.getLastName().toUpperCase()));
Predicate firstNamePredicate = cb.like(cb.upper(firstNamePath),
format(LIKE_MASK, criteria.getFirstName().toUpperCase()));
Predicate middleNamePredicate = cb.like(cb.upper(middleNamePath),
format(LIKE_MASK, criteria.getMiddleName().toUpperCase()));
Predicate fullNamePredicate =
cb.and(lastNamePredicate, firstNamePredicate, middleNamePredicate);
Predicate compositePredicate = cb.and(
fullNamePredicate,
documentIdsPredicate,
startDatePredicate,
endDatePredicate
);
query.where(compositePredicate);
Query limitedQuery = em.createQuery(query
.orderBy(cb.desc(root.get(AUDIT_TABLE).get(DATE_CREATE_FIELD))))
.setFirstResult(nonNull(criteria.getSize()) ?
criteria.getPage() * criteria.getSize() :
criteria.getPage());
if (nonNull(criteria.getSize())) {
limitedQuery.setMaxResults(criteria.getSize());
}
List<Document> documents = limitedQuery.getResultList();
return new PageImpl<>(documents, pageable, criteria.getSize());
}
}
Generates the following SQL:
select
document0_.id as id1_3_0_,
user1_.id as id1_13_1_,
document0_.created_dt as created_2_3_0_,
document0_.updated_dt as updated_3_3_0_,
document0_.created_user_id as created_6_3_0_,
document0_.updated_user_id as updated_7_3_0_,
document0_.name as name4_3_0_,
user1_.first_name as first_na2_13_1_,
user1_.last_name as last_nam3_13_1_,
user1_.middle_name as middle_n5_13_1_
from
some_scheme.document document0_
left outer join
some_scheme.s_user user1_
on document0_.created_user_id=user1_.id cross
join
some_scheme.s_user user2_
where
document0_.created_user_id=user2_.id
and (
upper(user2_.last_name) like '%LASTNAME%'
)
and (
upper(user2_.first_name) like '%FIRSTNAME%'
)
and (
upper(user2_.middle_name) like '%MIDDLENAME%'
)
and (
document0_.id in (
2 , 1
)
)
and document0_.created_dt>=...
and document0_.created_dt<=...
order by
document0_.created_dt desc limit 10;
In my case, the problem was as follows. I registered customization of SQL function date_trunc:
public class CustomSqlFunction implements MetadataBuilderContributor {
#Override
public void contribute(MetadataBuilder metadataBuilder) {
metadataBuilder.applySqlFunction(
"truncate_for_minutes",
new SQLFunctionTemplate(
StandardBasicTypes.TIMESTAMP,
"date_trunc('minute', (?1 AT TIME ZONE ?2))"
)
);
}
}
If change the StandardBasicTypes.TIMESTAMP to ZonedDateTime.INSTANCE and pass ZonedDateTime in one parameter, then the comparison in the JPQL query does not cause errors:
public class CustomSqlFunction implements MetadataBuilderContributor {
#Override
public void contribute(MetadataBuilder metadataBuilder) {
metadataBuilder.applySqlFunction(
"truncate_for_minutes",
new SQLFunctionTemplate(
ZonedDateTimeType.INSTANCE,
"date_trunc('minute', ?1)"
)
);
}
}

How can i add column names to the ResultSet. I use Java with the EntityManager

Is it possible to read out the columnnames?
My Application: Java, SpringBoot, JPA, Hibernate
My code:
Query query = entityManager.createNativeQuery(
"select name as Name, concat('Time:',time, ' AND:', event) as FOO
from XYZ
where NAME = 'BLA'");
List<Object[]> resultList = query.getResultList();
I need the columnnames (Name and FOO) in the first Object[] of the ResultSet or
somewhere else...
My unsightly solution:
private String getColumnNames(String sql) {
StringBuilder sb = new StringBuilder();
String part1 = sql.split("from")[0];
part1 = part1.replaceAll("\\(.*?\\)", "");
String[] columns = part1.split(",");
for (String column : columns) {
String alias = column;
if (column.toUpperCase().contains(" AS ")) {
alias = column.toUpperCase().split(" AS ")[1];
}
alias = alias.replace("'","");
sb.append(alias+",");
}
String columnNames = sb.toString();
if (columnNames.length() != 0) {
columnNames = columnNames.substring(0, columnNames.length()-1);
}
return columnNames;
}
Thanks
You should use other overloaded versions of createNativeQuery() :
public Query createNativeQuery(String sqlString, Class resultClass);
or
public Query createNativeQuery(String sqlString, String resultSetMapping);
In this way, you could map the result of the query to a class that contains name and foo fields.
For example with the first way :
Query query = entityManager.createNativeQuery(
"select name as Name, concat('Time:',time, ' AND:', event) as FOO
from XYZ
where NAME = 'BLA'", Xyz.class);
List<Xyz> resultList = query.getResultList();
Note that query.getResultList() will return a raw type.
What you should do here is to:
First create an entity with Name and FOO attributes, so you can map it as a result of query.getResultList().
Then map it with a List<YourEntity> instead of List<Object[].
And Last thing is to define a mapper for the request so it can map those results to the desired entity, if the query uses different attributes names other than the ones in the entity, using #SqlResultSetMapping.
You can check this Hibernate Tips: How to map native query results to entities tutorial for further details.
Edit:
If you can't create a new Entity to map the results, you can map those results in a HashMap in each element of the Object[] like this:
Map results = new HashMap();
results.put("Name", obj[0]);
results.put("Foo", obj[1]);
Yes it is possible. Lets assume your entity name is XYZ and you Repository would be XYZRepository, Lets create a DTO which only contains name and eventDetais since we want to extract only these 2 details from table.
public class XyzDTO {
String name;
String eventDetails;
public XyzDTO() {
}
public XyzDTO(String name, String eventDetails) {
this.name = name;
this.eventDetails =eventDetails;
}
}
This code would be written in XYZRepository.
#Query(nativeQuery = true)
List<Details> getList();
This code would be written in XYZ entity.
#SqlResultSetMapping(name = "getList",
classes = #ConstructorResult(targetClass = XyzDTO.class,
columns = {#ColumnResult(name = "name", type = String.class),
#ColumnResult(name = "eventDetails", type = String.class)}))
#NamedNativeQuery(name = "XYZ.getList",
query = "select name as name, concat('Time:',time, ' AND:', event) as eventDetails from XYZ where name = 'BL")
public class XYZ implements Serializable {
}

java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to className

Code:
public void getDetails() {
try {
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
String hql = "select c.mobile, c.password FROM CrbtSubMasterDemo c where rownum<20";
Query query = session.createQuery(hql);
List<CrbtSubMasterDemo> itr = query.list();
session.getTransaction().commit();
for (CrbtSubMasterDemo pojo : itr) {//excepion line
System.out.println("[" + pojo.getMobile() + "]");
}
} catch (Exception e) {
e.printStackTrace();
}
}
CrbtSubMasterDemo is pojo mapped with the db.
When I try to run it, it gives following Exception:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to com.telemune.demoPojo.CrbtSubMasterDemo
at com.telemune.demoHibernate.QueryTester.getDetails(QueryTester.java:57)
at com.telemune.demoHibernate.QueryTester.main(QueryTester.java:23)
The question is query.list() is returning the list of objects of pojo class. Then why is this Exception. I am new to Hibernate, sorry if its a silly question.
When you write this:
String hql = "select c.mobile, c.password FROM CrbtSubMasterDemo c where rownum<20";
Your result set is not a List of CrbtSubMasterDemo
Try to write:
String hql = "select FROM CrbtSubMasterDemo c where rownum<20";
Another way is define a new constructor of CrbtSubMasterDemo where you pass only two fields c.mobile, c.password
so your query becomes:
String hql = "select new " + CrbtSubMasterDemo.class.getName() + "(c.mobile, c.password) FROM CrbtSubMasterDemo c where rownum<20";
If you follow this solution, remeber to add a default constructor too (without parameters), so in your pojo you have:
public CrbtSubMasterDemo(String mobile, String password) {
this.mobile = mobile;
this.password = password
}
and
public CrbtSubMasterDemo() {
}
String hql = "select c.mobile, c.password FROM CrbtSubMasterDemo c where rownum<20";
Query query = session.createQuery(hql);
Result of this will be List<Object[]>
List<Object[]> itr = query.list();
for (Object[] row : itr) {
System.out.println(String.format("mobile:%s, password:%s", row[0], row[1]));
}
if mobile and password are strings, of course. You can use a transformer to transform results directly to CrbtSubMasterDemo.
Hibernate 3.2: Transformers for HQL and SQL
FluentHibernateResultTransformer
Sir, Many times user faces this kinda requirements . Hibernate has ResultTransformer to convert a hql/sql in Object.
public CrbtSubMasterDemo{
private Stirng mobile;
private String password;
public CrbtSubMasterDemo(){
--------------
}
#####after setting the transation set whichever columns you are selecting should be given as name of property of your object
String hql = "select c.mobile as mobile, c.password as password FROM CrbtSubMasterDemo c where rownum<20";
Query query = session.createQuery(hql);
List<CrbtSubMasterDemo> itr = query.setResultTransformer(Transformers.aliasToBean(CrbtSubMasterDemo.class) ).list();
##No need to commit the transaction.
}
It will convert you query into the CrbtSubMasterDemo
Do not directly cast the result of "query.list();" to List of CrbtSubMasterDemo. As query.list() return object list. Iterate over the object list received and cast one by one to put in list List of CrbtSubMasterDemo

SQL statement conversion to criteria (Hibernate )

I have an SQL query that I would like to convert into a criteria (Hibernate 3.3.2) to use it into my persistancy layer. That's the query :
SELECT TYPE_CHANTIER.ID,
TYPE_CHANTIER.CODE,
TYPE_CHANTIER.LIBELLE
FROM TYPE_CHANTIER
INNER JOIN CATEGORIE_CHANTIER
ON TYPE_CHANTIER.ID = CATEGORIE_CHANTIER.TYPE
INNER JOIN CHANTIER
ON CATEGORIE_CHANTIER.ID = CHANTIER.CATEGORIE
WHERE CHANTIER.ID = 26869423;
I already try a lot a combinations, but there is no result that would be good for me ... This is my code :
public TypeChantier recupererTypeChantier(Integer idChantier) {
TypeChantier retour = null;
if (idChantier != null) {
Criteria criteria = getSession().createCriteria(TypeChantier.class);
// requete
criteria.createAlias("categorieChantiers", "categorieChantiers", JoinType.INNER_JOIN);
criteria.createAlias("categorieChantiers.chantiers", "chantiers", JoinType.INNER_JOIN);
// requete
criteria.add(Restrictions.eq("chantiers.id", idChantier));
// resultat
retour = (TypeChantier) criteria.uniqueResult();
}
return retour;
}
this code return a java null pointer exception

Filtering results in a JPQL query

I am trying to make "filter" search for all questions in my database. Now I get a exception telling me that I can't compare enum values with string. Is it because I don't use the fully qualified package name of wher the enum type is declared? If so, is it better ways than hard-coding the package name?
Exception Description: Error compiling the query [SELECT q FROM
Question q WHERE q.status = 'APPROVED'], line 1, column 40: invalid
enum equal expression, cannot compare enum value of type
[app.utility.Status} with a non enum value of type
[java.lang.String].
public List<Question> all(Status status, ViewOption viewOption) {
String jpql = "SELECT q FROM Question q ";
boolean isWhereClauseAdded = false;
if (status != Status.ALL) {
if (!isWhereClauseAdded) {
jpql += "WHERE ";
}
jpql += "q.status = '" + status + "'";
}
if (viewOption != ViewOption.ALL) {
if (!isWhereClauseAdded) {
jpql += "WHERE ";
}
// Check if 'AND' operator is needed.
if (status != Status.ALL) {
jpql += " AND ";
}
switch (viewOption) {
case ONLY_IMAGES:
jpql += "q.image != ''";
break;
case NO_IMAGES:
jpql += "q.image = '' ";
break;
}
}
TypedQuery<Question> query = entityManager.createQuery(jpql,
Question.class);
return query.getResultList();
}
The right thing to do would be to use a query parameter:
String jpql = "select ... where q.status = :status";
Query query = em.createQuery(jpql).setParameter("status", status);
Rather than creating your query dynamically be concatenating query parts, you should also use the Criteria API, which has been designed with this goal in mind.
Can you try changing:
jpql += "q.status = '" + status + "'";
To:
jpql += "q.status = app.utility.Status." + status;

Categories