问题描述
我的问题实际上是这个问题的衍生如此处所示... 因此在继续之前检查该线程可能会有所帮助.
My question is actually a spin-off of this question as seen here... so it might help to check that thread before proceeding.
在我的 Spring Boot 项目中,我有两个实体 Sender
和 Recipient
代表一个 Customer
并且几乎具有相同的字段,所以我让它们扩展基类 Customer
;
In my Spring Boot project, I have two entities Sender
and Recipient
which represent a Customer
and pretty much have the same fields, so I make them extend the base class Customer
;
Customer 基类;
Customer base class;
@MappedSuperclass
public class Customer extends AuditableEntity {
@Column(name = "firstname")
private String firstname;
@Transient
private CustomerRole role;
public Customer(CustomerRole role) {
this.role = role;
}
//other fields & corresponding getters and setters
}
发送方域对象;
Sender domain object;
@Entity
@Table(name = "senders")
public class Sender extends Customer {
public Sender(){
super.setRole(CustomerRole.SENDER);
}
}
收件人域对象;
Recipient domain object;
@Entity
@Table(name = "recipients")
public class Recipient extends Customer {
public Recipient(){
super.setRole(CustomerRole.RECIPIENT);
}
}
注意 - Sender
和 Recipient
完全相同,只是它们的角色不同.通过使 Customer
基类本身成为实体,这些可以很容易地存储在单个 customers 表中,但我故意以这种方式将实体分开,因为我有义务坚持每个实体客户类型在单独的数据库表中.
NOTE - Sender
and Recipient
are exactly alike except for their roles. These can be easily stored in a single customers Table by making the Customer
base class an entity itself, but I intentionally separate the entities this way because I have an obligation to persist each customer type in separate database tables.
现在我在一个视图中有一个表单,它收集了 Sender
和 Sender
的详细信息.Recipient
,例如为了收集名字,我不得不对表单字段进行不同的命名,如下所示;
Now I have one form in a view that collects details of both Sender
& Recipient
, so for example to collect the firstname, I had to name the form fields differently as follows;
表单的发件人部分;
<input type="text" id="senderFirstname" name="senderFirstname" value="$!sender.firstname">
表单的收件人部分;
<input type="text" id="recipientFirstname" name="recipientFirstname" value="$!recipient.firstname">
但是可供客户使用的字段太多了,以至于我正在寻找一种方法,通过 这个问题在这里.但是,解决方案提供了那里这意味着我必须为两个域对象创建单独的代理并相应地注释字段,例如
But the fields available for a customer are so many that I'm looking for a way to map them to a pojo by means of an annotation as asked in this question here. However, the solutions provided there would mean that I have to create separate proxies for both domain objects and annotate the fields accordingly e.g
public class SenderProxy {
@ParamName("senderFirstname")
private String firstname;
@ParamName("senderLastname")
private String lastname;
//...
}
public class RecipientProxy {
@ParamName("recipientFirstname")
private String firstname;
@ParamName("recipientLastname")
private String lastname;
//...
}
所以我很好奇并且想知道,有没有办法将这个代理映射到多个@ParamName,例如基类可以只注释如下?;
So I got very curious and was wondering, is there a way to map this Proxies to more than one @ParamName such that the base class for example can just be annotated as follows?;
@MappedSuperclass
public class Customer extends AuditableEntity {
@Column(name = "firstname")
@ParamNames({"senderFirstname", "recipientFirstname"})
private String firstname;
@Column(name = "lastname")
@ParamNames({"senderLastname", "recipientLastname"})
private String lastname;
@Transient
private CustomerRole role;
public Customer(CustomerRole role) {
this.role = role;
}
//other fields & corresponding getters and setters
}
然后也许可以找到一种基于注释选择字段值的方法??
And then perhaps find a way to select value of fields based on annotation??
推荐答案
来自张杰的建议 like ExtendedBeanInfo
所以我是这样做的
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Alias {
String[] value();
}
public class AliasedBeanInfoFactory implements BeanInfoFactory, Ordered {
@Override
public BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
return supports(beanClass) ? new AliasedBeanInfo(Introspector.getBeanInfo(beanClass)) : null;
}
private boolean supports(Class<?> beanClass) {
Class<?> targetClass = beanClass;
do {
Field[] fields = targetClass.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Alias.class)) {
return true;
}
}
targetClass = targetClass.getSuperclass();
} while (targetClass != null && targetClass != Object.class);
return false;
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 100;
}
}
public class AliasedBeanInfo implements BeanInfo {
private static final Logger LOGGER = LoggerFactory.getLogger(AliasedBeanInfo.class);
private final BeanInfo delegate;
private final Set<PropertyDescriptor> propertyDescriptors = new TreeSet<>(new PropertyDescriptorComparator());
AliasedBeanInfo(BeanInfo delegate) {
this.delegate = delegate;
this.propertyDescriptors.addAll(Arrays.asList(delegate.getPropertyDescriptors()));
Class<?> beanClass = delegate.getBeanDescriptor().getBeanClass();
for (Field field : findAliasedFields(beanClass)) {
Optional<PropertyDescriptor> optional = findExistingPropertyDescriptor(field.getName(), field.getType());
if (!optional.isPresent()) {
LOGGER.warn("there is no PropertyDescriptor for field[{}]", field);
continue;
}
Alias alias = field.getAnnotation(Alias.class);
addAliasPropertyDescriptor(alias.value(), optional.get());
}
}
private List<Field> findAliasedFields(Class<?> beanClass) {
List<Field> fields = new ArrayList<>();
ReflectionUtils.doWithFields(beanClass,
fields::add,
field -> field.isAnnotationPresent(Alias.class));
return fields;
}
private Optional<PropertyDescriptor> findExistingPropertyDescriptor(String propertyName, Class<?> propertyType) {
return propertyDescriptors
.stream()
.filter(pd -> pd.getName().equals(propertyName) && pd.getPropertyType().equals(propertyType))
.findAny();
}
private void addAliasPropertyDescriptor(String[] values, PropertyDescriptor propertyDescriptor) {
for (String value : values) {
if (!value.isEmpty()) {
try {
this.propertyDescriptors.add(new PropertyDescriptor(
value, propertyDescriptor.getReadMethod(), propertyDescriptor.getWriteMethod()));
} catch (IntrospectionException e) {
LOGGER.error("add field[{}] alias[{}] property descriptor error", propertyDescriptor.getName(),
value, e);
}
}
}
}
@Override
public BeanDescriptor getBeanDescriptor() {
return this.delegate.getBeanDescriptor();
}
@Override
public EventSetDescriptor[] getEventSetDescriptors() {
return this.delegate.getEventSetDescriptors();
}
@Override
public int getDefaultEventIndex() {
return this.delegate.getDefaultEventIndex();
}
@Override
public PropertyDescriptor[] getPropertyDescriptors() {
return this.propertyDescriptors.toArray(new PropertyDescriptor[0]);
}
@Override
public int getDefaultPropertyIndex() {
return this.delegate.getDefaultPropertyIndex();
}
@Override
public MethodDescriptor[] getMethodDescriptors() {
return this.delegate.getMethodDescriptors();
}
@Override
public BeanInfo[] getAdditionalBeanInfo() {
return this.delegate.getAdditionalBeanInfo();
}
@Override
public Image getIcon(int iconKind) {
return this.delegate.getIcon(iconKind);
}
static class PropertyDescriptorComparator implements Comparator<PropertyDescriptor> {
@Override
public int compare(PropertyDescriptor desc1, PropertyDescriptor desc2) {
String left = desc1.getName();
String right = desc2.getName();
for (int i = 0; i < left.length(); i++) {
if (right.length() == i) {
return 1;
}
int result = left.getBytes()[i] - right.getBytes()[i];
if (result != 0) {
return result;
}
}
return left.length() - right.length();
}
}
}
这篇关于绑定spring mvc命令对象时如何将多个参数名称映射到POJO的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!