Hibernate: using LocalDateTime to query a range - java

I'm using hibernate 5 with the hibernate-java8 addon to use LocalDateTime fields. I now have an entity which has a date field and a user field like in:
#Entity
public class Transaction {
// ...
private User user;
private LocalDateTime date;
// ..
}
Now I simply want to query all transactions of a user within a certain time range. So I use the following query:
SELECT t FROM Transaction t WHERE t.user = :owner AND t.date BETWEEN :from AND :to
Strange enough this query does not give me any results. I also tried using the < and > operators but that did not help either. When I leave out the time range part I get the correct list of transactions for the user. When I execute the SQL query generated by Hibernate in the MySQL workbench I also get the expected results. I use the following snippet to execute the (named) query:
public <T> List<T> findList(Class<T> type, String queryName,
Map<String, Object> params) {
final EntityManager em = this.entityManager.get();
final TypedQuery<T> query = em.createNamedQuery(queryName, type);
params.forEach(query::setParameter);
return query.getResultList();
}
This is simply called providing the query listed above and a map of named parameters like:
findList(Transaction.class, Transaction.BY_USER_AND_RANGE,
ImmutableMap.of("owner", owner, "from", from, "to", to));
In my test case a persisted a single Transaction with the current date and created a range from yesterday to tomorrow for the query. Inspecting the table in the MySQL workbench shows that the transaction is there and that the date field has the correct type and contains the correct value. Yet my query won't give me any results.
Is there anything I'm missing?

The date you are passing as param should also be
LocalDateTime
see the example code below might help you
LocalDate date = LocalDate.of(2015, 8, 11);
TypedQuery<MyEntity> query = this.em.createQuery("SELECT e FROM MyEntity e WHERE date BETWEEN :start AND :end", MyEntity.class);
query.setParameter("start", date.minusDays(2));
query.setParameter("end", date.plusDays(7));
MyEntity e = query.getSingleResult();

Related

Using Like Expression for Date Field using JPA 2.1

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.

Fetching data from mysql database table based on condition of date

Can anyone suggest me the solution of fetching data on condition of current date which i am passing in dao layer method of type system current date.
for e.g I have to fetch data from table "X" which have a column "startdate" of Date type(Mysql) on the basis if that date is greater with the current date passed to the query in java.
I have tried with java.util.Date but not working and also my requirement is not to used database specific function like curr() or Now().
I found a relative post but none helped.Is there no alternative apart from using JodaTime.
Thanks.
In your Entity class mark 'startdate' field with
#Column(name = "startdate")
#Temporal(TemporalType.DATE)
private Date startdate;
And create query like this one:
#NamedQuery(name = "Entity.findAfterDate", query = "SELECT e FROM Entity e WHERE e.startdate >= :dateAfter")
In your EntityDAOImpl
public List<Entity> getEntitiesAfterDate(Date date) {
Query query = openSession().getNamedQuery("Entity.findAfterDate");
query.setParameter("dateAfter", date);
return query.list();
}

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 timestamp comparison not working

I have a persistent object with two Date fields, like this
#Temporal(TemporalType.TIMESTAMP)
private Date generated;
#Temporal(TemporalType.TIMESTAMP)
private Date expirationTime;
On object construction the expirationTime is initialized based on the generated field
this.expirationTime = new Date(generated.getTime() + ttlMillis);
Now I'm trying to delete all expired objects from the database with a JPA query
Query q = em.createQuery("delete from MyObject t where CURRENT_TIMESTAMP > t.expirationTime");
q.executeUpdate();
But running a simple test
MyObject o = new MyObject(somePastDate, someTTL);
em.persist(o);
... create the query above
q.executeUpate();
shows no row is being deleted. Any clue on what I'm doing wrong? I'm running tests on HSQLDb and using Hibernate as my JPA provider.
Edit: solved, but I'm not sure why this version should work differently.
Query q = em.createQuery("delete from MyObject t where :now > t.expirationTime");
q.setParameter("now", new Date());
q.executeUpdate();
What's the use of CURRENT_TIMESTAMP if comparisons with other fields fail?
You did not set any parameter, and the query has to get a named parameter:
Query q = em.createQuery("delete from MyObject t where CURRENT_TIMESTAMP > :expirationTime");
q.setParameter("expirationTime" , yourObject.getExpirationTime());//or your getter
q.executeUpdate();
I add a link to enforce the topic here.
Check if it works :-)

Older then query in java persistence

I need to do a query where i take things older then 5 days.(a custom date)
i have looked at HQL but since it is in persistence i dont have access to setTimestamp as per here
String hqlQuery;
Calendar minDate = Calendar.getInstance();
minDate.add(Calendar.DATE, -days);
hqlQuery = "select n from notifications where n.app_id=:app and ((n.sent_date<=:minDate) OR (n.sent_date is null)) and n.handled='N'";
is how i tried...
Any sweet helpers, thanks in advance :-)
Edit:
So i changed my method it now looks like this:
public static List<Notification> findOlderThen(EntityManager em, Long app, int days) {
String hqlQuery;
Calendar minDate = Calendar.getInstance();
minDate.add(Calendar.DATE, -days);
hqlQuery = "select n from notifications where n.app_id=:app and ((n.sent_date<=:minDate) OR (n.sent_date is null)) and n.handled='N'";
System.out.println(hqlQuery);
return em.createQuery(hqlQuery).setParameter("app", app).setParameter("minDate",minDate.getTime()).getResultList();
}
But it gives this error:
Caused by: java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Syntax error parsing the query [select n from notifications where n.app_id=:app and ((n.sent_date<=:minDate) OR (n.sent_date is null)) and n.handled='N'], line 1, column 28: syntax error at [where].
Internal Exception: UnwantedTokenException(found=where, expected 80)
SessionFactory sf = // get your session factory
Query q = sf.getCurrentSession()
.createQuery(hqlQuery);
q.setParameter("minDate",minDate.getTime())
.setParameter("app", appId)
.list();
Would be how you set a date parameter, assuming type of sent_date is Date in Notifications model class. Also, on this part of your query select n from notifications, make sure notifications is the actual name of your class. Case matters! It should probably be select n from Notifications n.
Update:
You need to declare the alias for the class.
If I'm not mistaken, aren't you supposed to do something like this when you're adding dates in a query:
hqlQuery = "select n from notifications where n.app_id=:app and ((n.sent_date<=':minDate') OR (n.sent_date is null)) and n.handled='N'";
notice the ' around the minDate variable

Categories