🗃️ A Camada de Repositório (`@Repository`)
Acesso Antecipado (Beta)Esta documentação refere-se a uma versão em acesso antecipado do SDK Sankhya. As funcionalidades e APIs estão sujeitas a modificações. Para obter acesso, envie um e-mail para [email protected] informando a
appkeydo seu projeto.
🗃️ A Camada de Repositório (@Repository)
@Repository)O padrão Repository no SDK Sankhya é uma abstração poderosa que simplifica drasticamente o acesso a dados. Ele permite que você defina consultas de forma declarativa, sem escrever uma única linha de implementação, e promove um código limpo, seguro e fácil de manter.
🤔 Por que usar Repositórios?
- ✅ Implementação Automática: Você define a interface, e o SDK escreve o código de acesso a dados para você em tempo de compilação.
- ✅ CRUD Gratuito: Métodos como
save(),findById(),findAll()edelete()já vêm prontos para uso. - ✅ Consultas Declarativas: Escreva consultas complexas com anotações (
@Criteria), utilizando parametros nomeados. - ✅ Segurança de Tipos (Type-Safety): Chega de "strings mágicas". Parâmetros e retornos são fortemente tipados, e erros são pegos em tempo de compilação, não em execução.
- ✅ Código Limpo e Testável: A camada de acesso a dados fica completamente isolada, permitindo que seus serviços sejam testados facilmente com repositórios mock.
📊 Abordagem Antiga vs. Nova
❌ Abordagem Tradicional (DAO com Jape)
O padrão DAO (Data Access Object) com Jape manual é verboso, propenso a erros de digitação e vulnerável a SQL Injection.
// Código legado: verboso, inseguro e difícil de manter
public class VeiculoDAO {
public Optional<Veiculo> buscarPorPlaca(String placa) throws Exception {
JapeWrapper dao = JapeFactory.dao("Veiculo");
// Concatenação de strings é perigosa e propensa a erros
Collection<DynamicVO> vos = dao.find("PLACA = '" + placa + "'");
if (vos.isEmpty()) {
return Optional.empty();
}
// Conversão manual de DynamicVO para POJO
DynamicVO vo = vos.iterator().next();
Veiculo veiculo = new Veiculo();
veiculo.setId(vo.asLong("CODVEICULO"));
veiculo.setPlaca(vo.asString("PLACA"));
return Optional.of(veiculo);
}
}✅ Nova Abordagem com @Repository
@RepositoryCom o SDK, você apenas define a interface. O código é limpo, seguro e declarativo.
// Código moderno: seguro, declarativo e sem boilerplate
@Repository
public interface VeiculoRepository extends JapeRepository<Veiculo, Long> {
@Criteria("ativo = :ativo AND v.marca = :nomeMarca")
List<Veiculo> findAtivosPorMarca(
@Parameter("ativo") boolean ativo,
@Parameter("nomeMarca") String nomeMarca
);
@Criteria(clause = "ATIVO = :ativo")
Page<Veiculo> findByAtivoPaginado(Boolean ativo, Pageable pageable);
}
// Uso no serviço:
@Service
public class VeiculoService {
private final VeiculoRepository repository;
@Inject
public VeiculoService(VeiculoRepository repository) {
this.repository = repository;
}
public Optional<Veiculo> buscarPorPlaca(String placa) {
// Simples, direto e seguro.
return repository.findByPlaca(placa);
}
public Page<Veiculo> buscarAtivosPaginados(String nomeMarca) {
//Objeto de paginação número da página, tamanho da página e argumentos para paginação
PageRequest pageRequest = PageRequest.of(0, 10, Sort.by("NOME", Direction.DESC), Sort.by("PLACA", Direction.DESC));
return repository.findByAtivoPaginado(nomeMarca, pageRequest);
}
}📝 Como Criar um Repositório
Um repositório no SDK é uma interface que estende JapeRepository<T, ID>.
T: A classe da sua entidade (ex:Veiculo).ID: O tipo da chave primária da sua entidade (ex:Long).
import br.com.sankhya.sdk.data.repository.JapeRepository;
import br.com.sankhya.studio.stereotypes.Repository;
@Repository
public interface VeiculoRepository extends JapeRepository<Veiculo, Long> {
// Seus métodos de consulta customizados virão aqui
}Métodos CRUD Padrão
Ao estender JapeRepository, sua interface herda automaticamente os seguintes métodos:
| Método | Descrição |
|---|---|
save(T entity) | Salva uma nova entidade ou atualiza uma existente. |
findById(ID id) | Busca uma entidade pela sua chave primária. Retorna Optional<T>. |
findAll() | Retorna todas as instâncias da entidade. |
findAll(Pageable pageable) | Retorna uma página de resultados com ordenação. Retorno Page<T> |
delete(T entity) | Remove uma entidade do banco de dados. |
🔍 Criando Consultas Customizadas
@Criteria
A anotação @Criteria permite definir consultas de busca (SELECT) personalizadas usando condições SQL na cláusula WHERE.
Características Principais
- Declarativa: Define a consulta através de anotação
- Parâmetros Nomeados: Vincula parâmetros do método à query
- Flexível: Suporta funções SQL e macros Sankhya
- Type-Safe: Retorno tipado (entidade ou lista de entidades)
Sintaxe Básica
@Criteria(clause = "CAMPO = :parametro")
ReturnType nomeDoMetodo(TipoParametro parametro);@NativeQuery
A anotação @NativeQuery permite executar consultas SQL nativas diretamente em métodos de repositório, oferecendo controle total sobre o SQL utilizado. Ela é ideal para cenários complexos, de alta performance ou que não podem ser representados facilmente por abstrações como @Criteria.
Quando Usar
- Consultas complexas: JOINs múltiplos, subqueries, window functions e outras operações específicas do banco de dados.
- Otimização de performance: Quando é necessário escrever uma consulta altamente otimizada.
- Relatórios e agregações: Consultas que realizam cálculos complexos (GROUP BY, HAVING) e retornam DTOs personalizados.
Características Principais
- SQL nativo: Permite escrever consultas completas e específicas do banco.
- Segurança: Suporte a parâmetros nomeados (
:nome) para evitar SQL Injection. - Mapeamento flexível: Conversão automática para interfaces (DTOs), listas ou tipos primitivos Java (
String,Boolean,Long, etc.). - Gerenciamento de transações: Integração com
JdbcWrapperpara reutilização de conexões existentes. - Sankhya Macros: É possível utilizar Macros Sankhya (ex:
dbDate()) como em qualquer outro lugar.
Sintaxe Básica
@NativeQuery("SELECT CAMPO1, CAMPO2, CAMPO_3, CAMPO4 AS CAMPOCOMALIAS FROM MINHA_TABELA WHERE CAMPO = :parametro")
ReturnType nomeDoMetodo(TipoParametro parametro);Tipos de Retorno
A anotação @NativeQuery pode retornar tanto interfaces quanto tipos Java simples.
- Interfaces
Interfaces que representam o resultado da consulta devem ser anotadas com @NativeQuery.Result. Cada método deve seguir o padrão de getter (getCampo) e corresponder exatamente ao nome da coluna ou alias retornado.
@NativeQuery.Result
public interface MinhaInterface {
String getCampo1();
String getCampo2();
String getCampo_3();
String getCampoComAlias();
}Consulta correspondente:
@NativeQuery("SELECT CAMPO1, CAMPO2, CAMPO_3, CAMPO4 AS CAMPOCOMALIAS FROM MINHA_TABELA WHERE CAMPO = :parametro")
MinhaInterface buscarUm(TipoParametro parametro);
@NativeQuery("SELECT CAMPO1, CAMPO2, CAMPO_3, CAMPO4 AS CAMPOCOMALIAS FROM MINHA_TABELA")
List<MinhaInterface> buscarTodos();Atenção: o nome dos getters deve coincidir exatamente com os nomes (ou aliases) das colunas. Qualquer divergência resultará em
null.
- Tipos Java
Atenção: Os tipos de retornos das funções do repositório deve sempre ser os tipos Wrapper Java (ex: Integer, Long, String, Boolean, etc.). Tipos primitivos não são suportados.
Também é possível retornar tipos Java diretamente:
@NativeQuery("SELECT CAMPO1 FROM MINHA_TABELA WHERE CAMPO = :parametro")
String buscarCampo(TipoParametro parametro);
@NativeQuery("SELECT CAMPO1 FROM MINHA_TABELA")
List<String> buscarCampos();
@NativeQuery("SELECT COUNT(1) FROM MINHA_TABELA")
Long contarItens();
Consultas que retornam tipos Java simples devem garantir que o resultado contenha exatamente uma (1) coluna.Caso a consulta retorne mais de uma coluna, será lançada a exceção
ResultHasMoreThanOneColumnException.✅ Exemplo válido:
SELECT CAMPO1 FROM MINHA_TABELA❌ Exemplo inválido:
SELECT CAMPO1, CAMPO2 FROM MINHA_TABELA
Métodos Ambíguos
Como internamente o @NativeQuery utiliza java.sql.ResultSet, pode haver ambiguidade entre métodos com o mesmo tipo de retorno (por exemplo, getString() e getNString()). Para resolver, use o atributo method:
@NativeQuery(value = "SELECT CAMPO1 FROM MINHA_TABELA", method = ResultSetMethods.GET_STRING)
String buscarPorString();
@NativeQuery(value = "SELECT CAMPO1 FROM MINHA_TABELA", method = ResultSetMethods.GET_N_STRING)
String buscarPorNString();Observação: Ambiguidade só ocorre em retornos de tipos Java simples. Interfaces são resolvidas automaticamente.
Recebendo JdbcWrapper como Argumento
O @NativeQuery permite receber um JdbcWrapper como argumento — essencial ao executar dentro de containers Sankhya (ex: @Listener). Reaproveitar o JdbcWrapper existente evita problemas de performance e vazamento de conexões.
@NativeQuery("SELECT COUNT(1) FROM TGFVEI")
Long contarVeiculos(JdbcWrapper jdbcWrapper);Uso dentro de um listener:
@Listener(instanceNames = {"Veiculo"})
@RequiredArgsConstructor(onConstructor_ = @Inject)
public class ListenerDeVeiculo extends PersistenceEventAdapter {
private final Repository repository;
@Override
public void afterDelete(PersistenceEvent event) throws Exception {
JdbcWrapper jdbcWrapper = event.getJdbcWrapper();
Long quantidade = repository.contarVeiculos(jdbcWrapper);
}
}Boas Práticas
- Prefira interfaces para consultas com múltiplas colunas.
- Evite
SELECT *: mapeie apenas os campos necessários. - Verifique aliases: garanta que o nome dos getters coincida exatamente com os nomes/aliases retornados.
Exemplos Práticos
1. Consulta Simples por Campo
@Repository
public interface VeiculoRepository extends JapeRepository<Long, Veiculo> {
// Busca por placa específica
@Criteria(clause = "PLACA = :placa")
Optional<Veiculo> findByPlaca(String placa);
// Busca por status ativo
@Criteria(clause = "ATIVO = :ativo")
List<Veiculo> findByAtivo(Boolean ativo);
// Busca por status ativo
@NativeQuery("SELECT CODVEICULO, PLACA FROM TGFVEI WHERE MODELO = :modelo")
List<InterfaceVeiculoDTO> findByAtivoNativo(String modelo);
}2. Consultas com Múltiplos Parâmetros
@Repository
public interface PedidoRepository extends JapeRepository<Long, Pedido> {
// Busca por empresa e status
@Criteria(clause = "CODEMP = :empresa AND STATUS = :status")
List<Pedido> findByEmpresaAndStatus(Long empresa, String status);
// Busca com parâmetros nomeados explicitamente
@Criteria(clause = "CODEMP = :COD_EMP AND DTNEG BETWEEN :dataInicio AND :dataFim")
List<Pedido> findByPeriodo(
@Parameter(name = "COD_EMP") Long codigoEmpresa,
@Parameter(name = "dataInicio") LocalDate inicio,
@Parameter(name = "dataFim") LocalDate fim
);
@NativeQuery("SELECT CODEMP, NOME FROM TB_PEDIDO WHERE CODEMP = :empresa AND STATUS = :STATUS_PEDIDO")
List<InterfacePedidoDTO> findByEmpresaAndStatusNativo(Long empresa, @Parameter(name = "STATUS_PEDIDO") String status);
}3. Consultas com Operadores SQL
@Repository
public interface ProdutoRepository extends JapeRepository<Long, Produto> {
// Busca com LIKE
@Criteria(clause = "DESCRPROD LIKE :termo")
List<Produto> findByDescricaoContaining(String termo);
// Busca com IN
@Criteria(clause = "CODPROD IN :codigos")
List<Produto> findByCodigos(List<Long> codigos);
// Busca com comparação numérica
@Criteria(clause = "VLRUNIT >= :valorMinimo")
List<Produto> findByValorMinimo(BigDecimal valorMinimo);
@NativeQuery("SELECT CODPROD, DESCRPROD FROM TGFPRO WHERE VLRUNIT >= :valorMinimo")
List<InterfaceProdutoDTO> findByValorMinimoNativo(BigDecimal valorMinimo);
}4. Consultas com Funções SQL
@Repository
public interface NotaRepository extends JapeRepository<Long, Nota> {
// Usando função UPPER
@Criteria(clause = "UPPER(NOMEPARC) = UPPER(:nome)")
List<Nota> findByNomeParceiroCaseInsensitive(String nome);
// Usando função de data
@Criteria(clause = "EXTRACT(YEAR FROM DTNEG) = :ano")
List<Nota> findByAno(Integer ano);
// Usando CONCAT
@Criteria(clause = "CONCAT(CODPARC, ' - ', NOMEPARC) LIKE :busca")
List<Nota> findByCodigoOuNomeParc(String busca);
@NativeQuery("SELECT NUNOTA, CODTIPOPER FROM TGFCAB WHERE CONCAT(CODPARC, ' - ', NOMEPARC) LIKE :busca")
List<InterfaceNotaDTO> findByCodigoOuNomeParcNativo(String busca);
}5. Consultas com Macros Sankhya
@Repository
public interface MovimentacaoRepository extends JapeRepository<Long, Movimentacao> {
// Usando macro dbDate()
@Criteria(clause = "DTMOV = dbDate()")
List<Movimentacao> findByDataAtual();
// Usando macro getEmpresa()
@Criteria(clause = "CODEMP = getEmpresa()")
List<Movimentacao> findByEmpresaAtual();
// Combinando macros
@Criteria(clause = "CODEMP = getEmpresa() AND DTMOV >= dbDate() - :diasAtras")
List<Movimentacao> findUltimosDias(Integer diasAtras);
@NativeQuery("SELECT NUFIN, NUNOTA FROM TGFFIN WHERE DTMOV = dbDate()")
List<InterfaceMovimentacaoDTO> findByDataAtualNativo();
}6. Consultas com Paginação
Suportada apenas para
@Criteria.@NativeQuerynão suporta paginação.
@Repository
public interface VeiculoRepository extends JapeRepository<Long, Veiculo> {
// Busca páginada por status ativo
@Criteria(clause = "ATIVO = :ativo")
Page<Veiculo> findByAtivoPaginado(Boolean ativo, Pageable pageable);
}@Component
public class VeiculoService {
private final VeiculoRepository repository;
@Inject
public VeiculoService(VeiculoRepository repository) {
this.repository = repository;
}
public Optional<Veiculo> buscarPorAtivoPaginado(Boolean ativo) {
if (ativo == null) {
throw new IllegalArgumentException("Ative não pode ser vazia, valores aceitos: true ou false");
}
//page: mínimo é 0
//size: mínimo é 1
//sort: se não informado, ordena por id de forma crescente. Caso id não esteja incluso no Sort, será
//incluído automaticamente ordenando de forma crescente. A ordenação padrão é crescente caso não informado.
int page = 0;
int size = 10;
Pagerequest pageRequest = PageRequest.of(page, size, Sort.by("PLACA", Direction.DESC));
return repository.findByAtivoPaginado(ativo, pageRequest);
}
}7. Entidade Com EmbbededID
@Embeddable
public interface ItemNotaPK {
@Column(name="NUNOTA")
Long numeroUnico;
@Column(name="SEQUENCIA")
Long sequenciaItem;
}@JapeEntity(entity="ItemNota", table="TGFITE")
public interface ItemNotaEntity {
// Usando macro dbDate()
@Id
ItemNotaPK id;
@Column(name="CODPROD")
Long codProduto;
@Column(name="CODVOL")
Long unidadeMedida;
}@Repository
public interface ItemNotaRepository extends JapeRepository<ItemNotaPK, ItemNotaEntity> {
}Operações de Exclusão com @Delete
A anotação @Delete permite executar operações de exclusão em massa baseadas em critérios específicos.
Características
- Exclusão em Massa: Remove múltiplos registros de uma vez
- Baseada em Critérios: Usa a mesma sintaxe do
@Criteria - Eficiente: Executa DELETE SQL direto no banco
- Transacional: Deve ser executada dentro de uma transação
Sintaxe Básica
@Delete(clause = "CAMPO = :parametro")
int nomeDoMetodo(TipoParametro parametro);
// Retorna o número de registros excluídosExemplos Práticos
1. Exclusão Simples
@Repository
public interface LogRepository extends JapeRepository<Long, Log> {
// Exclui logs antigos
@Delete(clause = "DTLOG < :dataLimite")
int deleteLogsByDataAnterior(LocalDate dataLimite);
// Exclui por tipo de log
@Delete(clause = "TIPO = :tipo")
int deleteByTipo(String tipo);
}2. Exclusão com Múltiplos Critérios
@Repository
public interface TempRepository extends JapeRepository<Long, TabelaTemp> {
// Exclui registros temporários por usuário e sessão
@Delete(clause = "CODUSU = :usuario AND SESSAO = :sessao")
int deleteBySessaoUsuario(Long usuario, String sessao);
// Exclui registros não processados antigos
@Delete(clause = "STATUS = 'PENDENTE' AND DTCRIACAO < :dataLimite")
int deletePendentesAntigos(LocalDateTime dataLimite);
}3. Exclusão com Subconsultas
@Repository
public interface ItemRepository extends JapeRepository<Long, Item> {
// Exclui itens de pedidos cancelados
@Delete(clause = "NUNOTA IN (SELECT NUNOTA FROM TGFCAB WHERE STATUS = 'CANCELADO')")
int deleteItensPedidosCancelados();
}Limitações e Comportamento Atual
1. Query Methods Não Suportados
// ❌ NÃO SUPORTADO (ainda) - Query methods do Spring
public interface VeiculoRepository extends JapeRepository<Long, Veiculo> {
List<Veiculo> findByPlacaStartingWith(String prefix); // Não funciona
List<Veiculo> findByAtivoTrue(); // Não funciona
}
// ✅ SUPORTADO - Use @Criteria
public interface VeiculoRepository extends JapeRepository<Long, Veiculo> {
@Criteria(clause = "PLACA LIKE :prefix")
List<Veiculo> findByPlacaStartingWith(String prefix);
@Criteria(clause = "ATIVO = 'S'")
List<Veiculo> findByAtivoTrue();
}2. Controle de Quantidade de Registros
Por padrão, as consultas são limitadas pela configuração de sessão (500 registros):
@Repository
public interface ProdutoRepository extends JapeRepository<Long, Produto> {
// ⚠️ ATENÇÃO: Retorna no máximo 500 registros (configuração padrão da sessão)
@Criteria(clause = "ATIVO = 'S'")
List<Produto> findAllAtivos();
}Nota: Funcionalidades de paginação e streaming serão disponibilizadas em versões futuras.
Boas Práticas
1. Nomenclatura de Métodos
// ✅ BOM: Nomes descritivos e consistentes
@Criteria(clause = "PLACA = :placa")
Optional<Veiculo> findByPlaca(String placa);
@Criteria(clause = "STATUS = :status")
List<Pedido> findByStatus(String status);
// ❌ RUIM: Nomes genéricos ou confusos
@Criteria(clause = "PLACA = :placa")
Optional<Veiculo> buscar(String placa);
@Criteria(clause = "STATUS = :status")
List<Pedido> getList(String status);2. Uso de Optional para Resultados Únicos
// ✅ BOM: Optional para resultados que podem não existir
@Criteria(clause = "CODPROD = :codigo")
Optional<Produto> findByCodigo(String codigo);
// ❌ RUIM: Pode lançar exceção se não encontrar
@Criteria(clause = "CODPROD = :codigo")
Produto findByCodigo(String codigo);3. Parâmetros Nomeados Explícitos
// ✅ BOM: Parâmetros explícitos para queries complexas
@Criteria(clause = "DTNEG BETWEEN :DT_INICIO AND :DT_FIM AND CODEMP = :COD_EMPRESA")
List<Nota> findByPeriodoEmpresa(
@Parameter(name = "DT_INICIO") LocalDate inicio,
@Parameter(name = "DT_FIM") LocalDate fim,
@Parameter(name = "COD_EMPRESA") Long empresa
);
// ❌ RUIM: Nomes de parâmetros confusos
@Criteria(clause = "DTNEG BETWEEN :inicio AND :fim AND CODEMP = :empresa")
List<Nota> findByPeriodoEmpresa(LocalDate dataInicial, LocalDate dataFinal, Long codigoEmpresa);4. Validação de Parâmetros
@Component
public class VeiculoService {
private final VeiculoRepository repository;
@Inject
public VeiculoService(VeiculoRepository repository) {
this.repository = repository;
}
// ✅ BOM: Validação antes da consulta
public Optional<Veiculo> buscarPorPlaca(String placa) {
if (placa == null || placa.trim().isEmpty()) {
throw new IllegalArgumentException("Placa não pode ser vazia");
}
return repository.findByPlaca(placa.toUpperCase());
}
}Anti-Patterns
1. Consultas Muito Genéricas
// ❌ RUIM: Consulta pode retornar muitos registros
@Criteria(clause = "1 = 1") // Retorna todos os registros
List<Produto> findAll();
// ✅ BOM: Consultas específicas com filtros
@Criteria(clause = "ATIVO = 'S' AND CODEMP = :empresa")
List<Produto> findAtivosByEmpresa(Long empresa);2. Uso Incorreto de @Delete
// ❌ RUIM: Delete sem transação
public class LogService {
@Delete(clause = "DTLOG < :data")
int limparLogsAntigos(LocalDate data); // Pode falhar
}
// ✅ BOM: Delete dentro de transação
@Component
public class LogService {
@Transactional
public int limparLogsAntigos(LocalDate data) {
return logRepository.deleteByDataAnterior(data);
}
}3. Consultas Complexas Demais
// ❌ RUIM: Query muito complexa em uma linha
@Criteria(clause = "EXISTS (SELECT 1 FROM TABELA2 T2 WHERE T2.ID = T1.ID AND T2.STATUS IN ('A', 'B', 'C')) AND CAMPO1 > :valor AND EXTRACT(MONTH FROM DATA1) = EXTRACT(MONTH FROM dbDate())")
List<Entidade> consultaComplexaDemais(BigDecimal valor);
// ✅ BOM: Divida em métodos menores e mais claros
@Criteria(clause = "STATUS IN ('A', 'B', 'C') AND VALOR > :valor")
List<Entidade> findByStatusAndValor(BigDecimal valor);
@Criteria(clause = "EXTRACT(MONTH FROM DATA1) = EXTRACT(MONTH FROM dbDate())")
List<Entidade> findByMesAtual();Exemplo Completo: Repository de E-commerce
@Repository
public interface PedidoRepository extends JapeRepository<Long, Pedido> {
// Consultas básicas
@Criteria(clause = "NUNOTA = :numero")
Optional<Pedido> findByNumero(Long numero);
@Criteria(clause = "CODPARC = :codigoCliente")
List<Pedido> findByCliente(Long codigoCliente);
// Consultas por período
@Criteria(clause = "DTNEG BETWEEN :inicio AND :fim")
List<Pedido> findByPeriodo(LocalDate inicio, LocalDate fim);
// Consultas com status
@Criteria(clause = "STATUS = :status AND CODEMP = :empresa")
List<Pedido> findByStatusEmpresa(String status, Long empresa);
// Consulta com valor
@Criteria(clause = "VLRNOTA >= :valorMinimo")
List<Pedido> findByValorMinimo(BigDecimal valorMinimo);
// Consulta usando macros
@Criteria(clause = "CODEMP = getEmpresa() AND DTNEG = dbDate()")
List<Pedido> findPedidosHoje();
// Exclusão de rascunhos antigos
@Delete(clause = "STATUS = 'RASCUNHO' AND DHALTER < :dataLimite")
int deleteRascunhosAntigos(LocalDateTime dataLimite);
}
// Serviço que utiliza o repository
@Component
public class PedidoService {
private final PedidoRepository pedidoRepository;
@Inject
public PedidoService(PedidoRepository pedidoRepository) {
this.pedidoRepository = pedidoRepository;
}
@Transactional
public void limparRascunhosAntigos() {
LocalDateTime dataLimite = LocalDateTime.now().minusDays(30);
int removidos = pedidoRepository.deleteRascunhosAntigos(dataLimite);
System.out.println("Removidos " + removidos + " rascunhos antigos");
}
public List<Pedido> buscarPedidosCliente(Long codigoCliente) {
if (codigoCliente == null) {
throw new IllegalArgumentException("Código do cliente é obrigatório");
}
return pedidoRepository.findByCliente(codigoCliente);
}
}🚀 Recursos Implementados
As seguintes funcionalidades estão disponíveis e testadas:
- ✅ CRUD Automático:
save(),findById(),findAll(),delete() - ✅ Consultas Personalizadas:
@Criteriacom parâmetros nomeados e múltiplas condições - ✅ Exclusão em Massa:
@Deletepara operações eficientes de limpeza - ✅ Funções SQL: Suporte completo a
UPPER(),LOWER(),CONCAT(), etc. - ✅ Macros Sankhya:
dbDate(),getEmpresa(),getUsuario()nativamente suportadas - ✅ Type Safety: Parâmetros e retornos completamente tipados
- ✅ Optional Support:
Optional<T>para resultados únicos que podem não existir - ✅ Collection Support:
List<T>para múltiplos resultados - ✅ Parameter Binding:
@Parameterpara nomeação explícita de parâmetros - ✅ Paginação:
Page<T>para resultados paginados
O padrão Repository no SDK Sankhya oferece uma abstração poderosa e flexível para acesso a dados, mantendo a simplicidade e familiaridade com padrões de mercado como Spring Data JPA, enquanto se integra perfeitamente com o ecossistema Sankhya.
Updated 11 days ago
