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

> Learn to implement product attribute and category management using Spring Boot REST endpoints MyBatis services and DAO layers for efficient e-commerce operations in the Mall project.

- Repository: [macro/mall](https://github.com/macrozheng/mall)
- Tags: how-to-guide
- Published: 2026-02-28

---

**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`](https://github.com/macrozheng/mall/blob/main/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

```java
@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`](https://github.com/macrozheng/mall/blob/main/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):

```java
@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:

```java
@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`](https://github.com/macrozheng/mall/blob/main/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`:

```java
@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:

```java
@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`:

```java
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.