I have an entity with a java.util.Date field stored as a TemporalType.DATE. When passing a java.util.Date with hours, minutes or seconds to the where clause of a criteria query I can't seem to get a match from the database.
The setup is an embedded H2-database in Spring with Hibernate. I've tried using PostgreSQL instead of H2 and it works. I've also tried to set H2 in PostgreSQL-mode, but it doesn't change anything.
Given the entity
#Entity
public class SomeEntity {
#Id
private int id;
#Temporal(TemporalType.DATE)
private java.util.Date aDate;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getDate() {
return aDate;
}
public void setDate(Date aDate) {
this.aDate = aDate;
}
}
The following query only returns a match if the hours, minutes and seconds of the parameter have been set to 0.
public List<SomeEntity> someQueryOnDate(Date date) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<SomeEntity> query = cb.createQuery(SomeEntity.class);
Root<SomeEntity> root = query.from(SomeEntity.class);
Predicate dateEquals = cb.equal(root.get(SomeEntity_.date), date);
query.where(dateEquals);
// This list is always empty if the date in the predicate has a time part
return em.createQuery(query).getResultList();
}
A full example follows. The test fails on the last assertion, where I query the database using a Date with hours, minutes and seconds set.
#Test
public void testDateEquals() throws ParseException {
Date dateWithoutTime = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-03");
Date dateWithTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2014-07-03 09:45:01");
createEntity(dateWithoutTime);
List<SomeEntity> entitiesMatchingDateWithTime = listAllEntitiesWithDate(dateWithTime);
List<SomeEntity> entitiesMatchingDateWithoutTime = listAllEntitiesWithDate(dateWithoutTime);
Assert.assertFalse("No entities matched the date without time", entitiesMatchingDateWithoutTime.isEmpty());
Assert.assertFalse("No entities matched the date with time" , entitiesMatchingDateWithTime.isEmpty());
}
private void createEntity(Date d) {
SomeEntity entity = new SomeEntity();
entity.setDate(d);
em.persist(entity);
// For good measure
em.flush();
em.clear();
}
private List<SomeEntity> listAllEntitiesWithDate(Date date) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<SomeEntity> query = cb.createQuery(SomeEntity.class);
Root<SomeEntity> root = query.from(SomeEntity.class);
Predicate dateEquals = cb.equal(root.get(SomeEntity_.date), date);
query.where(dateEquals);
return em.createQuery(query).getResultList();
}
Maybe the solution is provided here:
Hibernate Criteria for Dates
You can use Restrictions to compare dates.
Related
How can I use Spring Specification with Date field? I have no problem with 'normal' fields like Strings. But when I have Date, I have a problem and can't find a solution to solve it.
Here is my TaskEntity.class:
#Entity
#Table(name = "TASKS")
public class TaskEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String description;
#ManyToOne
private StatusEntity status;
private Date expiryDate;
// ....
}
And here is my TaskSpecification.class:
public class TaskSpecification implements Specification<TaskEntity> {
private static final Logger LOGGER = Logger.getLogger(TaskSpecification.class.getName());
private List<SearchCriteria> searchCriteriaList;
public TaskSpecification() {
this.searchCriteriaList = new ArrayList<>();
}
public void add(SearchCriteria criteria) {
searchCriteriaList.add(criteria);
}
#Override
public Predicate toPredicate(Root<TaskEntity> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
LOGGER.info("toPredicate()");
List<Predicate> predicates = new ArrayList<>();
for (SearchCriteria criteria : searchCriteriaList) {
if (criteria.getValue() instanceof Date) {
// WHAT TO DO HERE?
} else {
predicates.add(
builder.equal(
root.get(criteria.getKey()),
criteria.getValue().toString())
);
}
}
LOGGER.info("toPredicate(...)");
return builder.and(predicates.toArray(new Predicate[0]));
}
}
Ihe same problem I faced a month ago but this solution solved my issue.
public static Date startDate(Date date) {
try {
DateFormat df2 = new SimpleDateFormat("yyyy-MM-dd");
String strDate = df2.format(date) + "T00:00:00";
LocalDateTime localDate = LocalDateTime.parse(strDate);
Instant instant = localDate.atZone(ZoneId.systemDefault()).toInstant();
date = Date.from(instant);
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
return date;
}
Create a function and call where you use the date.
like that
values.add(EntitiesSpecification.startDate(fr.getValues().get(0)));
you can get the date value and add the date format in entity like MM-DD-YYYY. Could you please try this way?
I have a Pojo object on which I store the ObjectID retrieved from Mongo.
When I print it, I can see this:
Object: { "_id" : { "$oid" : "5a4b939225c218fbe107199c" }, "organizationId" : "ORGANIZATION_ID", "organizationName" : "Organization name", "storeIds" : [] }
I want now to search using the ObjectID and I'm using this:
public String find(String dbId, String collection, ObjectId id) throws Exception {
BasicDBObject query = new BasicDBObject();
query.put("_id", id);
FindIterable<Document> search = collection.find(query);
}
The problem is that search is always null. How can I search using ObjectID? Is there a way to do without extracting the oid and creating the query in this way?
query.put("_id", new ObjectId(oid));
Try as,
import org.bson.types.ObjectId;
public DBObject findDocumentById(String id) {
BasicDBObject query = new BasicDBObject();
query.put("_id", new ObjectId(id));
DBObject dbObj = collection.findOne(query);
return dbObj;
}
You can find one or all the similar object ids.
For first, when the I fill the Pojo and ObjectID is filled with the values retrieved from MongoDB, a new object is created so, it will be different than the one retrieved.
What I have done is to create a new DataType, adding these two classes:
public class MongoOID {
#SerializedName("$oid")
private String oid;
public String getOid() {
return oid;
}
public void setOid(String oid) {
this.oid = oid;
}
}
public class MongoId {
#SerializedName("_id")
private MongoOID _id;
/**
* http://www.baeldung.com/migrating-to-java-8-date-time-api
*/
private LocalDateTime created;
/**
* http://www.baeldung.com/migrating-to-java-8-date-time-api
*/
private LocalDateTime updated;
//region Getter and Setter
public MongoOID get_id() {
return _id;
}
public void set_id(MongoOID _id) {
this._id = _id;
}
public LocalDateTime getCreated() {
return created;
}
public void setCreated(LocalDateTime created) {
this.created = created;
}
public LocalDateTime getUpdated() {
return updated;
}
public void setUpdated(LocalDateTime updated) {
this.updated = updated;
}
//endregion
}
So, on my Pojo, I replaced ObjectId _id with MongoID _id and I'm able to read the proper ID.
When I use this ID to query mongo, I simply use this:
BasicDBObject query = new BasicDBObject();
query.put("_id", new ObjectId(id));
where id is a String and it is :
organization.get_id().getOid()
I'm not sure this is the better way (and probably the correct neither) but it works. Unfortunately, I do not have the creation timestamp so I have had to add two key to my document (created and updated).
I'm looking for an easy way to query a database column of type string in format YYYYMMDD. This could be done with a native query like:
select * from TPRODUCT where to_date(ENDOFPRODUCTION, 'YYYYMMDD') > CURRENT_DATE;
But is there a way to achieve this comparison with an AttributeConverter
#NamedQuery(name = "product.filterByEOP", query =
"select p from Product p where p.eop > :currentDate")
Well, it turns out you can. In case anyone else is looking into this, after checking the reference I ended up with:
public class DateConverter implements AttributeConverter<Date, String> {
private final String DATE_FORMAT = "YYYYMMDD";
#Override
public String convertToDatabaseColumn(Date date) {
return new SimpleDateFormat(DATE_FORMAT).format(date);
}
#Override
public Date convertToEntityAttribute(String dateString) {
Date converted = null;
try {
converted = new SimpleDateFormat(DATE_FORMAT).parse(dateString);
} catch (ParseException ex) {
}
return converted;
}
}
which you can then use on an entity attribute:
#Column(name = "ENDOFPRODUCTION")
#Convert(converter = DateConverter.class)
private Date eop;
and use the query:
final TypedQuery<Product> query = this.entityManager.createNamedQuery("product.filterByEOP", Product.class);
query.setParameter("currentDate", new Date());
List<Product> models = query.getResultList();
Why you don't convert your String to a date and use this last like it is :
String myDate = "20170607";
DateFormat format = new SimpleDateFormat("YYYYMMDD");
Date newDate = format.parse(myDate);
Now you can use :
query.setParameter("currentDate", newDate);
I have a model class in hibernate with a field with Calendar type. The corresponding DB column is of timestampwithtimezone(6). I am using custom #SQLInsert. The class looks like below-
#Entity
#SQLInsert("insert into EMPLOYEE (STARTDATETIME) values (TO_TIMESTAMP_TZ(?,'DD- MM-YY HH.MI.SS PM TZHTZM '))" )
#Table(
name = "EMPLOYEE",
)
public class Employee {
#Column(
name = "STARTDATETIME",
nullable = false
)
#Type(type = "com.myPackage.TimeStampTypeImpl")
private Calendar startDateTime;
public Calendar getStartDateTime() {
return this.startDateTime;
}
public void setStartDateTime(Calendar startDateTime) {
this.startDateTime = startDateTime;
}
}
In the class TimeStampTypeImpl which is implementing Usertype is doing the necessary conversion of Calendar to suitable string which is supposed to be given in the input parameter of the insert query, in nullSafeSet method.
But the problem I am getting is- it seems that the the custome class TimestampImpl is not working- the nullSafeSet method is not getting called. So the insert query is failing.
The libraries I am using is-
hibernate-commons-annotations-4.0.1.Final-redhat-2.jar
hibernate-core-4.2.7.SP1-redhat-3.jar
hibernate-jpa-2.0-api-1.0.1.Final-redhat-2.jar
Code in TimeStampTypeImpl :
public void nullSafeSet(PreparedStatement st, Object value, int index)
throws HibernateException, SQLException {
Calendar cal = (Calendar) value;
log.debug("TIMESTAMPIMPL2");
tring dateTime = getOracleFormattedTimeWithZone(cal);
log.debug("TIMESTAMPIMPL3");
st.setString(index, dateTime);
}
private static String getOracleFormattedTimeWithZone(Calendar timeWithZone) {
String dateFormat = "dd-MM-yy hh.mm.ss a Z";
DateFormat df = new SimpleDateFormat(dateFormat);
String dateTime = df.format(timeWithZone.getTime());
System.out.println("()()()()()()()()()"+dateTime);
return dateTime;
}
I am trying to run a query to fetch some statistic data from my database. And I'm using JPA. But I faced such a trouble: when I run JPQL query, the empty result set is returned. But when I run SQL, produced with JPA for that JPQL query, I got a single row of data.
Here's what I've got:
The Ticket entity
#Entity
#Table(name="tickets")
public class Ticket extends AbstractEntity {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Embedded
private Owner owner;
#ManyToOne
#JoinColumn(name="flightId")
private Flight flight;
private String status;
public Ticket() {
this.status = "AVAILABLE";
}
The Flight entity
#Entity
#Table(name="flights")
public class Flight extends AbstractEntity {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String departure;
private String destination;
private Date date;
private float ticketCost;
#OneToMany(mappedBy="flight", fetch=FetchType.LAZY, cascade=CascadeType.ALL)
private List<Ticket> tickets = new ArrayList<Ticket>();
The result row class
public class SoldReportRow {
private String departure;
private String destination;
private DateTime date;
private int ticketsSold;
private float totalCost;
public SoldReportRow(Date date, String departure, String destination, Long ticketsSold, Double totalCost) {
this.departure = departure;
this.destination = destination;
this.ticketsSold = ticketsSold.intValue();
this.totalCost = totalCost.floatValue();
this.date = new DateTime(date);
}
The JPQL
SELECT NEW entities.SoldReportRow(f.date, f.departure, f.destination,
COUNT(t.id), SUM(f.ticketCost))
FROM Ticket t JOIN t.flight f
WHERE t.status = 'SOLD' AND t.owner IS NOT NULL AND f.date BETWEEN ? and ?
GROUP BY f.id
The generated SQL
SELECT t0.DATE, t0.DEPARTURE, t0.DESTINATION, COUNT(t1.ID), SUM(t0.TICKETCOST)
FROM flights t0, tickets t1
WHERE ((((t1.STATUS = ?) AND NOT ((((((t1.ADDRESS IS NULL)
AND (t1.EMAIL IS NULL)) AND (t1.NAME IS NULL)) AND (t1.OWNERFROM IS NULL))
AND (t1.PHONE IS NULL)))) AND (t0.DATE BETWEEN ? AND ?))
AND (t0.ID = t1.flightId)) GROUP BY t0.ID
So here is what I got when I run JPQL:
And here is what I got when I run the generated SQL:
UPD: the TicketDAO methods
// ...
protected static EntityManagerFactory factory;
protected static EntityManager em;
static {
factory = Persistence.createEntityManagerFactory(UNIT_NAME);
}
// ...
public static List<SoldReportRow> soldReportByDate(String from, String to) {
DateTimeFormatter dfTxt = DateTimeFormat.forPattern("dd/MM/yyyy");
DateTimeFormatter dfSql = DateTimeFormat.forPattern("yyyy-MM-dd");
String startDate = dfSql.print(dfTxt.parseDateTime(from));
String endDate = dfSql.print(dfTxt.parseDateTime(to));
String query = String.format(
"SELECT NEW entities.SoldReportRow(f.date, f.departure, f.destination, COUNT(t.id), SUM(f.ticketCost)) FROM " +
"Ticket t JOIN t.flight f " +
"WHERE t.status = 'SOLD' AND t.owner IS NOT NULL AND f.date BETWEEN '%s' and '%s' " +
"GROUP BY f.id",
startDate, endDate
);
return TicketDAO.query(SoldReportRow.class, query);
}
public static <T> List<T> query(Class<T> entityClass, String query) {
EntityManager entityManager = getEntityManager();
TypedQuery<T> q = entityManager.createQuery(query, entityClass);
List<T> entities = null;
try {
entities = q.getResultList();
} finally {
entityManager.close();
}
return entities;
}
public static EntityManager getEntityManager() {
return factory.createEntityManager();
}
The question is, why does this happen and how to fix that?
Thanks!
After the research, I've found that the trouble was caused by the data at the database. By default, SQLite does not have the DATE column type. And it uses strings to describe timestamps. So for date comparison (just like SELECT ... WHERE date BETWEEN a AND b) it's better to use UTC date form, not string one (1397036688 is the better value than the 2014-03-09).