Install
$ agentstack add skill-snk-devcenter-addon-studio-repository ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Repositório (@Repository) — Addon Studio 2.0
Padrão Repository no SDK Sankhya = abstração simplifica acesso dados. Define interfaces declarativas, SDK gera implementação em compile-time — sem boilerplate, type-safe, protege SQL Injection.
> Referência complementar: consulte entity para criar entidades correspondentes.
1. Criando um Repositório
Repositório = interface que estende JapeRepository:
import br.com.sankhya.sdk.data.repository.JapeRepository;
import br.com.sankhya.studio.stereotypes.Repository;
@Repository
public interface VeiculoRepository extends JapeRepository {
// métodos customizados aqui
}
BigDecimal→ tipo chave primária (primeiro param). PK de tabela nativa Sankhya =BigDecimal; PK de tabela do addon =Integer(verentity)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 | Exception | | delete(T entity) | Remove entidade do banco | Exception |
> Atenção: todos os métodos herdados lançam Exception checked. Todo método de serviço ou controller que os chame deve declarar throws Exception na assinatura. > > findByPK retorna T (nullable), não Optional. Use null-check manual: > ``java > MinhaEntidade e = repository.findByPK(id); > if (e == null) throw new MinhaException("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.
@Criteria(clause = "this.PLACA = :placa")
Optional findByPlaca(String placa);
@Criteria(clause = "this.ATIVO = :ativo")
List findByAtivo(Boolean ativo);
@Criteria(clause = "this.CODEMP = :empresa AND this.STATUS = :status")
List findByEmpresaAndStatus(BigDecimal empresa, String status);
> Quando usar @Parameter: > > - 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 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 (=, >, =, = :valor | | LIKE | this.PLACA LIKE :prefix | | IN com List | this.CODPROD IN (:codigos) (parâmetro List codigos) | | BETWEEN | this.DTNEG BETWEEN :inicio AND :fim | | IS NULL / IS NOT NULL | this.DHCONFIRMACAO IS NULL | | Funções SQL (UPPER, LOWER, CONCAT, COUNT, SUM) | UPPER(this.NOMEPARC) = UPPER(:nome) | | Macros Sankhya portáveis | this.DTMOV = dbDate(), ignorecase(this.NOME) = ignorecase(:nome) |
> 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 usar br.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 findByModeloNativo(String modelo);
Mapeando o resultado com @NativeQuery.Result
Crie interface anotada com @NativeQuery.Result para mapear colunas retornadas:
@NativeQuery.Result
interface VeiculoDTO {
BigDecimal getCodVeiculo(); // mapeia coluna CODVEICULO
String getPlaca(); // mapeia coluna PLACA
}
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);
3.3 @Modifying + @NativeQuery — Operações de Escrita (UPDATE / DELETE / INSERT)
@Modifying
@NativeQuery("UPDATE TGFPRO SET VLRVENDA = VLRVENDA * :fator WHERE CODGRUPOPROD = :grupo")
void reajustarPrecoPorGrupo(BigDecimal fator, BigDecimal grupo);
@Modifying
@NativeQuery("DELETE FROM TDCXYZLOG WHERE DTEXPIRACAO **`@Modifying` deve retornar `void` ou `Boolean`.** KSP rejeita `int` com erro de compilação.
Sempre envolva `@Modifying` em método `@Transactional`.
```java
@Transactional
public void limparLogsAntigos(java.sql.Date data) throws Exception {
logRepository.excluirLogsExpirados(data);
}
3.4 Paginação com @Criteria
Paginação suportada apenas com @Criteria. @NativeQuery não suporta.
@Criteria(clause = "this.ATIVO = :ativo")
Page findByAtivoPaginado(Boolean ativo, Pageable pageable);
Uso no serviço:
PageRequest pageRequest = PageRequest.of(0, 10, Sort.by("PLACA", Direction.DESC));
Page 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 findByDataAtual();
// Macro ignorecase() — busca tolerante a case e acentos
@Criteria(clause = "ignorecase(this.NOMEPARC) = ignorecase(:nome)")
List 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)
@Embeddable
public class ItemNotaPK {
@Column(name = "NUNOTA")
private BigDecimal numeroUnico;
@Column(name = "SEQUENCIA")
private BigDecimal sequenciaItem;
}
@JapeEntity(entity = "ItemNota", table = "TGFITE",
isNativeTable = true, isNativeInstance = true)
public class ItemNota {
@Id
private ItemNotaPK id;
@Column(name = "CODPROD")
private BigDecimal codProduto;
}
@Repository
public interface ItemNotaRepository extends JapeRepository {
// CRUD para chave composta funciona automaticamente
}
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:
import br.com.sankhya.sdk.data.structures.ParamMatrix;
import java.math.BigDecimal;
import java.util.Arrays;
ParamMatrix matrix = ParamMatrix.of(
Arrays.asList(BigDecimal.valueOf(1), BigDecimal.valueOf(1)), // (CODEMP=1, CODFIL=1)
Arrays.asList(BigDecimal.valueOf(1), BigDecimal.valueOf(2)), // (CODEMP=1, CODFIL=2)
Arrays.asList(BigDecimal.valueOf(2), BigDecimal.valueOf(3)) // (CODEMP=2, CODFIL=3)
);
List filiais = filialRepository.findByEmpresaFilial(matrix);
Sintaxe diverge entre Oracle e MSSQL
-- Oracle aceita IN com tuplas direto:
(CODEMP, CODFIL) IN (:matrix)
-- MSSQL não aceita; precisa EXISTS + VALUES:
EXISTS (SELECT 1 FROM (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 (` + ) — não dá pra escrever uma única clause portável. Ver seção 4. Queries SQL Externas`.
@NativeQuery(value = "queries/filiais-por-tupla.xml", fromFile = true)
List findByEmpresaFilial(ParamMatrix matrix);
SELECT * FROM TGFFIL WHERE (CODEMP, CODFIL) IN (:matrix)
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
)
4. Queries SQL Externas (Arquivos)
Queries complexas (100+ linhas) = arquivos externos com fromFile = true.
Arquivo .sql (compatível Oracle e MSSQL)
@NativeQuery(value = "queries/listar-veiculos-ativos.sql", fromFile = true)
List listarAtivos(String ativo);
Arquivo: model/src/main/resources/queries/listar-veiculos-ativos.sql
SELECT v.CODVEI, v.PLACA, v.DESCRICAO
FROM TGFVEI v
WHERE v.ATIVO = :ativo
ORDER BY v.DESCRICAO
Arquivo .xml (queries por banco de dados)
Use quando sintaxe difere entre Oracle e MSSQL:
Arquivo: model/src/main/resources/queries/quantidade-vendida.xml
SELECT COUNT(*) FROM TGFCAB
SELECT NVL(SUM(ITE.QTDNEG), 0) AS TOTAL FROM TGFITE ITE
WHERE ITE.CODPROD = :codProd
SELECT ISNULL(SUM(ITE.QTDNEG), 0) AS TOTAL FROM TGFITE ITE
WHERE ITE.CODPROD = :codProd
Prioridade de seleção:
- `` — usada para ambos bancos (maior prioridade)
- `
ou— só se` 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
Optionalpara resultados de@Criteriaque podem não existir. ParafindByPK, o retorno éT(nullable) — use null-check manual - Nomes descritivos — prefira
findByPlacaa genéricobuscar - 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
@Transactionalpara 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 `` 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 findAll();
// Correto: sempre use filtros obrigatórios
@Criteria(clause = "this.ATIVO = 'S' AND this.CODEMP = :empresa")
List findAtivosByEmpresa(BigDecimal empresa);
// Errado: lógica de negócio dentro do repositório
@Repository
public interface PedidoRepository extends JapeRepository {
default void aprovarPedido(BigDecimal nunota) { /* ... */ }
}
// Correto: lógica de negócio no serviço
@Component
public class PedidoService {
@Transactional
public void aprovarPedido(BigDecimal nunota) throws Exception {
Pedido pedido = repository.findByPK(nunota);
if (pedido == null) throw new IllegalArgumentException("Pedido não encontrado: " + nunota);
repository.save(pedido);
}
}
// Errado: query methods do Spring Data NÃO são suportados
List findByPlacaStartingWith(String prefix); // não funciona
// Correto: use @Criteria no lugar
@Criteria(clause = "this.PLACA LIKE :prefix")
List 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)
@Deletedescontinuada — use@Modifying+@NativeQuerypara 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)
public class Pedido {
@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
public interface PedidoResumoDTO {
BigDecimal getNumero();
BigDecimal getValor();
}
// Repositório
@Repository
public interface PedidoRepository extends JapeRepository {
@Criteria(clause = "this.CODPARC = :codigoCliente")
List findByCliente(BigDecimal codigoCliente);
@NativeQuery("SELECT NUNOTA as numero, VLRNOTA as valor FROM TGFCAB WHERE STATUS = :status AND CODEMP = :empresa")
List findResumoPorStatus(
String status,
BigDecimal empresa
);
@Modifying
@NativeQuery("DELETE FROM TGFCAB WHERE STATUS = 'R' AND DHALTER busc
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [snk-devcenter](https://github.com/snk-devcenter)
- **Source:** [snk-devcenter/addon-studio](https://github.com/snk-devcenter/addon-studio)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.