Validação de entidade, bloqueio otimista e consistência de consulta no Spring Data Couchbase
*1. Introdução *
Após nosso link:/spring-data-couchbase [introdução ao Spring Data Couchbase], neste segundo tutorial, focaremos no suporte à validação de entidade (JSR-303), bloqueio otimista e níveis diferentes de consistência de consulta para um banco de dados de documentos do Couchbase .
===* 2. Validação de entidade *
O Spring Data Couchbase fornece suporte para anotações de validação de entidade JSR-303. Para aproveitar esse recurso, primeiro adicionamos a biblioteca JSR-303 à seção de dependências do nosso projeto Maven:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
Em seguida, adicionamos uma implementação do JSR-303. Usaremos a implementação do Hibernate:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.4.Final</version>
</dependency>
Por fim, adicionamos um bean de fábrica validador e o ouvinte de evento Couchbase correspondente à nossa configuração do Couchbase:
@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
@Bean
public ValidatingCouchbaseEventListener validatingCouchbaseEventListener() {
return new ValidatingCouchbaseEventListener(localValidatorFactoryBean());
}
A configuração XML equivalente é assim:
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean id="validatingEventListener"
class="org.springframework.data.couchbase.core.mapping.event.ValidatingCouchbaseEventListener"/>
Agora adicionamos anotações JSR-303 às nossas classes de entidade. Quando uma violação de restrição é encontrada durante uma operação de persistência, a operação falhará, lançando uma ConstraintViolationException.
Aqui está uma amostra das restrições que podemos aplicar envolvendo nossas entidades Student:
@Field
@NotNull
@Size(min=1, max=20)
@Pattern(regexp="^[a-zA-Z .'-]+$")
private String firstName;
...
@Field
@Past
private DateTime dateOfBirth;
===* 3. Bloqueio otimista *
O Spring Data Couchbase não suporta transações com vários documentos semelhantes às que você pode obter em outros módulos do Spring Data, como o Spring Data JPA (por meio da anotação _ @ Transactional_), nem fornece um recurso de reversão.
No entanto, ele suporta bloqueio otimista da mesma maneira que outros módulos Spring Data através do uso da anotação _ @ Version_:
@Version
private long version;
Nos bastidores, o Couchbase usa o mecanismo conhecido como “comparar e trocar” (CAS) para obter um bloqueio otimista no nível do armazenamento de dados.
Cada documento no Couchbase possui um valor CAS associado, que é modificado automaticamente sempre que os metadados ou o conteúdo do documento são alterados. O uso da anotação _ @ Version_ em um campo faz com que esse campo seja preenchido com o valor CAS atual sempre que um documento for recuperado do Couchbase.
Quando você tenta salvar o documento de volta no Couchbase, esse campo é verificado em relação ao valor atual do CAS no Couchbase. Se os valores não corresponderem, a operação de persistência falhará com um OptimisticLockingException.
É* extremamente importante *observar que você nunca deve tentar acessar ou modificar esse campo no seu código.
===* 4. Consistência da consulta *
Ao implementar uma camada de persistência no Couchbase, é necessário considerar a possibilidade de leituras e gravações antigas. Isso ocorre porque, quando os documentos são inseridos, atualizados ou excluídos, pode levar algum tempo até que as exibições e os índices de backup sejam atualizados para refletir essas alterações.
E se você tiver um grande conjunto de dados apoiado por um cluster de nós do Couchbase, isso pode se tornar um problema significativo, especialmente para um sistema OLTP.
O Spring Data fornece um nível robusto de consistência para algumas operações de repositório e modelo, além de algumas opções que permitem determinar o nível de consistência de leitura e gravação aceitável para seu aplicativo.
====* 4.1 Níveis de consistência *
O Spring Data permite que você especifique vários níveis de consistência e robustez da consulta para seu aplicativo por meio da enumeração Consistency encontrada no pacote org.springframework.data.couchbase.core.query.
Essa enum define os seguintes níveis de consistência e robustez da consulta, do menos ao mais rigoroso:
-
EVENTUALLY_CONSISTENT
-
leituras obsoletas são permitidas
-
índices são atualizados de acordo com o algoritmo padrão do Couchbase
-
UPDATE_AFTER
-
leituras obsoletas são permitidas
-
índices são atualizados após cada solicitação
-
DEFAULT_CONSISTENCY (o mesmo que READ_YOUR_OWN_WRITES)
-
READ_YOUR_OWN_WRITES
-
leituras obsoletas não são permitidas
-
índices são atualizados após cada solicitação
-
STRONGLY_CONSISTENT
-
leituras obsoletas não são permitidas *índices são atualizados após cada instrução
====* 4.2 Comportamento padrão *
Considere o caso em que você possui documentos que foram excluídos do Couchbase e as visualizações e índices de backup não foram totalmente atualizados.
O método interno couchbaseRepository _deleteAll () _ ignora com segurança os documentos encontrados pela exibição de backup, mas cuja exclusão ainda não é refletida pela exibição.
Da mesma forma, os métodos internos CouchbaseTemplate findByView e findBySpatialView oferecem um nível semelhante de consistência ao não retornar documentos que foram inicialmente encontrados pela exibição de backup, mas que foram excluídos desde então.
Para todos os outros métodos de modelo, métodos de repositório interno e métodos de consulta de repositório derivados, de acordo com a documentação oficial do Spring Data Couchbase 2.1.x até a data desta redação, o Spring Data usa um nível de consistência padrão de Consistency.READ_YOUR_OWN_WRITES.
Vale ressaltar que as versões anteriores da biblioteca usavam o padrão Consistency.UPDATE_AFTER.
Qualquer que seja a versão que você esteja usando, se você tiver alguma dúvida sobre aceitar cegamente o nível de consistência padrão fornecido, o Spring oferece dois métodos pelos quais você pode controlar declarativamente o (s) nível (s) de consistência que estão sendo usados, como as subseções a seguir descreverão.
====* 4.3 Configuração de consistência global *
Se você estiver usando repositórios do Couchbase e seu aplicativo exigir um nível mais forte de consistência, ou se puder tolerar um nível mais fraco, poderá substituir a configuração de consistência padrão para todos os repositórios, substituindo o método _getDefaultConsistency () _ na sua configuração do Couchbase.
Aqui está como você pode substituir o nível de consistência global na sua classe de configuração do Couchbase:
@Override
public Consistency getDefaultConsistency() {
return Consistency.STRONGLY_CONSISTENT;
}
Aqui está a configuração XML equivalente:
<couchbase:template consistency="STRONGLY_CONSISTENT"/>
Observe que o preço de níveis mais rigorosos de consistência aumenta a latência no momento da consulta, portanto, adapte essa configuração com base nas necessidades do seu aplicativo.
Por exemplo, um armazém de dados ou aplicativo de relatório no qual os dados geralmente são anexados ou atualizados apenas em um lote seria um bom candidato para EVENTUALLY_CONSISTENT, enquanto um aplicativo OLTP provavelmente deve tender para níveis mais rigorosos, como READ_YOUR_OWN_WRITES ou STRONGLY_CONSISTENT.
====* 4.4 Implementação de consistência personalizada *
Se você precisar de configurações de consistência mais refinadas, poderá substituir o nível de consistência padrão consulta por consulta, fornecendo sua própria implementação de repositório para todas as consultas cujo nível de consistência você deseja controlar independentemente e usando o queryView e/ou queryN1QL métodos fornecidos por CouchbaseTemplate.
Vamos implementar um método de repositório personalizado chamado findByFirstNameStartsWith para nossa entidade Student para a qual não queremos permitir leituras antigas.
Primeiro, crie uma interface contendo a declaração do método personalizado:
public interface CustomStudentRepository {
List<Student> findByFirstNameStartsWith(String s);
}
Em seguida, implemente a interface, definindo a configuração Stale do Java SDK do Couchbase subjacente para o nível desejado:
public class CustomStudentRepositoryImpl implements CustomStudentRepository {
@Autowired
private CouchbaseTemplate template;
public List<Student> findByFirstNameStartsWith(String s) {
return template.findByView(ViewQuery.from("student", "byFirstName")
.startKey(s)
.stale(Stale.FALSE),
Student.class);
}
}
Por fim, ao fazer com que sua interface de repositório padrão estenda a interface CrudRepository genérica e sua interface de repositório customizada, os clientes terão acesso a todos os métodos internos e derivados de sua interface de repositório padrão, além de quaisquer métodos personalizados implementados em sua classe de repositório personalizada :
public interface StudentRepository extends CrudRepository<Student, String>,
CustomStudentRepository {
...
}
===* 5. Conclusão*
Neste tutorial, mostramos como implementar a validação de entidade JSR-303 e obter um recurso de bloqueio otimista ao usar o projeto da comunidade Spring Data Couchbase.
Também discutimos a necessidade de entender a consistência das consultas no Couchbase e introduzimos os diferentes níveis de consistência fornecidos pelo Spring Data Couchbase.
Por fim, explicamos os níveis de consistência padrão usados globalmente pelo Spring Data Couchbase e para alguns métodos específicos, e demonstramos maneiras de substituir a configuração de consistência padrão global e como substituir as configurações de consistência consulta por consulta, fornecendo suas próprias implementações de repositório customizado.
Você pode visualizar o código fonte completo deste tutorial em o projeto GitHub.
Para saber mais sobre o Spring Data Couchbase, visite o site oficial do projeto Spring Data Couchbase.