Using Like Expression for Date Field using JPA 2.1 - java

Am trying to perform a query with LIKE expression on a date field using a named query as shown below ;
public Users getResultsByDate(Date regDate){
List<Users> dbUsers = new ArrayList<Users>();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
Query searchQuery = getEntityManager().createNamedQuery("getUserByRegDate");
searchQuery.setParameter("regDate", "%"+dateFormat.format(regDate)+"%");
try{
dbUsers = searchQuery.getResultList();
}
catch(Exception ex){
dbUsers = new ArrayList<Users>();
}
return dbUsers;
}
The name query in corresponding class is;
#NamedQuery(name = "getUserByRegDate", query = "SELECT user FROM Users user WHERE user.regDate LIKE :regDate"),
The query was working using previous JPA 2.0 version. I get the following error when i execute the same when running on JPA 2.1;
You have attempted to set a value of type class java.lang.String for
parameter :regDate with expected type of class java.util.Date ...
How can i resolve this when running on JPA 2.1 ? It's Working OK in JPA 2.0.

JPA spec 4.6.10
The syntax for the use of the comparison operator [NOT] LIKE in a
conditional expression is as follows:
like_expression ::=
string_expression [NOT] LIKE pattern_value [ESCAPE escape_character]
The string_expression must have a string value. The pattern_value is a
string literal or a string-valued input parameter ...
So no, it does not "work" in JPA 2.0; it is not part of ANY JPA spec to use Date in LIKE expressions. It may have just worked by accident because some JPA provider (which one?) didn't do a check and enforce the JPA spec ...

Try with a native named query, transforming user.reg_date to string and then comparing it with the operator like to the parameter. In case of an Oracle DB it would be for example:
#NamedNativeQuery(name = "getUserByRegDate", query =
"SELECT user FROM Users user WHERE TO_CHAR(user.reg_date, 'DD-MON-YYYY') LIKE :regDate"),

Key is to use DB functions 'TO_CHAR' (ORACLE) or MySQL function 'date_format' then user FUNCTION IN #NamedQuery as shown below ;
ORACLE DB
#NamedQuery(name = "getUserByRegDate", query = "SELECT user FROM Users user WHERE FUNCTION('TO_CHAR',user.reg_date,'yyyy-MM-dd') LIKE :regDate")
MySQL
Change 'TO_CHAR' above to 'date_format'
#NamedQuery(name = "getUserByRegDate", query = "SELECT user FROM Users user WHERE FUNCTION('date_format',user.reg_date,'%Y-%m-%d') LIKE :regDate")
Also see http://www.w3schools.com/sql/func_date_format.asp for more info on the DATE_FORMAT MySQL Function.
Thanks.

Related

Pass the dynamically created query (according to condition) in #Query annotation

