Esfinge Comparasion - Comparação de Instâncias


Utilizando a Comparação de Instâncias
Configurando a Comparação de Instâncias
- @IgnoreInComparison : ignora o atributo anotado, não incluindo-o na comparação. Deve ser utilizado para campos que normalmente não são persistidos ou não são importantes relacionados ao negócio.
- @DeepComparison : executa o algoritmo de comparação de forma recursiva para a propriedade. Devem ser utilizados para propriedades complexas, que possuem propriedades dentro delas. Exemplo: caso a classe Pessoa possua uma instância da classe Endereco com essa propriedade, o algoritmo irá entrar compara cada propriedade dos endereços.
- @Tolerance: configura a tolerância numérica permitida para o campo. Deve ser usado em campos do tipo ponto flutuante onde podem haver diferenças de arredondamento.
- @CompareSubstring: compara apenas parte da string iniciada no atributo begin e terminada no atributo end da anotação. Deve ser usado em propriedades onde apenas parte da informação é relevante na comparação.
public class Person{
private String name;
private double weight;
private int age;
private Address address;
@DeepComparison
public Address getAddress() {
return address;
}
public void setAddress(Address adddress) {
this.address = adddress;
}
@CompareSubstring(begin=3)
public String getName() {
return name;
}
public void setName(String nome) {
this.name = nome;
}
@Tolerance(0.1)
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
@IgnoreInComparison
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Invocando a Comparação
public class Test {
public static void main(String[] args) throws CompareException {
Person p1 = new Person("Sr. Zé",70.5f,20);
Address e1 = new Address("Pariquis","50");
p1.setAddress(e1);
Person p2 = new Person("Dr. Zé",70.7f,21);
Address e2 = new Address("Pariquis","55");
p2.setAddress(e2);
ComparisonComponent c = new ComparisonComponent();
List difs = c.compare(p2, p1);
for(Difference d : difs){
System.out.println(d);
}
}
}
No caso, o retorno desse código será:
weight:70.69999694824219/70.5
address.number:55/50
Relacionamentos Circulares
Configurações do Esfinge Comparison
Camadas de Comparação
- NullComparisonLayer: realiza a comparação quando pelo menos um dos valores a serem comparados é nulo.
- DeepComparisonLayer: realiza a comparação quando deve ser feita uma comparação profunda entre os valores da propriedade.
- CollectionItensComparisonLayer: realiza a comparação quando a propriedade é uma coleção de objetos simples ou complexos.
- ValueComparisonLayer: realiza a comparação simples de valores, utilizando o ComparisonProcessor configurado para aquela propriedade se for o caso (será mostrado mais a frente como fazer essa configuração)
public abstract class ComparisonLayer {
private ComparisonComponent component;
public abstract boolean compare(Object oldValue, Object newValue, List difs,
PropertyDescriptor descProp) throws CompareException ;
public ComparisonComponent getComponent() {
return component;
}
public void setComponent(ComparisonComponent component) {
this.component = component;
}
}
Leitores de Metadados
public interface ComparisonMetadataReader {
public abstract void populateContainer(Class c, ComparisonDescriptor descriptor);
}
ChainComparisonMetatataReader chainReader =
new ChainComparisonMetatataReader(
new AnnotationComparisonMetadataReader(),
new JPAComparisonMetadataReader()
);
MetadataReaderProvider.set(chainReader);
Usando os Metadados do JPA
- @Transiente: pelo fato de uma propriedade transiente não ser persistida, uma mudança nela não representa uma mudança persistente da entidade em questão. Por esse motivo, toda propriedade com a anotação @Transient será ignorada na comparação.
- @Entity: essa anotação em uma classe a configura como uma classe persistente. Para o componente de comparação, uma propriedade com um tipo que possua essa anotação, representa uma propriedade composta, a qual o algoritmo de comparação deve ser executado de forma recursiva. Em outras palavras, uma propriedade com um tipo anotado com @Entity é o mesmo que possuir a anotação @DeepComparison.
- @Id e @EmbededId: essas anotações são utilizadas para identificar a propriedade responsável pela identidade da entidade. É utilizada na comparação de listas, para identificar quais são os elementos equivalentes nas duas listas no momento da comparação.
Criando Novas Anotações de Comparação
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@DelegateReader(SubstringComparisonReader.class)
public @interface CompareSubstring {
int begin() default 0;
int end() default Integer.MAX_VALUE;
}
public class SubstringComparisonReader implements AnnotationReader {
@Override
public void readAnnotation(CompareSubstring annotation, PropertyDescriptor descriptor) {
int begin = annotation.begin();
int end = annotation.end();
SubstringProcessor p = new SubstringProcessor(begin,end);
descriptor.setProcessor(p);
}
}
public class SubstringProcessor implements ComparisonProcessor {
private int begin;
private int end;
public SubstringProcessor(int begin, int end) {
this.begin = begin;
this.end = end;
}
@Override
public Difference compare(String prop, Object oldValue, Object newValue) {
if (newValue == null) {
if (oldValue != null) {
return new PropertyDifference(prop, newValue, oldValue);
}
} else{
String oldString, newString;
if(end == Integer.MAX_VALUE){
oldString = oldValue.toString().substring(begin);
newString = newValue.toString().substring(begin);
}else{
oldString = oldValue.toString().substring(begin, end);
newString = newValue.toString().substring(begin, end);
}
if(!oldString.equals(newString))
return new PropertyDifference(prop, newValue, oldValue);
}
return null;
}
}
Lidando com Listas
O Funcionamento da Comparação de Listas
Na comparação de coleções com objetos complexos, o método equals() não é mais utilizado para identificar itens que representam a mesma entidade. Nesse caso, é procurada na classe da entidade uma propriedade anotada com @Id ou @EmbededId que será utilizada para identificação da entidade.
Caminhos das Propriedades em Listas
Criando Testes da Comparação
public class TesteComaparacaoPessoa {
@BeforeClass
public static void configuraReader(){
ChainComparisonMetatataReader chainReader =
new ChainComparisonMetatataReader(
new AnnotationComparisonMetadataReader(),
new JPAComparisonMetadataReader()
);
MetadataReaderProvider.set(chainReader);
}
@Test
public void testePropriedadeSimples() throws CompareException{
Person p1 = new Person("José", 80, 23);
Person p2 = new Person("José", 83, 23);
ComparisonComponent cc = new ComparisonComponent();
List list = cc.compare(p1, p2);
assertEquals("weight", list.get(0).getPath());
assertEquals(83.0, ((PropertyDifference)list.get(0)).getNewValue());
assertEquals(80.0, ((PropertyDifference)list.get(0)).getOldValue());
}
@Test
public void testePropriedadeComposta() throws CompareException{
Person p1 = new Person("José", 80, 23);
Address e1 = new Address("Pariquis","50");
p1.setAddress(e1);
Person p2 = new Person("José", 80, 23);
Address e2 = new Address("Pariquis","55");
p2.setAddress(e2);
ComparisonComponent cc = new ComparisonComponent();
List list = cc.compare(p1, p2);
assertEquals("address.number", list.get(0).getPath());
assertEquals("55", ((PropertyDifference)list.get(0)).getNewValue());
assertEquals("50", ((PropertyDifference)list.get(0)).getOldValue());
}
}
Apoio

