В предыдущей статье я рассказал, как научить Hibernate хранить пользовательские типы данных. Теперь попробуем использовать эти данные при фильтрации выборок. Задекларируем результат, который мы хотим получить:
Сначала напишем универсальную реализацию критерия поиска. Для этого внесем небольшое изменение в методы StringArrayCustomType.nullSafeGet и StringArrayCustomType.nullSafeSet, а именно окружим пробелами значение, хранимое в колонке. (Изменения в 7 и 25 строках)
Теперь аккуратно реализуем интерфейс org.hibernate.criterion.Criterion.
Ну вот собственно и все. Теперь мы умеем сохранять пользовательские типы и фильтровать по ним.
______________________
- String filteringTag = "habr";
- Session session = ...;
- Criteria criteria = session.createCriteria(StringArrayContainer.class);
- criteria.add(new StringArrayContainsCriterion("tags", filteringTag));
- List containers = criteria.list(); //Получаем список контейнеров, содержащих тег "habr"
- ...
Сначала напишем универсальную реализацию критерия поиска. Для этого внесем небольшое изменение в методы StringArrayCustomType.nullSafeGet и StringArrayCustomType.nullSafeSet, а именно окружим пробелами значение, хранимое в колонке. (Изменения в 7 и 25 строках)
- @Override
- public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
- String value = (String) Hibernate.TEXT.nullSafeGet(rs, names[0]);
- if (value == null) {
- return null;
- } else {
- String[] array = StringUtils.split(value.trim(), ' ');
- for (int i = 0; i < array.length; i++) {
- array[i] = WhitespaceEscapeUtil.unescape(array[i]);
- }
- return array;
- }
- }
-
- @Override
- public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
- if (value == null) {
- Hibernate.TEXT.nullSafeSet(st, null, index);
- } else {
- String[] array = (String[]) value;
- String[] copy = new String[array.length];
- for (int i = 0; i < array.length; i++) {
- copy[i] = WhitespaceEscapeUtil.escape(array[i]);
- }
- Hibernate.TEXT.nullSafeSet(st, ' '+StringUtils.join(copy, ' ')+' ', index);
- }
- }
Теперь аккуратно реализуем интерфейс org.hibernate.criterion.Criterion.
- public class GenericStringArrayContainsCriterion extends LikeExpression {
- public GenericStringArrayContainsCriterion(String propertyName, String value) {
- super(propertyName, "% "+WhitespaceEscapeUtil.escape(value) + " %")
- }
- }
Понятное дело, что реализация через like не очень эффективна с точки зрения производительности. В целях оптимизации напишем тоже самое, используя возможности PostgreSQL 8.x.
- public class PostgresStringArrayContainsCriterion implements Criterion {
-
- private final String propertyName;
- private final String value;
-
- private final String TEMPLATE = "to_tsvector('simple', {column}) @@ plainto_tsquery('simple', ?)";
- private static final char TAG_SEPARATOR = ' ';
-
- public PostgresStringArrayContainsCriterion(String propertyName, String value) {
- this.propertyName = propertyName;
- this.value = WhitespaceEscapeUtil.escape(value);
- }
-
- public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
- return StringHelper.replace(TEMPLATE, "{column}", criteriaQuery.getColumn(criteria, propertyName));
- }
-
- public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
- return new TypedValue[]{new TypedValue(Hibernate.STRING, value, EntityMode.POJO)};
- }
- }
И с помощью шаблона «стратегия» будем выбирать реализацию в зависимости от текущего диалекта:
- public class StringArrayContainsCriterion implements Criterion {
-
- private final String propertyName;
- private final String value;
-
-
- public StringArrayContainsCriterion(String propertyName, String value) {
- this.propertyName = propertyName;
- this.value = value;
- }
-
- public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
- return getStrategy(criteriaQuery.getFactory().getDialect()).toSqlString(criteria, criteriaQuery);
- }
-
- public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
- return getStrategy(criteriaQuery.getFactory().getDialect()).getTypedValues(criteria, criteriaQuery);
- }
-
- public Criterion getStrategy(Dialect dialect) {
- if (dialect instanceof PostgreSQLDialect) {
- return new PostgresStringArrayContainsCriterion(propertyName, value);
- } else {
- return new GenericStringArrayContainsCriterion(propertyName, value);
- }
- }
- }
-
Ну вот собственно и все. Теперь мы умеем сохранять пользовательские типы и фильтровать по ним.
______________________