当前位置:

首页 > 编程开发 > Hybris注册页添加自定义属性方法

Hybris注册页添加自定义属性方法

本文旨在解决Hybris电商平台在注册页添加自定义属性(如“Pan”号)时遇到的数据持久化问题。通过详细阐述ModelSavingException的根源及optional="true"修饰符的局限性,文章将指导读者如何正确地扩展和修改Hybris的关键组件,包括RegisterForm、RegisterData、RegistrationPageController和CustomerFacade,以确保用户输入的新字段值能完整地从前端传递到后端模型并成功存储到数据库中。

Hybris注册页添加自定义属性并持久化

本文旨在解决Hybris电商平台在注册页添加自定义属性(如“Pan”号)时遇到的数据持久化问题。通过详细阐述ModelSavingException的根源及optional="true"修饰符的局限性,文章将指导读者如何正确地扩展和修改Hybris的关键组件,包括RegisterForm、RegisterData、RegistrationPageController和CustomerFacade,以确保用户输入的新字段值能完整地从前端传递到后端模型并成功存储到数据库中。

1. 问题背景与根源分析

在Hybris注册流程中,当尝试在CustomerModel上添加一个新的强制性属性(例如Pan,即个人识别码),并在注册表单中收集该值时,常常会遇到de.hybris.platform.servicelayer.exceptions.ModelSavingException: missing values for [pan]错误。这个错误表明在创建新的CustomerModel实例时,其强制性的pan属性没有被赋值。

即使尝试通过在items.xml中将该属性设置为optional="true"来规避上述错误,虽然可以成功注册,但Pan值却无法被存储到数据库中。这说明问题不仅仅在于属性的强制性,更在于数据从前端用户界面到后端数据模型的完整传递链条中断。核心问题在于,用户在前端表单中输入的值,没有正确地经过Hybris的MVC层级,最终映射并保存到CustomerModel实例中。

2. 解决方案:扩展Hybris核心组件

要彻底解决此问题,需要确保新添加的Pan属性值能够贯穿Hybris的整个数据流,从UI表单到最终的CustomerModel。这涉及到对以下核心组件的扩展和修改:

  • items.xml:定义CustomerModel上的新属性。
  • RegisterForm:承载前端表单提交的数据。
  • RegisterData:作为数据传输对象(DTO),在控制器和门面之间传递数据。
  • RegistrationPageController:处理注册请求,将表单数据绑定到RegisterForm,并传递给CustomerFacade。
  • CustomerFacade:作为业务逻辑的入口,负责将RegisterData中的值映射到CustomerModel并进行保存。

2.1 定义CustomerModel上的新属性 (items.xml)

首先,确保在你的items.xml文件中正确定义了CustomerModel上的pan属性。如果该属性是业务必需的,则不应将其设置为optional="true"。



    
        
            
                
                    Customer's Permanent Account Number (PAN).
                    
                    
                    
                
            
        
    

注意事项:

  • 在添加新属性后,需要运行ant all并启动Hybris服务器,然后执行系统更新(platform update),以确保数据库模式更新并识别新属性。
  • 如果pan是强制性的,请不要添加optional="true"修饰符,否则将无法触发ModelSavingException,但数据也无法被强制输入。

2.2 扩展RegisterForm

RegisterForm是Spring MVC用来绑定前端表单数据的Java Bean。你需要在此类中添加pan字段,以接收来自HTML表单的输入。

// Assuming your extension extends the core Hybris storefront
// e.g., mystorefront/src/com/mycompany/storefront/forms/MyRegisterForm.java
package com.mycompany.storefront.forms;

import de.hybris.platform.acceleratorstorefrontcommons.forms.RegisterForm;
import javax.validation.constraints.NotEmpty; // Example validation

public class MyRegisterForm extends RegisterForm {

    @NotEmpty(message = "{register.pan.invalid}") // Add appropriate validation
    private String pan;

    public String getPan() {
        return pan;
    }

    public void setPan(String pan) {
        this.pan = pan;
    }
}

注意事项:

  • 确保在你的JSP/Thymeleaf注册表单中,输入字段的name属性与MyRegisterForm中的pan字段名称匹配。例如:
  • 你可以根据需要添加JSR 303/349验证注解(如@NotEmpty, @Pattern等)。

2.3 扩展RegisterData

