How to Implement Product Attribute and Category Management in the Mall Project

To implement product attribute and category management in the Mall project, use Spring Boot controllers to expose REST endpoints, MyBatis services to handle business logic like counter updates and hierarchical tree building, and DAO/Mapper layers for persistence and complex queries.

The Mall project by macrozheng is a comprehensive e-commerce platform built on Spring Boot and MyBatis that demonstrates how to implement product attribute and category management through a clean layered architecture. Understanding this implementation provides a blueprint for building scalable catalog management systems in Java enterprise applications.

Architecture Overview

The system follows a classic Spring Boot + MyBatis architecture with clear separation of concerns:

  • Controller Layer: Exposes REST APIs in mall-admin, handles input validation, and returns standardized CommonResult responses
  • Service Layer: Encapsulates business logic, manages transactions, and handles complex operations like counter updates and tree construction
  • DAO/Mapper Layer: Combines MyBatis Generator mappers for CRUD operations with custom DAOs for complex join queries

Implementing Product Attribute Management

Product attributes in the Mall project represent specifications (like color, size) and parameters (like weight, material) associated with products. The implementation spans three layers with specific responsibilities.

REST API Endpoints (PmsProductAttributeController)

The controller layer in mall-admin/src/main/java/com/macro/mall/controller/PmsProductAttributeController.java exposes endpoints for attribute lifecycle management:

  • GET /productAttribute/list/{cid} – Paginated list by category ID and type
  • POST /productAttribute/create – Create new attribute
  • POST /productAttribute/update/{id} – Update existing attribute
  • GET /productAttribute/{id} – Fetch single attribute details
  • POST /productAttribute/delete – Batch deletion by IDs
  • GET /productAttribute/attrInfo/{productCategoryId} – Retrieve attribute info with relations
@ApiOperation("添加商品属性信息")
@RequestMapping(value = "/create", method = RequestMethod.POST)
@ResponseBody
public CommonResult create(@RequestBody PmsProductAttributeParam productAttributeParam) {
    int count = productAttributeService.create(productAttributeParam);
    return count > 0 ? CommonResult.success(count) : CommonResult.failed();
}

Business Logic Implementation (PmsProductAttributeServiceImpl)

The service layer in mall-admin/src/main/java/com/macro/mall/service/impl/PmsProductAttributeServiceImpl.java handles transactional business rules.

Creating Attributes with Counter Updates: When creating an attribute, the service copies the DTO to the entity, inserts it, then updates the parent category counters (attributeCount or paramCount depending on type):

@Override
public int create(PmsProductAttributeParam pmsProductAttributeParam) {
    PmsProductAttribute pmsProductAttribute = new PmsProductAttribute();
    BeanUtils.copyProperties(pmsProductAttributeParam, pmsProductAttribute);
    int count = productAttributeMapper.insertSelective(pmsProductAttribute);

    // Update attribute-category counters
    PmsProductAttributeCategory category = productAttributeCategoryMapper
            .selectByPrimaryKey(pmsProductAttribute.getProductAttributeCategoryId());
    if (pmsProductAttribute.getType() == 0) {
        category.setAttributeCount(category.getAttributeCount() + 1);
    } else {
        category.setParamCount(category.getParamCount() + 1);
    }
    productAttributeCategoryMapper.updateByPrimaryKey(category);
    return count;
}

Pagination and Filtering: The list query uses PageHelper for pagination and builds dynamic SQL criteria:

@Override
public List<PmsProductAttribute> getList(Long cid, Integer type,
                                          Integer pageSize, Integer pageNum) {
    PageHelper.startPage(pageNum, pageSize);
    PmsProductAttributeExample example = new PmsProductAttributeExample();
    example.setOrderByClause("sort desc");
    example.createCriteria()
           .andProductAttributeCategoryIdEqualTo(cid)
           .andTypeEqualTo(type);
    return productAttributeMapper.selectByExample(example);
}

Data Access Layer

Generated Mappers: PmsProductAttributeMapper in mall-mbg provides standard CRUD operations via MyBatis Generator.

