Hibernate. Optimizing sqlRestriction - java

How can I optimize the following without sqlRestriction:
public List<SomeEntity> getEntityByYearMonthAndDay(Calendar cal) {
Criteria criteria = helper.createCriteria();
criteria.add(sqlRestriction("(DATE(date_column) = ?)", cal.getTime(), org.hibernate.type.StandardBasicTypes.DATE));
return criteria.list();
}
SomeEntity looks like:
#Entity
#Table(name="some_table")
public class SomeEntity extends Identifiable {
#Column(name = "date_column")
private Calendar dateColumn;
//Getters and setters
}
In the DB I have such representation:
date_column => datetime (YYYY-MM-DD HH:mm:ss).
It's obvious that logic compares only date truncating time values.

Yeah, guys :)
So if you want not to use built-in MySQL DATE(...) function and use only JPA:
public List<SomeEntity> getEntityByYearMonthAndDay(Calendar cal) {
Criteria criteria = helper.createCriteria();
Calendar leftBorder = Calendar.getInstance();
leftBorder.setTime(cal.getTime());
Calendar rightBorder = Calendar.getInstance();
rightBorder.setTime(cal.getTime());
rightBorder.add(Calendar.DATE, 1);
Conjunction dateConjunction = Restrictions.conjunction();
dateConjunction.add(Restrictions.eq("dateColumn", leftBorder));
dateConjunction.add(Restrictions.lt("dateColumn", rightBorder));
criteria.add(dateConjunction);
return criteria.list();
}

Related

Creating JPA query with Date in Spring

This is how my entity looks like:
#Entity
public class Registration {
#Id
#GeneratedValue
private Integer id;
#org.springframework.format.annotation.DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate date;
}
This is how my repo could look like:
#Query(value = "SELECT * FROM registration WHERE MONTH(date) = ?1 AND YEAR(date) = ?2")
List<Registration> findAll(Integer month, Integer year);
And this will be service:
public List<Registration> getCurrentRegistration() {
LocalDate today = LocalDate.now();
return registrationRepository.findAll(today.getMonth().getValue(), today.getYear());
}
public List<Registration> getRegistrations(Integer month, Integer year) {
return registrationRepository.findAll(month, year);
}
How can I change my native query to be JPA query?
Will the JPA query able to work on postgresql and hsqldb?
And why JPA queries are the best for spring apps? (or why they are not)
Make a specification class and write the below specification method in it.
import javax.persistence.criteria.Predicate;
import org.springframework.data.jpa.domain.Specification;
public class RegistrationSpecification {
public static Specification<Registration > registrationSpecForDate(
LocalDate invoiceDate ) {
return (root, cq, cb) -> {
List<Predicate> predicates = new ArrayList<Predicate>();
if (invoiceDate!=(null)) {
predicates.add(cb.greaterThanOrEqualTo(root.get("date"),
invoiceDate));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
}
Then in your repository inject this specification in JPA's findAll() method.
`public List<Registration> getRegistrations(LocalDate date) {
return
registrationRepository.findAll
(RegistrationSpecification.registrationSpecForDate(date));
`
You could do this using QueryDSL JPA (https://github.com/querydsl/querydsl/tree/master/querydsl-jpa) to define a predicate:
Predicate createPredicate(Integer month, Integer year) {
return QRegistration.date.year().eq(year).and(QRegistration.date.month().eq(month));
}
Then make your repo extend QueryDslPredicateExecutor:
public interface RegistrationRepository extends JpaRepository<Registration>, QueryDslPredicateExecutor {
// Your query methods here
}
This contains a List<T> findAll(Predicate predicate) method which you can pass your predicate in to obtain the items you were after, e.g.:
registrationRepository.findAll(createPredicate(1, 1970));
See here for more info about using QueryDSL with Spring: https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/

JPA AttributeConverter comparison?

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);

#Type annotation in Hibernate is not Working. I am using #SQLInsert

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;
}

Using dates in Criteria Query where clause

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.

Convert date to string in Hibernate Criteria

Is it possible to perform a request that checks the string representation of a Date instance. For example
Restrictions.like("dateField", "%12%")
to retrieve dates that either have String 12 in day or 12 in month or 12 in year where "dateField" is an instance of java.util.Date
Thanks
I had the same problem and here's what I did:
First, I created my own Criterion implementation:
public class DateLikeExpression implements Criterion {
private static final long serialVersionUID = 1L;
private String propertyName;
private String value;
public DateLikeExpression(String propertyName, String value) {
this.propertyName = propertyName;
this.value = value;
}
public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
String[] columns = criteriaQuery.getColumnsUsingProjection(criteria, propertyName);
if (columns.length != 1) {
throw new HibernateException("Like may only be used with single-column properties");
}
return "to_char(" + columns[0] + ", 'DD/MM/YYYY HH24:MI') like ?";
}
public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
return new TypedValue[] { new TypedValue(new org.hibernate.type.StringType(),
MatchMode.START.toMatchString(value.toLowerCase()), EntityMode.POJO) };
}
}
Then, I just use it when I put the Criteria together:
criteria.add(new DateLikeExpression("dateColumnName", "26/11%"));
And that's about it. Note that this implementation is locale-dependent (pt_BR in this case) and works for postgresql, which has the to_char function. You might have to tweak it a little bit to work with your database engine.
Something like this
Restrictions.sqlRestriction("month(dateField) = 12");
Restrictions.sqlRestriction("right(year(dateField),2) = 12");
The part within the sqlRestriction depends on which database you are using.

Categories