Principe¶
La route lit la table source par lots de 1 000 lignes (OFFSET / FETCH) jusqu’à épuisement, transforme chaque ligne et l’insère dans PostgreSQL. Le déclenchement est manuel via un appel REST.
Configuration des datasources¶
Créer le fichier camel/application.properties :
# Datasource MS-SQL
camel.datasource.mssql.url=jdbc:sqlserver://mssql:1433;databaseName=sourcedb;encrypt=false
camel.datasource.mssql.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
camel.datasource.mssql.username=sa
camel.datasource.mssql.password=Str0ngPass!
# Datasource PostgreSQL
camel.datasource.postgres.url=jdbc:postgresql://postgres:5432/targetdb
camel.datasource.postgres.driver-class-name=org.postgresql.Driver
camel.datasource.postgres.username=camel
camel.datasource.postgres.password=camel
# Taille des lots
sync.batch.size=1000Route Camel (YAML)¶
Trois routes : déclencheur REST, boucle de pagination, traitement d’un lot.
Créer le fichier camel/routes/mssql-to-pg.camel.yaml :
# Route 1 : point d'entrée REST
- route:
id: sync-trigger
description: Déclenche la synchronisation sur POST /sync
from:
uri: rest:post:sync
steps:
- setProperty:
name: offset
expression:
constant: 0
- setProperty:
name: totalRows
expression:
constant: 0
- to:
uri: direct:sync-loop
- setBody:
expression:
simple: "${exchangeProperty.totalRows} lignes synchronisées"
# Route 2 : boucle de pagination
- route:
id: sync-loop
description: Lit les lots MS-SQL jusqu'à épuisement
from:
uri: direct:sync-loop
steps:
- log:
message: "Lecture lot offset=${exchangeProperty.offset}"
# Lecture d'un lot
- setBody:
expression:
simple: >
SELECT id, nom, prix, actif
FROM dbo.produits
ORDER BY id
OFFSET ${exchangeProperty.offset} ROWS
FETCH NEXT {{sync.batch.size}} ROWS ONLY
- to:
uri: jdbc:mssql
# Arrêt si lot vide
- choice:
when:
- simple: "${body.size()} == 0"
steps:
- log:
message: "Synchronisation terminée — ${exchangeProperty.totalRows} lignes"
- stop: {}
otherwise:
steps:
# Traitement du lot
- to:
uri: direct:sync-batch
# Incrémenter offset et totalRows
- setProperty:
name: offset
expression:
groovy: >
exchange.getProperty('offset', Integer) + {{sync.batch.size}}
- setProperty:
name: totalRows
expression:
groovy: >
exchange.getProperty('totalRows', Integer) + exchange.getProperty('batchSize', Integer)
# Itération suivante
- to:
uri: direct:sync-loop
# Route 3 : traitement d'un lot
- route:
id: sync-batch
description: Transforme et insère un lot dans PostgreSQL
from:
uri: direct:sync-batch
steps:
- setProperty:
name: batchSize
expression:
simple: "${body.size()}"
- split:
expression:
simple: "${body}"
steps:
# Conversion BIT → BOOLEAN
- setBody:
expression:
groovy: |
def row = request.body
row['actif'] = row['actif'] == 1
return row
# Upsert PostgreSQL
- to:
uri: jdbc:postgres
body: >
INSERT INTO produits (id, nom, prix, actif)
VALUES (:#id, :#nom, :#prix, :#actif)
ON CONFLICT (id) DO UPDATE
SET nom = EXCLUDED.nom,
prix = EXCLUDED.prix,
actif = EXCLUDED.actifExécution et vérification¶
Déclencher la synchronisation
curl -X POST http://localhost:8080/syncRéponse attendue :
1500000 lignes synchroniséesVérifier le résultat dans PostgreSQL
docker exec -it camel-postgres psql -U camel -d targetdb \
-c "SELECT COUNT(*) FROM produits;"Conversions de types MS-SQL → PostgreSQL¶
| MS-SQL | PostgreSQL | Remarque |
|---|---|---|
NVARCHAR | TEXT | Pas de limite à déclarer |
DATETIME | TIMESTAMP | Format ISO 8601 automatique |
BIT | BOOLEAN | Conversion explicite nécessaire |
DECIMAL | NUMERIC | Précision identique |
INT IDENTITY | INTEGER | Pas d’auto-increment côté cible |
UNIQUEIDENTIFIER | UUID | Cast via ::uuid si besoin |
Points d’attention¶
La table source doit avoir une clé primaire ou une colonne ordonnée stable pour que
OFFSET / FETCHsoit déterministeON CONFLICTrequiert une contraintePRIMARY KEYouUNIQUEsur la table cibleLa transformation BIT → BOOLEAN est obligatoire : PostgreSQL rejette les entiers
0/1sur un champBOOLEANPour les tables sans clé primaire : remplacer l’upsert par un
DELETE + INSERTdans une transactionAjuster
sync.batch.sizeselon la RAM disponible dans le conteneur Karavan (1 000 lignes ≈ sûr, 10 000 possible si les lignes sont légères)Pour les très grandes tables (>10M lignes), envisager un filtre sur date de modification (
WHERE updated_at > :lastSync) plutôt qu’une recopie complète