RegisterData是一个数据传输对象(DTO),用于在控制器和服务层之间传递注册信息。同样,需要在此DTO中添加pan字段。

// e.g., mycore/src/com/mycompany/core/data/MyRegisterData.java
package com.mycompany.core.data;

import de.hybris.platform.commercefacades.user.data.RegisterData;

public class MyRegisterData extends RegisterData {

    private String pan;

    public String getPan() {
        return pan;
    }

    public void setPan(String pan) {
        this.pan = pan;
    }
}

2.4 修改RegistrationPageController

RegistrationPageController负责处理注册页面的请求和表单提交。你需要修改它以使用你的自定义MyRegisterForm和MyRegisterData,并确保数据从表单正确复制到DTO。

通常,你会通过Spring的@Controller注解覆盖或扩展现有的控制器。

// e.g., mystorefront/src/com/mycompany/storefront/controllers/pages/MyRegistrationPageController.java
package com.mycompany.storefront.controllers.pages;

import com.mycompany.storefront.forms.MyRegisterForm;
import com.mycompany.core.data.MyRegisterData; // Your custom RegisterData
import de.hybris.platform.acceleratorstorefrontcommons.controllers.pages.Abstract='de.hybris.platform.acceleratorstorefrontcommons.controllers.pages.AbstractRegisterPageController';
import de.hybris.platform.cms2.exceptions.CMSItemNotFoundException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.validation.BindingResult;
import org.springframework.ui.Model;
import javax.validation.Valid;

@Controller("myRegistrationPageController")
@RequestMapping(value = "/register")
public class MyRegistrationPageController extends AbstractRegisterPageController {

    // You might need to override the initBinder or other methods
    // to ensure MyRegisterForm is used for binding.

    @RequestMapping(method = RequestMethod.POST)
    public String doRegister(@Valid final MyRegisterForm form, final BindingResult bindingResult, final Model model)
            throws CMSItemNotFoundException {

        // If you're extending, call super.doRegister() or copy its logic
        // and adapt it to use MyRegisterForm and MyRegisterData.

        // Example of adapting the data transfer
        final MyRegisterData registerData = new MyRegisterData();
        registerData.setFirstName(form.getFirstName());
        registerData.setLastName(form.getLastName());
        registerData.setEmail(form.getEmail());
        registerData.setPassword(form.getPassword());
        registerData.setTitleCode(form.getTitleCode());
        registerData.setConsentGiven(form.isConsentGiven());
        // *** Crucial: Copy your new field from form to data ***
        registerData.setPan(form.getPan());

        // Now, pass your custom registerData to the customerFacade
        // This part usually involves calling a method like register(registerData)
        // on the customerFacade.
        // If super.doRegister handles this, ensure it's overridden to accept MyRegisterData.

        // Example of calling the facade (simplified)
        // try {
        //     getCustomerFacade().register(registerData);
        //     // Handle success
        //     return REDIRECT_PREFIX + "/register/success";
        // } catch (final DuplicateUidException e) {
        //     // Handle duplicate email
        //     bindingResult.rejectValue("email", "register.email.duplicate");
        //     return get  Controller().register(model, form); // Return to form with error
        // }

        // For a full override, you'd replicate the logic from the original doRegister method,
        // replacing RegisterForm with MyRegisterForm and RegisterData with MyRegisterData.
        return super.doRegister(form, bindingResult, model); // If super method can be adapted
    }

    // You might need to override the getRegisterForm() method as well if it's used
    // to instantiate the form in GET requests.
    @Override
    protected MyRegisterForm getRegisterForm() {
        return new MyRegisterForm();
    }
}

注意事项:

  • 你需要确保你的Spring配置(如web-spring.xml)将你的自定义控制器设置为处理/register路径,而不是默认的Hybris控制器。这通常通过Bean定义中的id或name属性来完成。
  • 最重要的是,确保form.getPan()的值被正确地设置到registerData.setPan()中。

2.5 扩展CustomerFacade

CustomerFacade是业务逻辑层面的接口,它将RegisterData转换为CustomerModel并保存。这是将Pan值最终映射到CustomerModel的关键步骤。通常,CustomerFacade会使用一个或多个Populator来完成数据映射。

// e.g., myfacades/src/com/mycompany/facades/customer/impl/MyCustomerFacade.java
package com.mycompany.facades.customer.impl;

import com.mycompany.core.data.MyRegisterData; // Your custom RegisterData
import de.hybris.platform.commercefacades.customer.impl.DefaultCustomerFacade;
import de.hybris.platform.core.model.user.CustomerModel;
import de.hybris.platform.servicelayer.model.ModelService;
import de.hybris.platform.servicelayer.user.UserService;
import org.springframework.beans.factory.annotation.Required;

public class MyCustomerFacade extends DefaultCustomerFacade {

    private ModelService modelService;
    private UserService userService; // Inject if needed

    @Override
    public void register(final MyRegisterData registerData) {
        final CustomerModel newCustomer = modelService.create(CustomerModel.class);
        // Populate common fields (first name, last name, email, password, etc.)
        // You might use a populator chain here, or manually map.

        // Example: Manually mapping
        newCustomer.setUid(registerData.getEmail());
        newCustomer.setName(registerData.getFirstName() + " " + registerData.getLastName());
        newCustomer.setCustomerID(registerData.getEmail()); // Or generate unique ID
        newCustomer.setSessionCurrency(getCommonI18NService().getCurrentCurrency());
        newCustomer.setSessionLanguage(getCommonI18NService().getCurrentLanguage());
        newCustomer.setLoginDisabled(false);
        newCustomer.setEncodedPassword(getUserService().encodePassword(registerData.getPassword(), newCustomer.getSalt()));

        // *** Crucial: Set your new field on the CustomerModel ***
        newCustomer.setPan(registerData.getPan());

        // Save the customer model
        modelService.save(newCustomer);

        // Optionally, authenticate the newly registered user
        // getCustomerFacade().loginSuccess();
    }

    @Required
    public void setModelService(final ModelService modelService) {
        this.modelService = modelService;
    }

    @Required
    public void setUserService(final UserService userService) {
        this.userService = userService;
    }
}

最佳实践:使用Populator 在更复杂的场景中,推荐使用Hybris的Populator机制来映射数据。你可以创建一个新的RegisterDataToCustomerPopulator来处理MyRegisterData到CustomerModel的映射。

  1. 定义Populator接口:

    // myfacades/src/com/mycompany/facades/populators/MyRegisterDataToCustomerPopulator.java
    package com.mycompany.facades.populators;
    
    import com.mycompany.core.data.MyRegisterData;
    import de.hybris.platform.converters.Populator;
    import de.hybris.platform.core.model.user.CustomerModel;
    import de.hybris.platform.servicelayer.dto.converter.ConversionException;
    
    public class MyRegisterDataToCustomerPopulator implements Populator {
    
        @Override
        public void populate(final MyRegisterData source, final CustomerModel target) throws ConversionException {
            // Populate common fields if this populator is the primary one
            // target.setUid(source.getEmail());
            // target.setName(source.getFirstName() + " " + source.getLastName());
    
            // Populate your new field
            target.setPan(source.getPan());
        }
    }
  2. 在Spring配置中注册Populator并将其注入到CustomerFacade中:

    
    
    
    
        
        
            
                
                
                
                
            
        
    
    
    
        
        
    

    注意事项:

    • 确保你的CustomerFacade(MyCustomerFacade)被配置为使用这个包含你自定义Populator的Converter。
    • 如果MyCustomerFacade继承自DefaultCustomerFacade,并且DefaultCustomerFacade已经使用了registerConverter,那么你只需要覆盖其register方法以使用你的MyRegisterData,并确保registerConverter(如果你使用了Populator)被正确配置以包含你的myRegisterDataToCustomerPopulator。

3. 部署与测试

完成上述代码修改后,需要进行以下步骤:

  1. ant all:编译你的Hybris项目。
  2. 启动Hybris服务器:运行hybrisserver.bat或hybrisserver.sh。
  3. 执行系统更新:登录Hybris Administration Console (HAC),导航到Platform -> Update,勾选你的扩展,并执行Update running system。这会确保items.xml中的新属性被正确添加到数据库模式中。
  4. 清理缓存:在HAC中执行Clear all caches。
  5. 测试:访问你的注册页面,填写包括Pan在内的所有信息,尝试注册。检查是否成功注册,并通过HAC或数据库工具验证新注册用户的CustomerModel中Pan属性是否已正确存储。

4. 总结

