当您在hybris的items.xml中为customermodel添加一个新的属性(例如pan),并将其设置为optional="false"(即强制属性)时,如果在注册流程中没有正确地将该属性的值从前端传递并设置到customermodel实例上,系统在尝试保存customermodel时就会抛出de.hybris.platform.servicelayer.exceptions.modelsavingexception: missing values for [pan]错误。
即使您为了避免上述错误而将属性设置为optional="true",虽然错误不再出现,但您会发现该属性的值并未被保存到数据库中。这表明仅仅在items.xml中定义属性和在JSP页面上添加输入框是不足够的,Hybris的注册流程需要一个完整的数据流转机制来处理新的字段。
Hybris的注册流程是一个多层架构,数据从用户界面(UI)经过控制器(Controller)、数据传输对象(DTO)、门面(Facade)最终到达服务层(Service)和数据模型(Model)。要成功添加并保存自定义属性,必须在数据流的每个关键节点进行相应的扩展。
具体而言,您需要修改或扩展以下核心组件:
首先,在您的Hybris扩展的items.xml文件中,为Customer类型添加新的属性。例如,添加一个名为pan的字符串类型属性:
PAN card number for the customer
注意: 如果pan字段是强制的,请保持optional="false"。后续步骤将确保其值被正确传递,从而避免ModelSavingException。
完成items.xml修改后,请务必执行ant all并更新系统(update running system)。
RegisterForm是用于绑定注册页面表单数据的POJO类。您需要扩展它以包含新的pan字段。
// 在您的核心或自定义扩展中创建或修改
// 例如:yourstorefront/web/src/com/yourcompany/storefront/forms/YourRegisterForm.java
package com.yourcompany.storefront.forms;
import de.hybris.platform.acceleratorstorefrontcommons.forms.RegisterForm; // 导入Hybris的RegisterForm
public class YourRegisterForm extends RegisterForm { // 继承Hybris的RegisterForm
private String pan;
public String getPan() {
return pan;
}
public void setPan(String pan) {
this.pan = pan;
}
// 您可以添加验证注解,例如 @NotEmpty, @Pattern 等
}RegisterData是用于在Web层和Facade层之间传递注册信息的DTO。它也需要包含pan字段。
// 在您的核心或自定义扩展中创建或修改
// 例如:yourcore/src/com/yourcompany/core/data/YourRegisterData.java
package com.yourcompany.core.data;
import de.hybris.platform.commercefacades.user.data.RegisterData; // 导入Hybris的RegisterData
public class YourRegisterData extends RegisterData { // 继承Hybris的RegisterData
private String pan;
public String getPan() {
return pan;
}
public void setPan(String pan) {
this.pan = pan;
}
}RegistrationPageController负责处理注册表单的提交。在这里,您需要将YourRegisterForm中的pan值映射到YourRegisterData中。通常,您会覆盖或扩展Hybris提供的默认控制器。
// 在您的storefront扩展中创建或修改
// 例如:yourstorefront/web/src/com/yourcompany/storefront/controllers/pages/YourRegistrationPageController.java
package com.yourcompany.storefront.controllers.pages;
import com.yourcompany.core.data.YourRegisterData;
import com.yourcompany.storefront.forms.YourRegisterForm;
import de.hybris.platform.acceleratorstorefrontcommons.controllers.pages.Abstract='yourstorefront'.controllers.pages.RegisterPageController; // 导入Hybris的RegisterPageController
import de.hybris.platform.acceleratorstorefrontcommons.forms.RegisterForm;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import javax.validation.Valid;
@Controller
@RequestMapping(value = "/register")
public class YourRegistrationPageController extends RegisterPageController {
// 覆盖默认的注册方法,使用您的自定义表单和数据对象
@RequestMapping(method = RequestMethod.POST)
public String doRegister(@Valid final YourRegisterForm form, final BindingResult bindingResult, final Model model)
throws CMSItemNotFoundException {
// ... (其他验证和逻辑,通常继承自父类)
if (bindingResult.hasErrors()) {
// 处理验证错误
return getViewForPage(model); // 返回注册页面
}
final YourRegisterData registerData = new YourRegisterData();
// 使用ModelMapper或手动复制属性
// 示例:手动复制,推荐使用ModelMapper或其他BeanUtils
registerData.setFirstName(form.getFirstName());
registerData.setLastName(form.getLastName());
registerData.setLogin(form.getEmail());
registerData.setPassword(form.getPwd());
registerData.setTitleCode(form.getTitleCode());
registerData.setPan(form.getPan()); // <-- 关键:将pan从表单复制到数据对象
try {
getCustomerFacade().register(registerData); // 调用CustomerFacade进行注册
} catch (final DuplicateUidException e) {
// 处理重复用户ID异常
bindingResult.rejectValue("email", "registration.error.account.exists.title");
return getViewForPage(model);
}
// ... (注册成功后的逻辑,例如自动登录,重定向)
return REDIRECT_PREFIX + get==''/register/success';
}
}注意: 您可能需要配置Spring来使用YourRegisterForm而不是默认的RegisterForm。这通常通过Spring的@Controller注解和@RequestMapping来实现,或者在Spring配置中覆盖默认的RegisterPageController Bean。
这是最关键的一步,负责将RegisterData中的pan值映射到CustomerModel实例上,并调用服务层进行持久化。您需要创建一个新的CustomerFacade实现或扩展现有的实现。
// 在您的core扩展中创建或修改 // 例如:yourcore/src/com/yourcompany/core/facades/customer/impl/YourCustomerFacadeImpl.java package com.yourcompany.core.facades.customer.impl; import com.yourcompany.core.data.YourRegisterData; import de.hybris.platform.commercefacades.user.impl.DefaultCustomerFacade; // 导入Hybris的DefaultCustomerFacade import de.hybris.platform.core.model.user.CustomerModel; import de.hybris.platform.servicelayer.model.ModelService; import org.springframework.beans.factory.annotation.Required; public class YourCustomerFacadeImpl extends DefaultCustomerFacade { private ModelService modelService; // 注入ModelService @Override public void register(final RegisterData registerData) throws DuplicateUidException { // 确保传入的是您的YourRegisterData类型 if (!(registerData instanceof YourRegisterData)) { super.register(registerData); // 如果不是,调用父类方法处理 return; } final YourRegisterData yourRegisterData = (YourRegisterData) registerData; // 核心逻辑:在创建CustomerModel之前或之后设置pan值 final CustomerModel customerModel = getModelService().create(CustomerModel.class); customerModel.setUid(yourRegisterData.getLogin()); customerModel.setName(yourRegisterData.getFirstName() + " " + yourRegisterData.getLastName()); customerModel.setSessionCurrency(getCommonI18NService().getCurrentCurrency()); customerModel.setSessionLanguage(getCommonI18NService().getCurrentLanguage()); customerModel.setPan(yourRegisterData.getPan()); // <-- 关键:设置pan值到CustomerModel // 设置其他标准属性(通常在父类或通过BeanUtils复制) populateCustomerModel(yourRegisterData, customerModel); getModelService().save(customerModel); // 保存CustomerModel // ... (其他注册后的逻辑,例如自动登录) } // 也可以选择覆盖populateCustomerModel方法,将pan字段映射进去 @Override protected void populateCustomerModel(final RegisterData registerData, final CustomerModel customerModel) { super.populateCustomerModel(registerData, customerModel); // 调用父类方法处理通用属性 if (registerData instanceof YourRegisterData) { final YourRegisterData yourRegisterData = (YourRegisterData) registerData; customerModel.setPan(yourRegisterData.getPan()); // 确保pan被设置 } } @Required public void setModelService(final ModelService modelService) { this.modelService = modelService; } }
注意: 您需要在Spring配置文件中将默认的customerFacade Bean替换为您的YourCustomerFacadeImpl。
在Hybris注册页面添加自定义属性并实现其持久化,不仅仅是在items.xml中定义字段和在JSP页面上添加输入框那么简单。它要求开发者深入理解Hybris的数据流机制,并对RegisterForm、RegisterData、RegistrationPageController和CustomerFacade等关键组件进行恰当的扩展。通过遵循本文提供的步骤和最佳实践,您可以有效地解决ModelSavingException和数据无法保存的问题,确保自定义属性在Hybris平台中得到完整且正确的处理。