O mapeamento de polimorfismo, introduzido na versão 2.0, é uma solução que permite a um entidade ser mapeada para mais de um tipo. É implementado com a anotação @Any. Este tipo de mapeamento requer que, além do mapeamento da entidade, seja informado o metabean, uma variável que contém o identificador da entidade associada. Pode ser usado em um bean nas propriedades ou nos argumentos de um construtor e em um controlador nas propriedades ou nos parâmetros de uma ação.
@Target({ElementType.METHOD,ElementType.PARAMETER,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Any {
Basic metaBean() default @Basic;
Class metaType() default String.class;
EnumerationType metaEnumerated() default EnumerationType.ORDINAL;
String metaTemporal() default BrutosConstants.DEFAULT_TEMPORALPROPERTY;
MetaValue[] metaValues() default {};
Class metaValuesDefinition() default MetaValuesDefinition.class;
}
- metaBean(): variável que contém os metadados.
- metaType(): tipo da variável que contém os metadados.
- metaEnumerated(): usado quando o metadado for uma enumeração.
- metaTemporal(): usado quando o metadado for uma data.
- metaValues(): associa um valor a um bean.
- metaValuesDefinition(): Permite em tempo de execução associar um valor a um bean.
Abaixo um exemplo de uso da anotação @Any em uma propriedade de um bean.
public class TestBean{
@Basic(bean="property")
@Any(
metaBean=@Basic(bean="property_type")
metaValues={
@MetaValue(name="Decimal", target=DecimalProperty.class),
@MetaValue(name="Set", target=SetProperty.class)
}
)
private Property property
...
}
Abaixo um exemplo de uso da anotação @Any em um construtor de um bean.
public class TestBean{
public TestBean(
@Basic(bean="property")
@Any(
metaBean=@Basic(bean="property_type")
metaValues={
@MetaValue(name="Decimal", target=DecimalProperty.class),
@MetaValue(name="Set", target=SetProperty.class)
}
)
Property property){
...
}
}
Abaixo um exemplo de uso da anotação @Any em uma propriedade de um controlador.
@Controller("/test")
public class TestController{
@Basic(bean="property")
@Any(
metaBean=@Basic(bean="property_type")
metaValues={
@MetaValue(name="Decimal", target=DecimalProperty.class),
@MetaValue(name="Set", target=SetProperty.class)
}
)
private Property property
...
}
Abaixo um exemplo de uso da anotação @Any em uma ação.
@Controller("/test")
public class TestController{
@Action("/")
public void testAction(
@Basic(bean="property")
@Any(
metaBean=@Basic(bean="property_type")
metaValues={
@MetaValue(name="Decimal", target=DecimalProperty.class),
@MetaValue(name="Set", target=SetProperty.class)
}
)
Property property){
...
}
}
Um exemplo da vida real seria um cenário de venda, onde um operador turístico vende diferentes serviços.
O exemplo irá permitir registrar um serviço aéreo ou de hospedagem por transação.
Dowload link: http://sourceforge.net/projects/brutos/files/brutos/2.0/examples/2.0-RC1/polymorphic-mapping.zip/download
Source code: http://sourceforge.net/p/brutos/code/HEAD/tree/trunk/examples/polymorphic-mapping/
Diagrama das entidades da aplicação.
Durante cada transação de venda, apenas um serviço será vendido. Podendo ser um serviço de hospedagem ou aéreo.
Classes
public class SaleTransaction {
private Long id;
@Transient
private Date date;
private Long price;
@Any(
metaBean =
@Basic(bean = "serviceType"),
metaType=
String.class,
metaValues = {
@MetaValue(name = "air", target = AirService.class),
@MetaValue(name = "hosting", target = HostingService.class)
}
)
private Service service;
//gets e sets
...
}
SaleTransaction.java
A anotação @Any indica que a propriedade service receberá uma instâncias da AirService, se o parâmetro serviceType for igual a "air", ou HostingServices, se o parâmetro serviceType for igual a "hosting".
public interface Service {
void setPrice(Long value);
Long getPrice();
String getServiceType();
}
Service.java
public abstract class AbstractService
implements Service{
protected Long price;
public void setPrice(Long value) {
this.price = value;
}
public Long getPrice() {
return this.price;
}
}
AbstractService.java
public class AirService extends AbstractService{
private String airplane;
private String seat;
@Temporal("yyyy-MM-dd hh:mm")
private Date departureDate;
@Temporal("yyyy-MM-dd hh:mm")
private Date arrivalDate;
public String getAirplane() {
return airplane;
}
//gets e sets
...
}
AirService.java
public class HostingService extends AbstractService{
private String hotel;
@Temporal("yyyy-MM-dd")
private Date checkin;
@Temporal("yyyy-MM-dd")
private Date checkout;
private String mealPlan;
private String room;
//gets e sets
...
}
HostingService.java
@Controller("/sales")
@Action(value="/new", view=@View("new"))
@RequestScoped
public class SalesController {
@Inject
@Transient
private SalesMemoryEntityAccess salesMemoryEntityAccess;
@Action("/save")
@Result("entity")
public SaleTransaction save(@Basic(bean="entity")
SaleTransaction entity){
this.salesMemoryEntityAccess.save(entity);
return entity;
}
...
}
SalesController.java
View
<form method="POST"
action="${pageContext.servletContext.contextPath}/sales/save">
<table>
<tbody>
<tr>
<td>Id</td>
<td>${entity.id}</td>
</tr>
<tr>
<td>Price</td>
<td><input type="text" name="entity.price"
value="${entity.price}"></td>
</tr>
<tr>
<td>Service type</td>
<!-- campo do metaBean serviceType -->
<td><select id="serviceType" name="entity.service.serviceType"
onchange="javascript:showServiceDataundefined)">
<option ${entity.service.serviceType == 'air'? "selected " : ""} value="air">Air</option>
<option ${entity.service.serviceType == 'hosting'? "selected " : ""} value="hosting">Hosting</option>
</select></td>
</tr>
<!-- inicio do formulário do serviço aéreo -->
<tr id="air">
<td>
<table width="100%">
<tbody>
<tr>
<td>Airplane</td>
<td><input type="text" name="entity.service.airplane"
value="${entity.service.serviceType != 'air'? null : entity.service.airplane}"></td>
</tr>
<tr>
<td>Departure date</td>
<td><input type="text" name="entity.service.departureDate"
value="<fmt:formatDate value="${entity.service.serviceType != 'air'? null : entity.service.departureDate}" pattern="yyyy-MM-dd hh:mm" />"></td>
</tr>
<tr>
<td>Arrival date</td>
<td><input type="text" name="entity.service.arrivalDate"
value="<fmt:formatDate value="${entity.service.serviceType != 'air'? null : entity.service.arrivalDate}" pattern="yyyy-MM-dd hh:mm" />"></td>
</tr>
</tbody>
</table>
</td>
</tr>
<!-- inicio do formulário do serviço de hospedagem -->
<tr id="hosting">
<td>
<table width="100%">
<tbody>
<tr>
<td>Hotel</td>
<td><input type="text" name="entity.service.hotel"
value="${entity.service.serviceType != 'hosting'? null : entity.service.hotel}"></td>
</tr>
<tr>
<td>Checkin</td>
<td><input type="text" name="entity.service.checkin"
value="<fmt:formatDate value="${entity.service.serviceType != 'hosting'? null : entity.service.checkin}" pattern="yyyy-MM-dd" />"></td>
</tr>
<tr>
<td>Checkout</td>
<td><input type="text" name="entity.service.checkout"
value="<fmt:formatDate value="${entity.service.serviceType != 'hosting'? null : entity.service.checkout}" pattern="yyyy-MM-dd" />"></td>
</tr>
<tr>
<td>Mealplan</td>
<td><input type="text" name="entity.service.mealPlan"
value="${entity.service.serviceType != 'hosting'? null : entity.service.mealPlan}"></td>
</tr>
<tr>
<td>Room</td>
<td><input type="text" name="entity.service.room"
value="${entity.service.serviceType != 'hosting'? null : entity.service.room}"></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td><c:if test="${!empty entity.id}">
<input type="hidden" name="entity.id" value="${entity.id}">
</c:if> <input type="submit" value="Submit"></td>
</tr>
</tfoot>
</table>
</form>
http://www.brutosframework.com.br/