Custom DAO: PmsProductAttributeDao in mall-admin handles complex queries like getProductAttrInfo, which performs joins across attribute, category, and relation tables to return ProductAttrInfo DTOs.

Implementing Product Category Management

Product categories support hierarchical nesting (parent-child relationships) and optional binding to filter attributes.

Category CRUD Operations (PmsProductCategoryController)

Located in mall-admin/src/main/java/com/macro/mall/controller/PmsProductCategoryController.java, this controller manages category hierarchies:

  • POST /productCategory/create – Create category with optional attribute binding
  • GET /productCategory/list/{parentId} – List categories by parent
  • GET /productCategory/list/withChildren – Retrieve full tree structure
  • POST /productCategory/delete/{id} – Remove category
  • Batch status updates for navStatus and showStatus

Hierarchical Tree and Level Management

Level Calculation: The PmsProductCategoryServiceImpl computes category levels automatically. Root categories have level 0, children inherit parentLevel + 1:

@Override
public int create(PmsProductCategoryParam param) {
    PmsProductCategory category = new PmsProductCategory();
    category.setProductCount(0);
    BeanUtils.copyProperties(param, category);
    setCategoryLevel(category);  // Sets level based on parent
    return productCategoryMapper.insertSelective(category);
}

Tree Retrieval: The listWithChildren() method delegates to PmsProductCategoryDao.listWithChildren() to build recursive category trees using MyBatis result mappings:

@Override
public List<PmsProductCategoryWithChildrenItem> listWithChildren() {
    return productCategoryDao.listWithChildren();
}

Managing Category-Attribute Relations

Categories can bind to filter attributes for faceted search. The service handles this through insertRelationList:

List<Long> attrIds = param.getProductAttributeIdList();
if (!CollectionUtils.isEmpty(attrIds)) {
    insertRelationList(category.getId(), attrIds);
}

During updates, the service rewrites the relation table (delete-then-insert pattern) and updates related product names in the PmsProduct table to maintain referential consistency.

Summary

Implementing product attribute and category management in the Mall project requires understanding these key architectural patterns:

  • Layered Architecture: Controllers handle HTTP traffic, services encapsulate business rules, and MyBatis mappers/DAOs manage persistence
  • Counter Pattern: Attribute categories maintain running counts of attributes and parameters that increment on create and decrement on delete
  • Hierarchical Modeling: Categories use self-referential parent IDs with computed level fields to support tree structures
  • Relation Management: Many-to-many relationships between categories and filter attributes use dedicated relation tables with batch insert/delete operations
  • Pagination Strategy: PageHelper integration provides consistent pagination across list endpoints

Frequently Asked Questions

How does the Mall project handle pagination for product attributes?

The implementation uses PageHelper in the service layer. Before executing the query, the service calls PageHelper.startPage(pageNum, pageSize), then builds a PmsProductAttributeExample with filter criteria. MyBatis intercepts the query to inject LIMIT clauses automatically, and the controller wraps results in CommonPage for standardized JSON responses.

What is the difference between product attributes and parameters in the Mall system?

In PmsProductAttribute, the type field distinguishes them: type 0 represents specification attributes used for product variants (e.g., color, size), while type 1 represents parameters used for product descriptions (e.g., weight, material). The system maintains separate counters (attributeCount vs paramCount) in PmsProductAttributeCategory for each type.

How does the category hierarchy level calculation work?

The setCategoryLevel() method in PmsProductCategoryServiceImpl checks the parentId. If null or 0, it sets level to 0 (root). Otherwise, it queries the parent category and sets level = parent.getLevel() + 1. This denormalized level field optimizes tree traversal queries and prevents recursive database calls during reads.

Where are the complex join queries for attribute information defined?

Complex queries that join multiple tables (attribute, attribute category, and category relations) reside in PmsProductAttributeDao rather than the generated mapper. This DAO contains the getProductAttrInfo method that returns ProductAttrInfo DTOs, keeping generated mappers clean while supporting specialized read operations for the admin interface.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →