Статьи

Управление иерархическими данными с использованием Spring, JPA и аспектов

Управление иерархическими данными с использованием двухмерных таблиц является проблемой. Есть некоторые модели, чтобы уменьшить эту боль. Одно из таких решений описано здесь . Эта статья о реализации того же с использованием Spring, JPA, аннотаций и аспектов. Пожалуйста, пройдите по ссылке, чтобы лучше понять описанное решение. Цель состоит в том, чтобы создать компонент, который удалит стандартный код на бизнес-уровне для обработки иерархических данных.
Резюме

  • Создать базовый класс для сущностей, используемых для представления иерархических данных
  • Создать классы аннотаций
  • Укажите Аспект, который будет выполнять дополнительные шаги для управления Иерархическими данными. (Сердце решения)
  • Теперь Аспект можно использовать везде, где используются иерархические данные.

подробность

  • Создайте базовый класс для сущностей, используемых для представления иерархических данных.
    Целью суперкласса является инкапсуляция всех распространенных атрибутов и операций, необходимых для управления иерархическими данными в таблице. Обратите внимание, что класс помечается как @MappedSuperclass.

    Методы предназначены для генерации запросов, необходимых для выполнения операций CRUD в таблице. Их использование будет более понятным позже в этой статье, когда мы вернемся к HierarchicalEntity.

    Теперь любой объект, расширяющий этот класс, будет иметь все атрибуты, необходимые для управления иерархическими данными.

    import com.es.clms.aspect.HierarchicalEntity;
    import javax.persistence.EntityListeners;
    import javax.persistence.MappedSuperclass;

    @MappedSuperclass
    @EntityListeners({HierarchicalEntity.class})
    public abstract class AbstractHierarchyEntity
    implements Serializable {

    protected Long parentId;
    protected Long lft;
    protected Long rgt;

    public String getMaxRightQuery() {
    return "Select max(e.rgt) from " + this.getClass().getName() + " e";
    }

    public String getQueryForParentRight() {
    return "Select e.rgt from " + this.getClass().getName()
    + " e where e.id = ?1";
    }

    public String getDeleteStmt() {
    return "Delete from " + this.getClass().getName()
    + " e Where e.lft between ?1 and ?2";
    }

    public String getUpdateStmtForFirst() {
    return "Update " + this.getClass().getName()
    + " e set e.lft = e.lft + ?2 Where e.lft >= ?1";
    }

    public String getUpdateStmtForRight() {
    return "Update " + this.getClass().getName()
    + " e set e.rgt = e.rgt + ?2 Where e.rgt >= ?1";
    }
    .
    .
    .//Getter and setters for all the attributes.
    }
  • Создание классов аннотаций
    Ниже приведен класс аннотаций, который будет использоваться для аннотирования методов, выполняющих операции CRUD с иерархическими данными. За ним следует enum, который будет определять тип выполняемой операции CRUD.

    Эти занятия будут иметь больше смысла после следующего раздела.


    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface HierarchicalOperation {
    HierarchicalOperationType operationType();
    }

    /**
    * Enum - Type of CRUD operation.
    */

    public enum HierarchicalOperationType {
    SAVE, DELETE;
    }


  • Укажите Аспект, который будет выполнять дополнительные шаги для управления Иерархическими данными. HierarchicalEntity — это аспект, который выполняет дополнительную логику, необходимую для управления иерархическими данными, как описано в статье здесь .

    Я впервые использую Aspect, поэтому я уверен, что есть лучшие способы сделать это. Те из вас, кто хорош в этом, пожалуйста, улучшите эту часть кода.

    Этот класс помечен как @Aspect. Pointcut будет перехватывать любой метод, аннотированный с HierarchicalOperation, и имеет вход типа AbstractHierarchyEntity. Пример его использования находится в следующем разделе.

    Метод операции аннотируется для выполнения до pointcut. На основе переданного HierarchicalOperationType этот метод будет выполнять дополнительные задачи, необходимые для сохранения или удаления иерархической записи. Именно здесь используются методы, определенные в AbstractHierarchyEntity для генерации JPA-запросов.

    GenericDAOHelper — это служебный класс для использования JPA.


    import com.es.clms.annotation.HierarchicalOperation;
    import com.es.clms.annotation.HierarchicalOperationType;
    import com.es.clms.common.GenericDAOHelper;
    import com.es.clms.model.AbstractHierarchyEntity;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Service;
    import org.springframework.beans.factory.annotation.Autowired;

    @Aspect
    @Service("hierarchicalEntity")
    public class HierarchicalEntity {

    @Autowired
    private GenericDAOHelper genericDAOHelper;

    @Pointcut(value = "execution(@com.es.clms.annotation.HierarchicalOperation * *(..)) "
    + "&& args(AbstractHierarchyEntity)")
    private void hierarchicalOps() {
    }

    /**
    *
    * @param jp
    * @param hierarchicalOperation
    */
    @Before("hierarchicalOps() && @annotation(hierarchicalOperation) ")
    public void operation(final JoinPoint jp,
    final HierarchicalOperation hierarchicalOperation) {
    if (jp.getArgs().length != 1) {
    throw new IllegalArgumentException(
    "Expecting only one parameter of type AbstractHierarchyEntity in "
    + jp.getSignature());
    }
    if (HierarchicalOperationType.SAVE.equals(
    hierarchicalOperation.operationType())) {
    save(jp);
    } else if (HierarchicalOperationType.DELETE.equals(
    hierarchicalOperation.operationType())) {
    delete(jp);
    }
    }

    /**
    *
    * @param jp
    */
    private void save(JoinPoint jp) {

    AbstractHierarchyEntity entity =
    (AbstractHierarchyEntity) jp.getArgs()[0];
    if (entity == null)
    return;
    if (entity.getParentId() == null) {
    Long maxRight = (Long) genericDAOHelper.executeSingleResultQuery(
    entity.getMaxRightQuery());

    if (maxRight == null) {
    maxRight = 0L;
    }
    entity.setLft(maxRight + 1);
    entity.setRgt(maxRight + 2);
    } else {
    Long parentRight = (Long) genericDAOHelper.executeSingleResultQuery(
    entity.getQueryForParentRight(), entity.getParentId());

    entity.setLft(parentRight);
    entity.setRgt(parentRight + 1);

    genericDAOHelper.executeUpdate(
    entity.getUpdateStmtForFirst(), parentRight, 2L);
    genericDAOHelper.executeUpdate(
    entity.getUpdateStmtForRight(), parentRight, 2L);
    }
    }

    /**
    *
    * @param jp
    */
    private void delete(JoinPoint jp) {

    AbstractHierarchyEntity entity =
    (AbstractHierarchyEntity) jp.getArgs()[0];

    genericDAOHelper.executeUpdate(
    entity.getDeleteStmt(), entity.getLft(), entity.getRgt());

    Long width = (entity.getRgt() - entity.getLft()) + 1;

    genericDAOHelper.executeUpdate(
    entity.getUpdateStmtForFirst(), entity.getRgt(), width * (-1));
    genericDAOHelper.executeUpdate(
    entity.getUpdateStmtForRight(), entity.getRgt(), width * (-1));
    }
    }

  • Пример использования
    С этого момента вам не нужно беспокоиться о дополнительных задачах, необходимых для управления данными. Просто используйте аннотацию HierarchicalOperation с соответствующим HierarchicalOperationType.

    Ниже приведен пример использования кода, разработанного до сих пор.


    @HierarchicalOperation(operationType = HierarchicalOperationType.SAVE)
    public long save(VariableGroup group) {

    entityManager.persist(group);
    return group.getId();
    }


    @HierarchicalOperation(operationType = HierarchicalOperationType.DELETE)
    public void delete(VariableGroup group) {
    entityManager.remove(entityManager.merge(group));
    }

    http://rajeshkilango.blogspot.com/2010/10/manage-hierarchical-data-using-spring.html