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