Cria, revisa e refatora interfaces `@Repository` Sankhya estendendo `JapeRepository<ID, Entity>` com `@Criteria`, `@NativeQuery`, `@Modifying`, `@Parameter(name = "...")`, paginação, `findByPK` (retorno nullable, `throws Exception`). Use ao criar, alterar, revisar, auditar ou padronizar a camada de acesso a dados, ao implementar consulta/listagem/filtro/paginação/busca, ao escrever query custom, ao trabalhar com arquivos `*Repository.java`, ou ao tocar em código com `@Repository`/`JapeRepository`.
Installation
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Cria, revisa e refatora interfaces `@Repository` Sankhya estendendo `JapeRepository<ID, Entity>` com `@Criteria`, `@NativeQuery`, `@Modifying`, `@Parameter(name = "...")`, paginação, `findByPK` (retorno nullable, `throws Exception`). Use ao criar, alterar, revisar, auditar ou padronizar a camada de acesso a dados, ao implementar consulta/listagem/filtro/paginação/busca, ao escrever query custom, ao trabalhar com arquivos `*Repository.java`, ou ao tocar em código com `@Repository`/`JapeRepository`.
BigDecimal → tipo chave primária (primeiro param). PK de tabela nativa Sankhya = BigDecimal; PK de tabela do addon = Integer (ver entity)
Veiculo → classe entidade (segundo param)
2. Métodos CRUD Padrão (Herdados)
Método
Descrição
Lança
save(T entity)
Salva ou atualiza entidade
Exception
findByPK(ID id)
Busca por PK → retorna T (pode ser null — verificar com null-check)
Exception
findAll()
Retorna todas instâncias
Exception
findAll(Pageable pageable)
Retorna página com ordenação → Page<T>
Exception
delete(T entity)
Remove entidade do banco
Exception
Atenção: todos os métodos herdados lançam Exceptionchecked. Todo método de serviço ou controller que os chame deve declarar throws Exception na assinatura.
findByPK retorna T (nullable), não Optional<T>. Use null-check manual:
MinhaEntidadee= repository.findByPK(id);
if (e == null) thrownewMinhaException("Registro não encontrado: " + id);
3. Consultas Customizadas
3.1 @Criteria — Consultas com Condição WHERE
Use @Criteria para filtros SELECT simples tipados.
Obrigatório: prefixo this. em todos campos da clause.
Opcional quando o nome do parâmetro Java casa com o :nome da clause — basta declarar BigDecimal empresa, String status (vincula automaticamente).
Obrigatório quando os nomes diferem. Sintaxe correta: @Parameter(name = "...") — sempre com name = explícito. A forma posicional @Parameter("...")causa erro de compilação.
// Nome do parâmetro Java != :nome da clause → @Parameter(name = "...") obrigatório@Criteria(clause = "this.DTNEG BETWEEN :dataInicio AND :dataFim")
List<Pedido> findByPeriodo(
@Parameter(name = "dataInicio") java.sql.Date inicio,
@Parameter(name = "dataFim") java.sql.Date fim
);
Operadores SQL suportados em @Criteria
Operador / construção
Exemplo de clause
Comparação (=, >, <, >=, <=, !=)
this.VLRNOTA >= :valor
LIKE
this.PLACA LIKE :prefix
IN com List<?>
this.CODPROD IN (:codigos) (parâmetro List<BigDecimal> codigos)
Macros são preferidas à sintaxe específica de banco (SYSDATE, NVL, ||, ROWNUM). Ver skill macros para o catálogo completo.
3.2 @NativeQuery — Consultas SQL Nativas
Import obrigatório:import br.com.sankhya.studio.persistence.NativeQuery;
Cobre tanto @NativeQuery quanto @NativeQuery.Result. Não usarbr.com.sankhya.sdk.data.repository.NativeQuery — pacote incorreto, causa erro de compilação.
Use @NativeQuery para queries complexas com JOINs, agregações, funções específicas banco etc.
@NativeQuery("SELECT CODVEICULO, PLACA FROM TGFVEI WHERE MODELO = :modelo")
List<VeiculoDTO> findByModeloNativo(String modelo);
Mapeando o resultado com @NativeQuery.Result
Crie interface anotada com @NativeQuery.Result para mapear colunas retornadas:
Nomes dos getters devem coincidir exatamente com nomes/aliases das colunas. Divergência = null.
Retornando tipos Java simples
@NativeQuery("SELECT DESCRPROD FROM TGFPRO WHERE CODPROD = :codigo")
String buscarDescricaoPorCodigo(BigDecimal codigo);
@NativeQuery("SELECT COUNT(1) FROM TGFPRO")
Long contarTotalDeProdutos();
Queries tipo simples devem retornar exatamente uma coluna. Mais de uma lança ResultHasMoreThanOneColumnException.
Passando JdbcWrapper para reuso de conexão
Ideal dentro de @Listener, reaproveita conexão transacional existente:
@NativeQuery("SELECT COUNT(1) FROM TGFCAB WHERE STATUS = 'P'")
Long contarPendentes(JdbcWrapper jdbc);
PageRequestpageRequest= PageRequest.of(0, 10, Sort.by("PLACA", Direction.DESC));
Page<Veiculo> pagina = repository.findByAtivoPaginado(true, pageRequest);
3.5 Funções SQL e Macros Sankhya
// Macro dbDate() — data/hora atual do banco (portatil Oracle/MSSQL)@Criteria(clause = "this.DTMOV = dbDate()")
List<Movimentacao> findByDataAtual();
// Macro ignorecase() — busca tolerante a case e acentos@Criteria(clause = "ignorecase(this.NOMEPARC) = ignorecase(:nome)")
List<Nota> findByNomeParceiro(String nome);
// Macro nullValue() — substitui null por padrao (NVL/ISNULL portatil)@NativeQuery("SELECT nullValue(VLRDESCONTO, 0) FROM TGFCAB WHERE NUNOTA = :nu")
BigDecimal descontoOuZero(BigDecimal nu);
Macros traduzem automaticamente entre Oracle e MSSQL. Lista completa (datas, texto, conversoes, agregacoes): ver macros. Sempre prefira macro a sintaxe especifica de banco (SYSDATE, NVL, ||, ROWNUM, etc.).
3.6 Entidade com Chave Primária Composta (@Embeddable)
3.7 ResultSetMethods — Desambiguação de Tipo Simples
Quando @NativeQuery retorna tipo Java simples (String, Long, etc.) e o ResultSet tem múltiplos métodos compatíveis (ex.: getString vs getNString), use o atributo method para forçar a leitura específica:
import br.com.sankhya.studio.persistence.NativeQuery;
// ResultSetMethods e enum ANINHADO em NativeQuery — sem import proprio; use NativeQuery.ResultSetMethods@NativeQuery(value = "SELECT DESCRICAO FROM TGFPRO WHERE CODPROD = :codigo",
method = NativeQuery.ResultSetMethods.GET_N_STRING)
String buscarDescricaoNVarchar(BigDecimal codigo);
Caso típico: colunas NVARCHAR/NCHAR (Unicode) no MSSQL exigem getNString para preservar caracteres multi-byte. Sem o method, o SDK chama getString por padrão.
3.8 ParamMatrix — Tuplas Múltiplas em IN
Quando o filtro envolve múltiplas colunas combinadas (ex.: (CODEMP, CODFIL) IN ((1,1), (1,2), (2,3))), use ParamMatrix no parâmetro:
-- Oracle aceita IN com tuplas direto:
(CODEMP, CODFIL) IN (:matrix)
-- MSSQL não aceita; precisa EXISTS + VALUES:EXISTS (SELECT1FROM (VALUES :matrix) AS T(CODEMP, CODFIL)
WHERE T.CODEMP = TGFFIL.CODEMP AND T.CODFIL = TGFFIL.CODFIL)
Recomendação: sempre que usar ParamMatrix, externalize a query em arquivo XML multi-banco (<oracle> + <mssql>) — não dá pra escrever uma única clause portável. Ver seção 4. Queries SQL Externas.
<sql><oracle>
SELECT * FROM TGFFIL WHERE (CODEMP, CODFIL) IN (:matrix)
</oracle><mssql>
SELECT TGFFIL.* FROM TGFFIL
WHERE EXISTS (
SELECT 1 FROM (VALUES :matrix) AS T(CODEMP, CODFIL)
WHERE T.CODEMP = TGFFIL.CODEMP AND T.CODFIL = TGFFIL.CODFIL
)
</mssql></sql>
<sql><both><!-- Query idêntica para ambos os bancos -->
SELECT COUNT(*) FROM TGFCAB
</both><oracle>
SELECT NVL(SUM(ITE.QTDNEG), 0) AS TOTAL FROM TGFITE ITE
WHERE ITE.CODPROD = :codProd
</oracle><mssql>
SELECT ISNULL(SUM(ITE.QTDNEG), 0) AS TOTAL FROM TGFITE ITE
WHERE ITE.CODPROD = :codProd
</mssql></sql>
Prioridade de seleção:
<both> — usada para ambos bancos (maior prioridade)
<oracle> ou <mssql> — só se <both> vazia
Queries de arquivos cacheadas automaticamente em memória após primeira leitura.
Validações em Compile-Time
SDK valida queries externas durante compilação:
Verificação
Resultado
Arquivo não encontrado
Erro de compilação
Arquivo .sql vazio
Erro de compilação
XML mal formado
Erro de compilação
Todas as tags XML vazias
Erro de compilação
Apenas Oracle OU MSSQL definida
Warning
Parâmetros inconsistentes entre DBs
Erro de compilação
5. Boas Práticas
Use Optional<T> para resultados de @Criteria que podem não existir. Para findByPK, o retorno é T (nullable) — use null-check manual
Nomes descritivos — prefira findByPlaca a genérico buscar
Valide params no serviço antes de chamar repositório
Separe responsabilidades — lógica de negócio no service (@Component), não no repositório
Use @Transactional para toda operação @Modifying
Evite SELECT * — mapeie só campos necessários
Use paginação para consultas com muitos registros (padrão: 500 por sessão)
Prefira <both> em XMLs para query única entre bancos
6. Anti-Patterns
// Errado: query genérica sem filtros — pode retornar muitos registros@Criteria(clause = "1 = 1")
List<Produto> findAll();
// Correto: sempre use filtros obrigatórios@Criteria(clause = "this.ATIVO = 'S' AND this.CODEMP = :empresa")
List<Produto> findAtivosByEmpresa(BigDecimal empresa);
// Errado: lógica de negócio dentro do repositório@RepositorypublicinterfacePedidoRepositoryextendsJapeRepository<BigDecimal, Pedido> {
defaultvoidaprovarPedido(BigDecimal nunota) { /* ... */ }
}
// Correto: lógica de negócio no serviço@ComponentpublicclassPedidoService {
@TransactionalpublicvoidaprovarPedido(BigDecimal nunota)throws Exception {
Pedidopedido= repository.findByPK(nunota);
if (pedido == null) thrownewIllegalArgumentException("Pedido não encontrado: " + nunota);
repository.save(pedido);
}
}
// Errado: query methods do Spring Data NÃO são suportados
List<Veiculo> findByPlacaStartingWith(String prefix); // não funciona// Correto: use @Criteria no lugar@Criteria(clause = "this.PLACA LIKE :prefix")
List<Veiculo> findByPlacaStartingWith(String prefix);
7. Limitações Conhecidas
Query Methods por nome (estilo Spring Data JPA) não suportados
Paginação só com @Criteria, não com @NativeQuery
Limite padrão registros: 500 por sessão (use paginação pra contornar)
@Delete descontinuada — use @Modifying + @NativeQuery para exclusões
8. Exemplo Completo
// Entidade (tabela e instância nativas — flags + Lombok obrigatórios, ver `entity` §1.2)@Data@NoArgsConstructor@AllArgsConstructor@JapeEntity(entity = "CabecalhoNota", table = "TGFCAB",
isNativeTable = true, isNativeInstance = true)publicclassPedido {
@Id@Column(name = "NUNOTA")private BigDecimal numero;
@Column(name = "CODPARC")private BigDecimal codigoCliente;
@Column(name = "VLRNOTA")private BigDecimal valor;
@Column(name = "STATUS")private String status;
@Column(name = "DHALTER")private Timestamp dataAlteracao;
}
// DTO de resultado@NativeQuery.Result
publicinterfacePedidoResumoDTO {
BigDecimal getNumero();
BigDecimal getValor();
}
// Repositório@RepositorypublicinterfacePedidoRepositoryextendsJapeRepository<BigDecimal, Pedido> {
@Criteria(clause = "this.CODPARC = :codigoCliente")
List<Pedido> findByCliente(BigDecimal codigoCliente);
@NativeQuery("SELECT NUNOTA as numero, VLRNOTA as valor FROM TGFCAB WHERE STATUS = :status AND CODEMP = :empresa")
List<PedidoResumoDTO> findResumoPorStatus(
String status,
BigDecimal empresa
);
@Modifying@NativeQuery("DELETE FROM TGFCAB WHERE STATUS = 'R' AND DHALTER < :dataLimite")voiddeleteRascunhosAntigos(Timestamp dataLimite);
}
// Serviço@ComponentpublicclassPedidoService {
privatefinal PedidoRepository pedidoRepository;
@InjectpublicPedidoService(PedidoRepository pedidoRepository) {
this.pedidoRepository = pedidoRepository;
}
@TransactionalpublicvoidlimparRascunhosAntigos()throws Exception {
Timestamplimite= Timestamp.valueOf(LocalDateTime.now().minusDays(30));
pedidoRepository.deleteRascunhosAntigos(limite);
}
public List<Pedido> buscarPedidosDoCliente(BigDecimal codigoCliente)throws Exception {
if (codigoCliente == null) {
thrownewIllegalArgumentException("Código do cliente é obrigatório.");
}
return pedidoRepository.findByCliente(codigoCliente);
}
}
9. Checklist
Criando um repositório novo
Interface Java em local apropriado da arquitetura do projeto.
Extends JapeRepository<TipoID, TipoEntidade> (ID primeiro, entidade depois).
Anotada com @Repository de br.com.sankhya.studio.stereotypes.
Métodos @Criteria com prefixo this. em todos campos da clause.
Nomes de params do método batem com :placeholders da clause.
@NativeQuery.Result como interface publica em arquivo proprio (não interface interna do repository).
@Modifying sempre com @Transactional no serviço chamador.
Queries complexas em arquivo externo com fromFile = true.
Optional<T> para métodos @Criteria que podem não encontrar resultado. findByPK retorna T nullable — usar null-check manual.
Métodos de serviço/controller que chamam save, findByPK, findAll ou delete declaram throws Exception.
Métodos @Modifying retornam void ou Boolean — nunca int.
Erros comuns
Erro
Correção
Omitir this. na clause do @Criteria
Usar this.CAMPO = :param — obrigatório
Parâmetros invertidos em JapeRepository<T, ID>
ID sempre primeiro param
@NativeQuery.Result como interface interna
Criar como interface pública em arquivo proprio
@Modifying sem @Transactional no chamador
Envolver chamada em método @Transactional
Usar Query Methods do Spring Data
Não suportado — usar @Criteria ou @NativeQuery
@Delete descontinuada
Usar @Modifying + @NativeQuery
@Modifying retornando int
KSP rejeita — usar void ou Boolean
findByPK(...).orElseThrow(...) ou .map(...)
findByPK retorna T (nullable), não Optional<T> — usar null-check manual
Esquecer throws Exception em método que usa repositório
Todo método que chama save, findByPK, findAll ou delete deve declarar throws Exception
Import errado de @NativeQuery
Usar br.com.sankhya.studio.persistence.NativeQuery, não br.com.sankhya.sdk.data.repository.NativeQuery
Skills relacionadas
entity — entidade @JapeEntity sobre a qual o repositório opera
macros — macros SQL para @NativeQuery e queries/<arquivo>.xml
test — JUnit + Mockito do repositório; repository é tipicamente mockado em testes (cuidado com quirks do JapeRepository)