DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
11 Monitoring and Observability Tools for 2023
Learn more
  1. DZone
  2. Data Engineering
  3. Data
  4. Manage Hierarchical Data using Spring, JPA and Aspects

Manage Hierarchical Data using Spring, JPA and Aspects

Rajesh Ilango user avatar by
Rajesh Ilango
·
Oct. 21, 10 · Interview
Like (0)
Save
Tweet
Share
15.57K Views

Join the DZone community and get the full member experience.

Join For Free

Managing hierarchical data using two dimentional tables is a pain. There are some patterns to reduce this pain. One such solution is described here. This article is about implementing the same using Spring, JPA, Annotations and Aspects. Please go through follow the link to better understand this solution described. The purpose is to come up with a component that will remove the boiler-plate code in the business layer to handle hierarchical data.
Summary

  • Create a base class for Entities used to represent Hierarchical data
  • Create annotation classes
  • Code the Aspect that will execute addional steps for managing Hierarchical data. (Heart of the solution)
  • Now the Aspect can be used everywhere Hierarchical data is used.

Detail
  • Create base class for Entities used to represent Hierarchical data.
    The purpose of the super class is to encapsulate all the common attrubutes and operations required for managing hierarchical data in a table. Please note that the class is annotated as @MappedSuperclass.

    The methods are meant to generate queries required to perform CRUD operations on the Table. Their use will be more clear later in the article when we will revisit HierarchicalEntity.

    Now any Entity that extends this class will have all the attributes required to manage hierarchical data.
    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.
    }
  • Create annotation classes
    The following is an annotation class that will be used to annotate the methods that perform CRUD operations on hierarchical data. It is followed by an enum that will decide the type of CRUD operation to be performed.

    These classes will make more sense after the next section.


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


  • Code the Aspect that will execute addional steps for managing Hierarchical data. HierarchicalEntity is an aspect that performs the additional logic required to manage the hierarchical data as descriped in the article here.

    This is the first time I have used an Aspect, therefore I am sure that there are better ways to do this. Those of you, who are good at it, please improve this part of code.

    This class is annotated as @Aspect. The pointcut will intercept any method anotated with HierarchicalOperation and has a input of type AbstractHierarchyEntity. A sample its usage is in next section.

    operation method is annotated to be executed before the pointcut. Based on the HierarchicalOperationType passed, this method will either execute the additional tasks required to save or delete the hierarchical record. This is where the methods defined in AbstractHierarchyEntity for generating JPA Queries are used.

    GenericDAOHelper is a utility class for using 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));
    }
    }

  • Sample Usage
    From this point on you don't have to worry about the additional tasks required for managing the data. Just use the HierarchicalOperation anotation with appropriate HierarchicalOperationType.

    Below is a sample use of the code developed so far.


    @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
Data (computing) Aspect (computer programming) Database Spring Framework

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • How To Handle Secrets in Docker
  • The Power of Zero-Knowledge Proofs: Exploring the New ConsenSys zkEVM
  • Authenticate With OpenID Connect and Apache APISIX
  • Running Databases on Kubernetes

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: