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
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

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

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Enterprise RIA With Spring 3, Flex 4 and GraniteDS
  • How To Get Closer to Consistency in Microservice Architecture
  • Improving Backend Performance Part 1/3: Lazy Loading in Vaadin Apps
  • High-Performance Batch Processing Using Apache Spark and Spring Batch

Trending

  • A Guide to Developing Large Language Models Part 1: Pretraining
  • Transforming AI-Driven Data Analytics with DeepSeek: A New Era of Intelligent Insights
  • How to Convert XLS to XLSX in Java
  • It’s Not About Control — It’s About Collaboration Between Architecture and Security
  1. DZone
  2. Data Engineering
  3. Data
  4. Manage Hierarchical Data using Spring, JPA and Aspects

Manage Hierarchical Data using Spring, JPA and Aspects

By 
Rajesh Ilango user avatar
Rajesh Ilango
·
Oct. 21, 10 · Interview
Likes (0)
Comment
Save
Tweet
Share
16.6K 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.

Related

  • Enterprise RIA With Spring 3, Flex 4 and GraniteDS
  • How To Get Closer to Consistency in Microservice Architecture
  • Improving Backend Performance Part 1/3: Lazy Loading in Vaadin Apps
  • High-Performance Batch Processing Using Apache Spark and Spring Batch

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

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

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!