在Hybris中添加自定义属性并确保其数据持久化,需要对整个数据流进行端到端的管理。从items.xml定义属性,到前端表单收集数据,再到RegisterForm、RegisterData、RegistrationPageController和CustomerFacade(或其背后的Populator机制)的层层传递和映射,每一步都至关重要。遵循上述步骤,可以有效解决ModelSavingException并确保自定义属性值的正确存储。

本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
编程开发
相关文章 更多
C++动态数组初始化怎么写?常用语句与代码示例
C++动态数组初始化怎么写?常用语句与代码示例

深入解析C++中动态数组的初始化机制,涵盖new操作符的不同用法、基本类型与类对象的初始化差异,以及为何在现代C++开发中应优先使用std::vector。

using namespace 使用中遇到的问题怎么解决
using namespace 使用中遇到的问题怎么解决

命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,

c语言函数递归 实操经验总结:这些技巧很实用
c语言函数递归 实操经验总结:这些技巧很实用

理解递归的基本原理在C语言中,递归是一种函数调用自身的编程技术。要掌握它,首先需要理解其核心思想:将一个复杂的大问题,分解为一个或几个与原问题相似但规模更小的子问题,直到子问题足够简单,可以直接求解。这个过程通常包含两个关键部分:递归出口和递归体。递归出口定义了问题何时不再继续分解,即最简单、可直接

c语言函数递归 怎么选?常见方案对比分析
c语言函数递归 怎么选?常见方案对比分析

递归函数的基本概念与适用场景在C语言编程中,递归是一种函数调用自身的编程技巧。它并非适用于所有问题,但在处理某些具有自相似结构的问题时,能提供极其清晰和优雅的解决方案。递归的核心思想是将一个大规模问题分解为一个或多个同类型但规模更小的子问题,直到子问题简单到可以直接求解。典型的适用场景包括树形结构的

Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解
Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解

理解内存管理的基石在Objective-C的编程世界中,内存管理是开发者必须掌握的核心技能之一。它直接关系到应用的性能、稳定性与资源利用效率。与一些采用自动垃圾回收机制的语言不同,Objective-C在很长一段时间里,依赖一套基于引用计数的、需要开发者部分介入的管理规则。这套规则的核心思想是明确的

如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏
如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏

理解 dealloc 的角色与时机在 iOS 应用开发中,内存管理是保障应用性能与稳定性的基石。dealloc 方法是 Objective-C 中对象生命周期结束时的关键回调,它标志着对象即将被系统回收内存。正确理解其触发时机至关重要:当一个对象的引用计数降为零时,运行时系统会自动调用该对象的 de

深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制
深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制

内存管理的基石在Objective-C的世界里,内存管理是开发者必须掌握的核心技能之一。作为一门在手动引用计数(MRC)时代诞生的语言,Objective-C要求程序员对对象的生命周期有清晰的认识。dealloc方法正是这一生命周期中至关重要的终点站。它是一个实例方法,当对象的引用计数降为零时,系统

理解 native2ascii:Java 国际化开发中的字符编码工具
理解 native2ascii:Java 国际化开发中的字符编码工具

native2ascii 工具的基本定位在Ja va应用程序的国际化与本地化开发过程中,处理非拉丁字符集是一个常见且关键的环节。Ja va内部使用Unicode字符集来统一表示全球各种语言的文字,但其属性文件(.properties)在历史上要求使用ASCII编码,或者更准确地说,要求非ASCII字

如何使用 native2ascii 转换中文字符为 Unicode 转义序列
如何使用 native2ascii 转换中文字符为 Unicode 转义序列

理解 native2ascii 工具的基本用途在软件开发,特别是涉及国际化处理的场景中,开发者常常需要处理不同编码的文本资源。native2ascii 是 Ja va 开发工具包(JDK)中提供的一个命令行实用程序,其主要功能是将包含本地字符编码(非ASCII字符)的文件,转换为包含 Unicode

Java native2ascii 命令详解:解决属性文件乱码问题
Java native2ascii 命令详解:解决属性文件乱码问题

native2ascii 命令的由来与作用在Ja va开发中,处理国际化资源文件是一个常见需求。资源文件通常以.properties格式存储,用于支持多语言界面。然而,Ja va属性文件默认采用ISO-8859-1字符集编码,这导致了一个直接的问题:当文件中包含非拉丁字符(如中文、日文、韩文等)时,

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师
macOS

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

WINDOWS 更多
Windows 10
Windows 10
Windows

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘
Windows/macOS/iOS/Android

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。