I am using the #Query annotation to execute the query in spring repository.
But I want to change the some part or make a new query according to the condition and pass in the #Query("pass here the query according to condition")
This is my query
#Query("SELECT ds.symptom FROM DoctorSymptomsModel ds where ds.doctorId = :doctorId and ds.isMostUsed = :isMostUsed)
If some condition satisfy then concat the "ORDER BY createdDate" part in query.
Or
Can I make the variable and set the query in that variable and set like that
String query = SELECT ds.symptom FROM DoctorSymptomsModel ds where
ds.doctorId = :doctorId and ds.isMostUsed = :isMostUsed
if(result){
query = SELECT ds.symptom FROM DoctorSymptomsModel ds where ds.doctorId =
:doctorId and ds.isMostUsed = :isMostUsed ORDER BY createdDate
}
//pass the query variable here
#Query(query)
List<String> findDoctorSymptomsModelList(#Param("doctorId") long doctorId,
#Param("isMostUsed") boolean isMostUsed);
To make a dynamic query, you should think about CriteriaQuery. Take a look at this link for brief introduction.

Ebean query using setDistinct() does not work

I'm using an ebean query in the play! framework to find a list of records based on a distinct column. It seems like a pretty simple query but the problem is the ebean method setDistinct(true) isn't actually setting the query to distinct.
My query is:
List<Song> allSongs = Song.find.select("artistName").setDistinct(true).findList();
In my results I get duplicate artist names.
From what I've seen I believe this is the correct syntax but I could be wrong. I'd appreciate any help. Thank you.
I just faced the same issue out of the blue and can not figure it out. As hfs said its been fixed in a later version but if you are stuck for a while you can use
findSet()
So in your example use
List<Song> allSongs = Song.find.select("artistName").setDistinct(true).findSet();
According to issue #158: Add support for using setDistinct (by excluding id property from generated sql) on the Ebean bug tracker, the problem is that an ID column is added to the beginning of the select query implicitly. That makes the distinct keyword act on the ID column, which will always be distinct.
This is supposed to be fixed in Ebean 4.1.2.
As an alternative you can use a native SQL query (SqlQuery).
The mechanism is described here:
https://ebean-orm.github.io/apidocs/com/avaje/ebean/SqlQuery.html
This is from the documentation:
public interface SqlQuery
extends Serializable
Query object for performing native SQL queries that return SqlRow's.
Firstly note that you can use your own sql queries with entity beans by using the SqlSelect annotation. This should be your first approach when wanting to use your own SQL queries.
If ORM Mapping is too tight and constraining for your problem then SqlQuery could be a good approach.
The returned SqlRow objects are similar to a LinkedHashMap with some type conversion support added.
// its typically a good idea to use a named query
// and put the sql in the orm.xml instead of in your code
String sql = "select id, name from customer where name like :name and status_code = :status";
SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
sqlQuery.setParameter("name", "Acme%");
sqlQuery.setParameter("status", "ACTIVE");
// execute the query returning a List of MapBean objects
List<SqlRow> list = sqlQuery.findList();
i have a solution for it:-
RawSql rawSql = RawSqlBuilder
.parse("SELECT distinct CASE WHEN PARENT_EQUIPMENT_NUMBER IS NULL THEN EQUIPMENT_NUMBER ELSE PARENT_EQUIPMENT_NUMBER END AS PARENT_EQUIPMENT_NUMBER " +
"FROM TOOLS_DETAILS").create();
Query<ToolsDetail> query = Ebean.find(ToolsDetail.class);
ExpressionList<ToolsDetail> expressionList = query.setRawSql(rawSql).where();//ToolsDetail.find.where();
if (StringUtils.isNotBlank(sortBy)) {
if (StringUtils.isNotBlank(sortMode) && sortMode.equals("descending")) {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"desc");
//expressionList.orderBy().asc(sortBy);
}else if (StringUtils.isNotBlank(sortMode) && sortMode.equals("ascending")) {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"asc");
// expressionList.orderBy().asc(sortBy);
} else {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"desc");
}
}
if (StringUtils.isNotBlank(fullTextSearch)) {
fullTextSearch = fullTextSearch.replaceAll("\\*","%");
expressionList.disjunction()
.ilike("customerSerialNumber", fullTextSearch)
.ilike("organizationalReference", fullTextSearch)
.ilike("costCentre", fullTextSearch)
.ilike("inventoryKey", fullTextSearch)
.ilike("toolType", fullTextSearch);
}
//add filters for date range
String fromContractStartdate = Controller.request().getQueryString("fm_contract_start_date_from");
String toContractStartdate = Controller.request().getQueryString("fm_contract_start_date_to");
String fromContractEndtdate = Controller.request().getQueryString("fm_contract_end_date_from");
String toContractEnddate = Controller.request().getQueryString("fm_contract_end_date_to");
if(StringUtils.isNotBlank(fromContractStartdate) && StringUtils.isNotBlank(toContractStartdate))
{
Date fromSqlStartDate=new Date(AppUtils.convertStringToDate(fromContractStartdate).getTime());
Date toSqlStartDate=new Date(AppUtils.convertStringToDate(toContractStartdate).getTime());
expressionList.between("fmContractStartDate",fromSqlStartDate,toSqlStartDate);
}if(StringUtils.isNotBlank(fromContractEndtdate) && StringUtils.isNotBlank(toContractEnddate))
{
Date fromSqlEndDate=new Date(AppUtils.convertStringToDate(fromContractEndtdate).getTime());
Date toSqlEndDate=new Date(AppUtils.convertStringToDate(toContractEnddate).getTime());
expressionList.between("fmContractEndDate",fromSqlEndDate,toSqlEndDate);
}
PagedList pagedList = ToolsQueryFilter.getFilter().applyFilters(expressionList).findPagedList(pageNo-1, pageSize);
ToolsListCount toolsListCount = new ToolsListCount();
toolsListCount.setList(pagedList.getList());
toolsListCount.setCount(pagedList.getTotalRowCount());
return toolsListCount;

JPA query equivalent to mysql query

Below is mysql query which is working fine and giving me expected results on mysql console.
select * from omni_main as t where t.date_time BETWEEN STR_TO_DATE(CONCAT('2011', '08', '01'),'%Y%m%d') AND LAST_DAY(STR_TO_DATE(CONCAT('2012', '08','01'), '%Y%m%d')) group by year(date_time),month(date_time)
I need its JPA equivalent query. Below is what I am trying but its returning nothing.
String queryStr = "select * from OmniMainEntity o where o.dateTime BETWEEN STR_TO_DATE(CONCAT('"+fromYear+"', '"+fromMonth+"','01'), '%Y%m%d') AND "
+"LAST_DAY(STR_TO_DATE(CONCAT('"+toYear+"', '"+toMonth+"','01'), '%Y%m%d'))";
Query query = manager.createQuery(queryStr);
System.out.println("Result Size: "+query.getResultList().size());
Here fromYear, fromMonth, toYear, toMonth are method parameters using in creating queryStr.
Please suggest where I may wrong!
Any other way to achieve goal is also welcome!
As you are using JPA Query, it would be better to not use database-specified sql function, such as STR_TO_DATE.
You can have a try by this way.(A Hibernate way, JPA should be similiar):
First, you can parse a java.util.Date object from "fromYear" and "fromMonth" like below:
DateFormat df = new SimpleDateFormat("yyyyMMdd");
Date startDate = df.parse(fromYear + "" + fromMonth + "01");
Date endDate = df.parse(.....);
Then, set them into the JPA query.
String queryStr = "select * from OmniMainEntity o where o.dateTime BETWEEN :startDate AND :endDate)"; // The query now changed to database independent
Query query = manager.createQuery(queryStr);
query.setDate("startDate", startDate);
query.setDate("endDate", endDate);
At last, doing the search:
System.out.println("Result Size: "+query.getResultList().size());
Your query doesn't have a verb in it. You probably want SELECT in there:
SELECT o FROM OmniMainEntity o WHERE...
Also, you should be using parameterized and typed queries, and it's usual to use short names (o instead of omniMainEnt) to make your queries readable.

