# Repository

> 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,…

- **Type:** Skill
- **Install:** `agentstack add skill-snk-devcenter-addon-studio-repository`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [snk-devcenter](https://agentstack.voostack.com/s/snk-devcenter)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [snk-devcenter](https://github.com/snk-devcenter)
- **Source:** https://github.com/snk-devcenter/addon-studio/tree/main/plugins/addon-studio/skills/repository

## Install

```sh
agentstack add skill-snk-devcenter-addon-studio-repository
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## 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`:

```java
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` (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`                                    | `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.**

```java

@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**.

```java
// 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.

```java

@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:

```java

@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

```java

@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:

```java

@NativeQuery("SELECT COUNT(1) FROM TGFCAB WHERE STATUS = 'P'")
Long contarPendentes(JdbcWrapper jdbc);
```

---

### 3.3 `@Modifying` + `@NativeQuery` — Operações de Escrita (UPDATE / DELETE / INSERT)

```java

@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.

```java

@Criteria(clause = "this.ATIVO = :ativo")
Page findByAtivoPaginado(Boolean ativo, Pageable pageable);
```

**Uso no serviço:**

```java
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

```java
// 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`)

```java

@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:

```java
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:

```java
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

```sql
-- 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`.

```java
@NativeQuery(value = "queries/filiais-por-tupla.xml", fromFile = true)
List findByEmpresaFilial(ParamMatrix matrix);
```

```xml

    
        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)

```java

@NativeQuery(value = "queries/listar-veiculos-ativos.sql", fromFile = true)
List listarAtivos(String ativo);
```

**Arquivo:** `model/src/main/resources/queries/listar-veiculos-ativos.sql`

```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`

```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:**

1. `` — usada para ambos bancos (maior prioridade)
2. `` 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

1. **Use `Optional`** para resultados de `@Criteria` que podem não existir. Para `findByPK`, o retorno é `T` (nullable) — use null-check manual
2. **Nomes descritivos** — prefira `findByPlaca` a genérico `buscar`
3. **Valide params no serviço** antes de chamar repositório
4. **Separe responsabilidades** — lógica de negócio no service (`@Component`), não no repositório
5. **Use `@Transactional`** para toda operação `@Modifying`
6. **Evite `SELECT *`** — mapeie só campos necessários
7. **Use paginação** para consultas com muitos registros (padrão: 500 por sessão)
8. **Prefira `` em XMLs** para query única entre bancos

---

## 6. Anti-Patterns

```java
// 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)
- **`@Delete` descontinuada** — use `@Modifying` + `@NativeQuery` para exclusões

---

## 8. Exemplo Completo

```java
// 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.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-snk-devcenter-addon-studio-repository
- Seller: https://agentstack.voostack.com/s/snk-devcenter
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
