Repositórios compostáveis de dados da Primavera
1. Introdução
Ao modelar um sistema ou processo do mundo real, os repositórios de estilo DDD (Domain-driven Design) são uma boa opção. Para esse propósito, podemos usar o Spring Data JPA como nossa camada de abstração de acesso a dados.
Se você é novo neste conceito, verifiquethis introductory tutorial para ajudá-lo a começar a trabalhar no mesmo ritmo.
Neste tutorial, vamos nos concentrar no conceito de criação de repositórios personalizados e composíveis, que são criados usando repositórios menores chamados fragmentos.
2. Dependências do Maven
A opção para criar repositórios composíveis está disponível a partir do Spring 5.
Vamos adicionar a dependência necessária para Spring Data JPA:
org.springframework.data
spring-data-jpa
2.0.9.RELEASE
Também precisaríamos configurar uma fonte de dados para que nossa camada de acesso a dados funcionasse. É uma boa ideiaset up an in-memory database like H2 para desenvolvimento e testes rápidos.
3. fundo
3.1. Hibernar como Implementação JPA
O Spring Data JPA, por padrão, usa o Hibernate como a implementação do JPA. Podemos facilmente confundir um com o outro ou compará-los, mas eles servem a propósitos diferentes.
Spring Data JPA é a camada de abstração de acesso a dados abaixo da qual podemos usar qualquer implementação. Poderíamos, por exemplo,switch out Hibernate in favor of EclipseLink.
3.2. Repositórios padrão
Em muitos casos, não precisaríamos escrever nenhuma consulta nós mesmos.
Em vez disso, precisamos apenas criar interfaces que, por sua vez, estendem as interfaces genéricas do repositório de dados Spring:
public interface LocationRepository extends JpaRepository {
}
E isso, por si só, nos permitiria fazer operações comuns - CRUD, paginação e classificação - no objetoLocation que tem uma chave primária do tipoLong.
Além disso, o Spring Data JPA vem equipado com um mecanismo de criação de consultas que fornece a capacidade de gerar consultas em nosso nome usando convenções de nomes de métodos:
public interface StoreRepository extends JpaRepository {
List findStoreByLocationId(Long locationId);
}
3.3. Repositórios personalizados
Se necessário, podemosenrich our model repository escrevendo uma interface de fragmento e implementando a funcionalidade desejada. Isso pode ser injetado em nosso próprio repositório JPA.
Por exemplo, aqui estamos enriquecendo nossoItemTypeRepository estendendo um repositório de fragmentos:
public interface ItemTypeRepository
extends JpaRepository, CustomItemTypeRepository {
}
Aqui,CustomItemTypeRepository é outra interface:
public interface CustomItemTypeRepository {
void deleteCustomById(ItemType entity);
}
Sua implementação pode ser um repositório de qualquer tipo, não apenas do JPA:
public class CustomItemTypeRepositoryImpl implements CustomItemTypeRepository {
@Autowired
private EntityManager entityManager;
@Override
public void deleteCustomById(ItemType itemType) {
entityManager.remove(itemType);
}
}
Precisamos apenas ter certeza de que ele possui o postfixImpl. No entanto, podemos definir um postfix personalizado usando a seguinte configuração XML:
ou usando esta anotação:
@EnableJpaRepositories(
basePackages = "com.example.repository", repositoryImplementationPostfix = "CustomImpl")
4. Compondo repositórios usando vários fragmentos
Até algumas versões atrás, só podíamos estender nossas interfaces de repositório usando uma única implementação personalizada. Essa era uma limitação por causa da qual teríamos que trazer todas as funcionalidades relacionadas em um único objeto.
Escusado será dizer que, para projetos maiores com modelos de domínio complexos, isso leva a classes inchadas.
Agora com o Spring 5, temos a opção deenrich our JPA repository with multiple fragment repositories. Novamente, permanece o requisito de termos esses fragmentos como pares de implementação de interface.
Para demonstrar isso, vamos criar dois fragmentos:
public interface CustomItemTypeRepository {
void deleteCustom(ItemType entity);
void findThenDelete(Long id);
}
public interface CustomItemRepository {
Item findItemById(Long id);
void deleteCustom(Item entity);
void findThenDelete(Long id);
}
Claro, precisaríamos escrever suas implementações. Mas, em vez de conectar esses repositórios customizados - com funcionalidades relacionadas - em seus próprios repositórios JPA, podemos estender a funcionalidade de um único repositório JPA:
public interface ItemTypeRepository
extends JpaRepository, CustomItemTypeRepository, CustomItemRepository {
}
Agora, teríamos todas as funcionalidades vinculadas em um único repositório.
5. Lidando com a ambiguidade
Como estamos herdando de vários repositórios, podemos ter problemas para descobrir quais de nossas implementações seriam usadas em caso de conflito. Por exemplo, em nosso exemplo, ambos os repositórios de fragmentos têm um método,findThenDelete(), com a mesma assinatura.
Nesse cenário,the order of the declaration of the interfaces is used to resolve the ambiguity. Consequentemente, em nosso caso, o método dentro deCustomItemTypeRepository será usado desde que seja declarado primeiro.
Podemos testar isso usando este caso de teste:
@Test
public void givenItemAndItemTypeWhenDeleteThenItemTypeDeleted() {
Optional itemType = composedRepository.findById(1L);
assertTrue(itemType.isPresent());
Item item = composedRepository.findItemById(2L);
assertNotNull(item);
composedRepository.findThenDelete(1L);
Optional sameItemType = composedRepository.findById(1L);
assertFalse(sameItemType.isPresent());
Item sameItem = composedRepository.findItemById(2L);
assertNotNull(sameItem);
}
6. Conclusão
Neste artigo, examinamos as diferentes maneiras pelas quais podemos usar os repositórios JPA do Spring Data. Vimos que o Spring simplifica a execução de operações de banco de dados em nossos objetos de domínio sem escrever muito código ou mesmo consultas SQL.
Esse suporte é consideravelmente personalizável através do uso de repositórios composíveis.
Os trechos de código deste artigo estão disponíveis comoMaven project here on Github.