JPA 2.0 - NVARCHAR in native query

The project that I'm working on has the following setup: JPA 2.0 (Hibernate 4 implementation) and SQL Server 2008 R2.
I need to select some data from an SQL view. In order to do this I use a native query, but I ran into some problems with the NVARCHAR fields. Basically, when using this piece of code:
String sql = "SELECT v.text_field as address FROM SOME_CUSTOM_VIEW v
Query q = entityManager.createNativeQuery(sql,"ItemDetailsMapping");
List<Object[]> result = q.getResultList();
The ItemDetailsMapping is declared like:
#SqlResultSetMapping(name = "ItemDetailsMapping", columns = { #ColumnResult(name = "address") })
I get an exception saying:
org.springframework.orm.hibernate3.HibernateSystemException: No Dialect mapping for JDBC type: -9; nested exception is org.hibernate.MappingException: No Dialect mapping for JDBC type: -9
Type -9 is actually the NVARCHAR type, which we are extensively using throughout the application and it works perfectly when we are using non-native queries. Why is it not working with native queries? I even used a custom dialect and registered the type, but it's still not working.
Thanks a lot for your help
You have to associate the data type NVARCHAR to String.When using Hibernate via Session interface, you can explcitly set a type of result with addScalar() instead (also accessible via unwrap() in JPA 2.0):
So modify your code as below,
String sql = "SELECT v.text_field as address FROM SOME_CUSTOM_VIEW v"
Query q = entityManager.createNativeQuery(sql,"ItemDetailsMapping");
q.unwrap(SQLQuery.class).addScalar("address ", StringType.INSTANCE);
List<Object[]> result = q.getResultList();
Read here for more information.
(Edit 7/1/15 -- Added quotation mark for clarity)
You can do it like this:
String myquery = "select cast(t2.name as varchar) column_name from sys.objects t1 inner join sys.columns t2 on t2.object_id = t1.object_id"+
" left join sys.indexes t3 on t3.object_id = t1.object_id and t3.is_unique = 1 left join sys.index_columns t4 on t4.object_id = t1.object_id and t4.index_id = t3.index_id and t4.column_id = t2.column_id where (upper(t1.type) = 'U' or upper(t1.type) = 'V') and upper(schema_name(t1.schema_id)) = 'dbo' and upper(t1.name) = 'TEST'";

Why EclipseLink Query cache only works when I use query.getSingleResult()?

My entity has a named query which looks like this:
#NamedQuery(name = "Person.find", query = "select p from Organization p where p.name=:NAME")
In my code I want to set the query cache hint:
query.setHint("eclipselink.cache-usage", "CheckCacheThenDatabase");
If I try to get whole result list:
List<Person> result = query.getResultList();
EclipseLink throws an exception:
Exception [EclipseLink-6124] (Eclipse Persistence Services - 1.0.1 (Build 20080905)): org.eclipse.persistence.exceptions.QueryException
Exception Description: Required query of class org.eclipse.persistence.queries.ReadAllQuery, found class org.eclipse.persistence.queries.ReadObjectQuery
If I try to get only a single result, it works:
Person person = query.getSingleResult();
If I remove the query hint, then getResultList() works also.
I don't understand the exception - isn't it saying that it is specifically expecting getResultList()?? What am I doing wrong?
EclipseLink documentation says:
"EclipseLink does not support the cache usage for native queries or queries that have complex result sets such as returning data or multiple objects."
Also the documentation says:
"CheckCacheThenDatabase – You can configure any read-object query to check the cache completely before you resort to accessing the database."
So the behaviour seems to be ok, I just found the exception misleading.
EDIT: Try something like this in the entity definition, that should be enough: (Coded on the web page so there can be errors)
Entity
#Cache(expiry = 3600000, // 1 hour size = 10000)
#NamedQueries({
#NamedQuery(name = "Person.findByPK", query = "select p from Person p " +
"where p.name=:NAME",
hints = {
#QueryHint(name = QueryHints.CACHE_USAGE,
value = CacheUsage.CheckCacheThenDatabase),
#QueryHint(name = QueryHints.QUERY_TYPE, value = QueryType.ReadObject)
}
})

Categories