Pull to refresh

Пользовательские критерии в Hibernate

Reading time4 min
Views7.2K
В предыдущей статье я рассказал, как научить Hibernate хранить пользовательские типы данных. Теперь попробуем использовать эти данные при фильтрации выборок. Задекларируем результат, который мы хотим получить:
  1. String filteringTag = "habr";
  2. Session session = ...;
  3. Criteria criteria = session.createCriteria(StringArrayContainer.class);
  4. criteria.add(new StringArrayContainsCriterion("tags", filteringTag));
  5. List containers = criteria.list(); //Получаем список контейнеров, содержащих тег "habr"
  6. ...


Сначала напишем универсальную реализацию критерия поиска. Для этого внесем небольшое изменение в методы StringArrayCustomType.nullSafeGet и StringArrayCustomType.nullSafeSet, а именно окружим пробелами значение, хранимое в колонке. (Изменения в 7 и 25 строках)
  1. @Override
  2. public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
  3.     String value = (String) Hibernate.TEXT.nullSafeGet(rs, names[0]);
  4.     if (value == null) {
  5.         return null;
  6.     } else {
  7.         String[] array = StringUtils.split(value.trim(), ' ');
  8.         for (int i = 0; i < array.length; i++) {
  9.             array[i] = WhitespaceEscapeUtil.unescape(array[i]);
  10.         }
  11.         return array;
  12.     }
  13. }
  14.  
  15. @Override
  16. public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
  17.     if (value == null) {
  18.         Hibernate.TEXT.nullSafeSet(st, null, index);
  19.     } else {
  20.         String[] array = (String[]) value;
  21.         String[] copy = new String[array.length];
  22.         for (int i = 0; i < array.length; i++) {
  23.             copy[i] = WhitespaceEscapeUtil.escape(array[i]);
  24.         }
  25.         Hibernate.TEXT.nullSafeSet(st, ' '+StringUtils.join(copy, ' ')+' ', index);
  26.     }
  27. }

Теперь аккуратно реализуем интерфейс org.hibernate.criterion.Criterion.
  1. public class GenericStringArrayContainsCriterion extends LikeExpression {
  2.     public GenericStringArrayContainsCriterion(String propertyName, String value) {
  3.         super(propertyName, "% "+WhitespaceEscapeUtil.escape(value) + " %")
  4.     }
  5. }
Понятное дело, что реализация через like не очень эффективна с точки зрения производительности. В целях оптимизации напишем тоже самое, используя возможности PostgreSQL 8.x.
  1. public class PostgresStringArrayContainsCriterion implements Criterion {
  2.  
  3.     private final String propertyName;
  4.     private final String value;
  5.  
  6.     private final String TEMPLATE = "to_tsvector('simple', {column}) @@ plainto_tsquery('simple', ?)";
  7.     private static final char TAG_SEPARATOR = ' ';
  8.  
  9.     public PostgresStringArrayContainsCriterion(String propertyName, String value) {
  10.         this.propertyName = propertyName;
  11.         this.value = WhitespaceEscapeUtil.escape(value);
  12.     }
  13.  
  14.     public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
  15.         return StringHelper.replace(TEMPLATE, "{column}", criteriaQuery.getColumn(criteria, propertyName));
  16.     }
  17.  
  18.     public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
  19.         return new TypedValue[]{new TypedValue(Hibernate.STRING, value, EntityMode.POJO)};
  20.     }
  21. }
И с помощью шаблона «стратегия» будем выбирать реализацию в зависимости от текущего диалекта:
  1. public class StringArrayContainsCriterion implements Criterion {
  2.      
  3.     private final String propertyName;
  4.     private final String value;
  5.  
  6.      
  7.     public StringArrayContainsCriterion(String propertyName, String value) {
  8.         this.propertyName = propertyName;
  9.         this.value = value;
  10.     }
  11.  
  12.     public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
  13.         return getStrategy(criteriaQuery.getFactory().getDialect()).toSqlString(criteria, criteriaQuery);
  14.     }
  15.  
  16.     public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
  17.         return getStrategy(criteriaQuery.getFactory().getDialect()).getTypedValues(criteria, criteriaQuery);
  18.     }
  19.    
  20.     public Criterion getStrategy(Dialect dialect) {
  21.         if (dialect instanceof PostgreSQLDialect) {
  22.             return new PostgresStringArrayContainsCriterion(propertyName, value);
  23.         } else {
  24.             return new GenericStringArrayContainsCriterion(propertyName, value);
  25.         }    
  26.     }    
  27. }
  28.  


Ну вот собственно и все. Теперь мы умеем сохранять пользовательские типы и фильтровать по ним.

______________________
Текст подготовлен в Редакторе Блогов от © SoftCoder.ru
Tags:
Hubs:
Total votes 9: ↑6 and ↓3+3
Comments8

Articles