# Running Oracle Database Server as a Docker container A series of 3 article(s), in reading order. # Faire tourner Oracle Database Server dans un container Docker > Apprenez à faire tourner Oracle Database Server dans un container Docker. Ce guide pas à pas couvre la mise en place de l'image, l'acceptation de la licence, la génération du token et l'accès à la base de données. --- ![Faire tourner Oracle Database Server dans un container Docker](/img/v2/oracle.webp) **TL;DR** Ce guide complet vous accompagne pas à pas pour faire tourner un serveur Oracle Enterprise Database dans un container Docker. Il couvre tout le processus : création d'un compte Oracle, authentification pour récupérer l'image officielle, déploiement du container avec du stockage persistant et des scripts de démarrage personnalisés. Vous découvrirez les concepts clés de l'architecture multi-tenant d'Oracle (CDB vs PDB) et comment vous connecter à votre base et l'administrer avec des outils comme `sqlplus`, Oracle SQL Developer et l'interface web Oracle Enterprise Manager. Au boulot, j'ai dû créer une base de données Oracle Enterprise en local pour pouvoir écrire des scripts dans différents langages et montrer [comment se connecter à Oracle avec, par exemple, .Net, NodeJS, Python ou PHP](/blog/oracle-dotnet-nodejs-php-python). L'idée a donc tout de suite été de vérifier s'il existait une image officielle. Elle existe mais, zut, ce n'est pas aussi simple que pour PostgreSQL, MySQL ou [MS SQL Server](/blog/docker-mssql-server). Dans cet article, qui est une mise au propre de mes notes, nous allons installer Oracle Enterprise en local dans un container, créer une base avec des données d'exemple et accéder à cette base de plusieurs façons. C'est un guide pas à pas pour faciliter la vie de ceux qui doivent faire la même chose — moi, par exemple, dans quelques mois, quand j'aurai tout oublié. Ça prend un moment (Oracle est lent à s'initialiser), mais voici où nous allons : un container Oracle qui tourne, prêt à être interrogé. ![La base de données est prête à l'emploi](./images/container_db_is_ready.webp) ![Oracle récupère la liste des employés](./images/oracle_getting_employees.webp) Construisons tout ça, en commençant par l'image elle-même. ## Télécharger une image Docker pour OracleDB {#download-a-docker-image-for-oracledb} Oui, il existe bien une image Docker appelée `container-registry.oracle.com/database/enterprise:latest` mais, avant de pouvoir la télécharger, il faudra faire quelques choses : 1. Créer un compte gratuit sur oracle.com 2. Générer un token 3. Accepter les termes de la licence 4. Configurer votre environnement Docker local pour utiliser ce token Si vous sautez une étape et essayez de télécharger l'image directement, vous obtiendrez cette erreur : ```bash $ docker pull container-registry.oracle.com/database/enterprise:latest Error response from daemon: Head "https://container-registry.oracle.com/v2/database/enterprise/manifests/21.3.0.0": unauthorized: Auth failed. ``` ### Création d'un compte gratuit sur oracle.com {#creation-of-a-free-account-on-oraclecom} Rendez-vous sur et créez un utilisateur en cliquant sur le bouton / lien `Sign In` en haut à droite. > Screenshot — https\://container-registry.oracle.com/ords/ocr/ba/database/enterprise > > Assurez-vous d'être connecté > Screenshot — https\://container-registry.oracle.com/ords/ocr/ba/database/enterprise > > Page de connexion Oracle ### Générer un token {#generate-a-token} Une fois votre utilisateur validé (après réception d'un email), retournez sur le site , connectez-vous, cliquez sur votre profil puis choisissez `Auth Token`. > Screenshot — https\://container-registry.oracle.com/ords/ocr/ba/database/enterprise > > Création de votre Auth Token Cliquez sur le bouton `Generate Secret Key` et copiez votre **SSO Username** et votre **Secret Key** quelque part (dans votre gestionnaire de mots de passe par exemple). ### Accepter les termes de la licence {#accept-license-terms} Rendez-vous sur et cherchez `enterprise` dans la colonne `Repository`. > Screenshot — https\://container-registry.oracle.com/ords/ocr/ba/database > > Cliquez sur enterprise Une fois connecté, cliquez sur le bouton `Continue` qui apparaît à droite. > Screenshot — https\://container-registry.oracle.com/ords/ocr/ba/database > > Acceptation des termes de la licence Une fenêtre popup s'affiche et, en bas, vous devrez cliquer sur `Accept`. Optionnel : en retournant sur , vous verrez qu'Oracle a bien enregistré le fait que vous avez accepté ses conditions. > Screenshot — https\://container-registry.oracle.com/ords/ocr/ba/database > > Acceptation du contrat de licence ### Configurer votre environnement Docker local {#configure-your-local-docker-environment} Dernière partie : apprendre à Docker que vous pouvez télécharger depuis le registre Oracle. Allez en ligne de commande et tapez `docker login container-registry.oracle.com`. Renseignez votre **SSO Username** et votre **Secret Key** (donnés par la page Auth Token sur oracle.com). Docker affichera *Login Succeeded*. > **Optionnel, voici la commande de déconnexion:** > > Si besoin, exécutez `docker logout container-registry.oracle.com` pour supprimer l'authentification vers oracle.com sur votre machine. > **Optionnel, l'authentification est stockée dans le fichier config.json:** > > En exécutant `cat ~/.docker/config.json`, vous verrez dans `auths` la présence du registre Oracle > > **\~/.docker/config.json** > > ```json > { > "auths": { > "container-registry.oracle.com": {}, > "https://index.docker.io/v1/": {} > } > } > ``` ### Enfin, vous pouvez récupérer l'image {#finally-you-can-pull-the-image} Si toutes les étapes précédentes ont été correctement réalisées, vous devriez maintenant pouvoir lancer `docker pull container-registry.oracle.com/database/enterprise:latest` sans erreur et télécharger l'image Docker de 3,5 Go. > **L'image latest est énorme mais la slim est trop ancienne:** > > Il existe une image slim d'environ 1,5 Go (`docker pull container-registry.oracle.com/database/enterprise:12.2.0.1-slim`) mais elle est très ancienne (quelque part en 2018). ## Concepts importants à connaître avec Oracle v12 et suivants (passez cette section si vous voulez juste le lancer) {#important-concepts-to-consider-when-working-with-oracle-v12-and-after-skip-this-if-you-just-want-to-run-it} ### Container database (CDB) versus Pluggable database (PDB) {#container-database-cdb-versus-pluggable-database-pdb} Dans l'architecture multi-tenant d'Oracle (introduite depuis Oracle 12c), les concepts de *Container Database* (`CDB`) et de **Pluggable Database** (`PDB`) sont fondamentaux. Par rapport aux autres moteurs de base de données, une `PDB` est une base de données tandis que la `CDB` est l'*infrastructure sous-jacente qui gère une ou plusieurs PDB, en partageant des processus d'arrière-plan et de la mémoire*. > **Dans cet article, nous ne travaillons jamais au niveau CDB:** > > Dans cet article, nous voulons uniquement créer des tables dans une base de données (PDB) — **nous devons toujours nous assurer d'être connectés à une PDB au moment de créer des tables.** Nous voulons que nos tables soient dans une seule base ; pas « partagées » à la racine du CDB. > > Donc, chaque fois que nous devrons travailler sur des tables, assurez-vous de vous connecter à la `PDB`. ## Créer un container de base de données Oracle {#create-an-oracle-database-container} Une fois l'image récupérée, nous pouvons créer notre container, c'est-à-dire notre service de base de données Oracle. La liste des options de ligne de commande supportées est ici : Si on le souhaite, on peut lancer une commande `docker run` tout de suite mais, avant, faisons un peu de configuration. Pourquoi ? Parce que la génération du container est terriblement lente et peut prendre jusqu'à dix minutes. Pour être le plus efficace possible, nous allons d'abord créer un ensemble de fichiers sur notre disque pour que, pendant la création de la base, Docker crée aussi automatiquement quelques tables et y insère des enregistrements. ### Création de quelques fichiers de configuration {#creation-of-some-configuration-files} Créez un nouveau dossier sur votre disque, allez-y, puis créez le dossier où nous mettrons nos fichiers de démarrage : ```bash $ mkdir -p /tmp/oracle && cd $_ $ mkdir -p scripts/startup ``` #### Créons une base d'exemple Human Resources {#lets-create-a-human-resources-sample-database} Oracle fournit des bases d'exemple sur la page [Oracle Database Sample Schemas](https://github.com/oracle-samples/db-sample-schemas/releases). Vous pouvez y télécharger l'archive `db-sample-schemas-23.3.zip`, l'ouvrir et aller dans le dossier `human_resources`. Vous y trouverez un fichier appelé `hr_create.sql` qui est un exemple de base RH. Il y a un second fichier appelé `hr_populate.sql` pour remplir les tables avec des données. > **Information:** > > Pour simplifier, vous pouvez récupérer ces fichiers en cliquant sur ces deux liens : [hr\_create.sql](./files/hr_create.sql) et [hr\_populate.sql](./files/hr_populate.sql). Téléchargez et copiez ces deux fichiers dans le dossier `scripts/startup/sql/` que vous avez créé précédemment. La deuxième chose à faire est de créer le fichier `scripts/startup/populate_db.sh` avec le contenu ci-dessous pour automatiser la création de nos tables et insérer des enregistrements dans notre base. Comme dit dans un chapitre précédent, il est important de bien se connecter à la `PDB` pour que les tables soient créées dans une base de données, pas dans le container. **scripts/startup/populate\_db.sh** ```bash #!/usr/bin/env bash sqlplus -S sys/admin@localhost:1521/ORCLCDB AS SYSDBA < **Les tables appartiendront à l'utilisateur system:** > > Faites attention à l'instruction `CONNECT system/admin@` orclpdb1 `: nous nous connectons d'abord en tant qu'utilisateur system avant de créer les tables. Les tables seront donc accessibles par cet utilisateur, à savoir l'utilisateur`system\`. Si vous avez besoin du modèle de données, le voici : ![Les relations de l'exemple Oracle Human Resources](./images/hr_schema.webp) ### Créer le volume OracleDBData {#create-the-oracledbdata-volume} Comme lancer le container est un processus très lent, nous allons créer un volume Docker pour garder la base persistante sur le disque. Le volume conservera la base sur notre disque, dans un volume géré par Docker. Ainsi, nous pouvons arrêter le container Oracle, le redémarrer et nous ne perdrons pas notre base. *La base sera persistante dans un volume auto-géré par Docker.* Lancez cette commande dans la console pour créer un volume géré par Docker : ```bash $ docker volume create OracleDBData ``` ### Créer le réseau oracle {#create-the-oracle-network} Et enfin, nous allons créer un réseau spécifique pour notre container Oracle : ```bash $ docker network create oracle ``` ### Prêt à créer le container {#ready-to-create-the-container} Nous sommes prêts à créer notre container en lançant cette commande : ```bash $ docker run -d \ --name oracle-db \ --network oracle \ -p 1521:1521 \ -p 5500:5500 \ -e ORACLE_SID=ORCLCDB \ -e ORACLE_PDB=ORCLPDB1 \ -e ORACLE_PWD=admin \ -v OracleDBData:/opt/oracle/oradata \ -v ./scripts/startup/:/docker-entrypoint-initdb.d/startup \ container-registry.oracle.com/database/enterprise:latest ``` > **Note:** > > C'est terriblement lent... Oracle aura besoin d'environ 10 minutes avant que le container soit utilisable. La commande `docker run` était terriblement complexe ; voici le détail : * `--name oracle-db` : notre container s'appellera oracle-db (nous pourrons donc y accéder plus tard avec `docker exec -it oracle-db [...]`), * `--network oracle` : nous créerons notre container sur le réseau `oracle`, * `-p 1521:1521` : Oracle utilise le port 1521 (Oracle Listener) et nous exposons ce port sur notre machine, * `-p 5500:5500` : optionnel. Oracle utilise un second port 5500 (OEM Express) et nous exposons également ce port pour pouvoir accéder à `http://localhost:`5500, * `-e ORACLE_SID=ORCLCDB` : le SID de la base Oracle (SID signifie *System identifier*) est un réglage très important. Nous gardons la valeur par défaut, à savoir ORCLCDB (rappel : `CDB` signifie `Container database`) * `-e ORACLE_PDB=ORCLPDB1` : le nom de la PDB Oracle. Ici aussi, nous gardons la valeur par défaut, à savoir ORCLPDB1. (rappel : `PDB` signifie `pluggable database`). * `-e ORACLE_PWD=admin` : le mot de passe des utilisateurs SYS, SYSTEM et PDBADMIN. Il n'y a pas de valeur par défaut puisque le mot de passe est généré automatiquement et doit être récupéré dans les logs (`docker log`). Ce paramètre modifie le mot de passe des utilisateurs SYS, SYSTEM et PDBADMIN. * `-v OracleDBData:/opt/oracle/oradata` : nous rendons la base persistante sur notre disque mais peu importe *où*, nous laissons Docker gérer ça. Nous avons créé précédemment un volume Docker nommé `OracleDBData`. * `-v ./scripts/startup/:/docker-entrypoint-initdb.d/startup` : cela nous permet de créer des fichiers en local dans le dossier `./scripts/startup` (comme nos fichiers SQL) et de les exécuter dans le container une fois la base Oracle principale prête. Et, en effet, nous avons créé précédemment les fichiers `populate_db.sh`, `sql/hr_create.sql` et `sql/hr_populate.sql` dans notre dossier `scripts/startup`. ## Constantes importantes à retenir {#important-constants-to-remember} À partir de maintenant, notre serveur de base de données Oracle tourne dans un container Docker. Nous allons beaucoup utiliser certaines constantes. Voici les constantes à retenir : * 1521 est le numéro de port à utiliser pour se connecter au service Oracle DB, * oracle-db est le nom de notre container, * ORCLCDB est le nom de notre CDB, * ORCLPDB1 est le nom de notre pluggable database, * `admin` est le mot de passe à utiliser et * `system` est l'utilisateur employé lors de la création de la base Si vous reprenez le script `populate_db.sh`, nous avions : **scripts/startup/populate\_db.sh** ```bash # highlight-next-line sqlplus -S sys/admin@localhost:1521/ORCLCDB AS SYSDBA < **Suis-je connecté au container database ou à une pluggable database ?:** > > Lancez simplement, dans la console SQL\*Plus, la commande `SHOW CON_NAME;`. > > ![Connecté à la PDB](./images/connected_on_pdb.webp) > > Si vous êtes connecté au CDB, vous obtiendrez la réponse suivante et c'est faux. Tapez `exit` et connectez-vous à la PDB. > > ![Connecté au CDB](./images/connected_on_cdb.webp) ### Un mot sur le schéma {#a-word-about-schema} Précédemment, nous avons créé nos tables avec le script `scripts/startup/populate_db.sh`. Voici le script utilisé : **scripts/startup/populate\_db.sh** ```bash #!/usr/bin/env bash sqlplus -S sys/admin@localhost:1521/ORCLCDB AS SYSDBA < **Information:** > > `SET WRAP OFF` nous permet d'utiliser toute la largeur de l'écran au lieu d'une largeur ridicule de 80 caractères et `SET PAGESIZE 1000` évite une liste paginée tous les 10 enregistrements. ### Accéder à Oracle Enterprise Manager Database Express {#accessing-the-oracle-enterprise-manager-database-express} Plus tôt, lors de notre commande `docker run`, nous avons mappé le port 5500 du container sur notre host. Ce port est celui utilisé par Oracle Enterprise Manager Database Express (alias `OEM Express`). C'est une application web accessible en surfant sur `https://localhost:` 5500`/em/`. Connectez-vous avec `system` / `admin` et orclpdb1 comme nom de container. > Screenshot — https\://localhost:5500/em/ > > Oracle Enterprise Manager Database Express ### Accéder à notre base avec les outils Oracle {#accessing-our-database-using-oracle-tools} #### Oracle SQL Developer {#oracle-sql-developer} Vous pouvez télécharger [Oracle SQL Developer](https://www.oracle.com/be/database/sqldeveloper). C'est un fichier ZIP et, une fois dézippé, vous pouvez lancer l'interface sans devoir installer le logiciel : c'est prêt à l'emploi. Une fois installé, lancez-le et créez une nouvelle connexion vers : | Label | Value | | -------------- | ----------- | | Utilisateur | `SYS` | | Rôle | `SYSDBA` | | Mot de passe | `admin` | | Nom d'hôte | `127.0.0.1` | | Port | `1521` | | Nom du service | `orclpdb1` | ![Création d'une connexion dans Oracle SQL Developer en tant que sys](./images/oracle_sql_dev_as_sys.webp) > **Rappel : vous devez vous connecter à notre PDB, pas au CDB:** > > Les tables se trouvent dans la *pluggable database* (`PDB`), pas dans le *container database* (`CDB`) ![Récupération des données avec Oracle SQL Developer](./images/oracle_sql_get_data.webp) Commencez à taper `SELECT * FROM system.` et appuyez sur `CTRL`+`SPACE` pour obtenir la liste des objets de ce schéma. En descendant un peu, vous trouverez employees. ![Oracle récupère les employés](./images/oracle_get_employees.webp) Validez ou tapez simplement le SQL complet `SELECT * FROM system.employees`. Dans l'exemple ci-dessous, je demande la liste des régions et la liste des employés. Je lance la query en appuyant simplement sur `F5` — le même résultat que celui déjà montré en début d'article. ##### Se connecter à Oracle SQL Developer avec le bon utilisateur {#connect-to-oracle-sql-developer-using-the-correct-user} Dans le chapitre précédent, nous avons créé une connexion avec `sys` comme nom d'utilisateur et, pour cette raison, si nous cliquons sur le menu `Tables (Filtered)` dans l'arborescence de gauche, nous obtiendrons un paquet de tables mais pas celles de l'utilisateur `system`, comme notre table employees. ![Création d'une connexion dans Oracle SQL Developer en tant que system](./images/oracle_sql_dev_as_system.webp) Maintenant, en dépliant la liste des tables, nous voyons les nôtres : ![Nos tables Human Resources](./images/oracle_sql_dev_tables.webp) #### Accéder à notre container Oracle DB avec Oracle SQLPlus {#access-our-oracle-db-container-using-oracle-sqlplus} Une autre façon serait d'utiliser l'image Docker officielle sql\*plus comme ceci : ```bash $ docker run --rm -it --network oracle oracletools/sqlplus:v19.18_lin SYS/admin@oracle-db:1521/orclpdb1 as sysdba ``` Une fois connectés, nous pouvons par exemple obtenir la liste des pays comme ceci : ![Oracle sqlplus](./images/oracle_sqlplus_countries.webp) > **Interne versus externe:** > > Pour cet article, il n'était pas vraiment nécessaire d'utiliser cette image. Jusqu'ici, nous avons utilisé SQL\*Plus plus d'une fois, par exemple quand nous avons lancé `docker exec -it ` oracle-db ` sqlplus sys/admin@` ORCLPDB1 ` as sysdba` pour nous connecter à la base. > > La différence : `docker exec -it ` oracle-db ` [...]` entre dans le container de notre base et lance sqlplus *en interne* (depuis le container où la base est stockée) tandis que `docker run [...] oracle oracletools/sqlplus:v19.18_lin [...]` lance sqlplus *à l'extérieur*, dans un container séparé. ## Pour aller plus loin {#going-further} Maintenant que le container oracle-db tourne, deux articles de suite le réutilisent directement : [Transform an Oracle DB as OpenData using Oracle REST Data Services](/blog/docker-oracle-ords) et [Accessing an Oracle database using .Net, NodeJS, PHP and Python](/blog/oracle-dotnet-nodejs-php-python). --- # Transformer une base Oracle en OpenData avec Oracle REST Data Services > Mettez en place Oracle REST Data Services (ORDS) dans Docker pour transformer votre base Oracle en OpenData avec une API REST puissante. Filtrage et pagination inclus. --- ![Transformer une base Oracle en OpenData avec Oracle REST Data Services](/img/v2/oracle.webp) **TL;DR** Ce tutoriel montre comment transformer votre base de données Oracle en une API RESTful puissante grâce à Oracle REST Data Services (ORDS) dans Docker. En partant d'un container Oracle DB déjà mis en place précédemment, ce guide vous accompagne dans le déploiement du container ORDS, sa liaison avec votre base de données et la configuration des schémas pour l'accès REST. Vous apprendrez à activer REST sur des tables ou des vues précises avec Oracle SQL Developer, puis à accéder à vos données via des endpoints OpenAPI générés automatiquement. L'article explique aussi comment utiliser les paramètres d'URL pour des requêtes avancées : filtrage, tri et pagination. Dans un article précédent ([Running Oracle Database Server as a Docker container](/blog/docker-oracle-database-server)), nous avons créé un container Docker appelé `oracle-db` dans lequel tourne une base de données Human Resources. Dans cet article, nous allons exposer partiellement la base de données sur le web avec OpenAPI. En résumé, nous allons permettre à des personnes autorisées d'accéder par exemple à `http://oursite/api/employees` pour obtenir la liste de tous les employés. *Ce schéma OpenAPI généré peut d'ailleurs être linté ; voyez [Validate your OpenAPI schema against the Belgif REST standards](/blog/belgif-api-linter).* Exactement comme nous l'avons fait avec PostgREST (voir mon article [Don't query your PostgreSQL db anymore, prefer PostgREST](/blog/docker-postgrest)) mais, cette fois, avec une base de données Oracle. Et la magie viendra de **[Oracle REST Data Services](https://www.oracle.com/database/technologies/appdev/rest.html)**, aussi appelé **ords**. Voici où nous allons : la même table Oracle, désormais accessible en simple JSON via HTTP. ![Récupérer la liste des employés en JSON avec curl](./images/getting_employees_as_json_curl.webp) Dans l'article précédent ([Running Oracle Database Server as a Docker container](/blog/docker-oracle-database-server)), nous avons créé un container Docker appelé `oracle-db` dans lequel tourne une base de données Human Resources. Lisez cet article et suivez les étapes pour disposer, avant de continuer ici, d'un container Docker fonctionnel comme décrit ci-dessous. ## Quelques prérequis {#some-prerequisites} **Avant de commencer, assurez-vous que...** 1. Vous avez un container Docker appelé `oracle-db` en cours d'exécution 2. Le container tourne donc sur votre `localhost` 3. Le numéro de port pour accéder à votre base est `1521` 4. Les identifiants à utiliser sont `SYS` (ou `SYSTEM`) et `admin` comme mot de passe 5. Le container contient une base appelée `ORCLPDB1` (aussi appelée nom de service) qui est l'exemple Human Resources d'Oracle (voyez l'article mentionné) et 6. Notre base `oracle-db` tourne sur un réseau appelé `oracle` ## Télécharger une image Docker pour Oracle REST Data Services {#download-a-docker-image-for-oracle-rest-data-services} Contrairement à l'image Docker Oracle Database Server, aucun identifiant n'est nécessaire pour télécharger l'image officielle [Oracle ORDS](https://container-registry.oracle.com/ords/ocr/ba/database/ords-developer). Lancez simplement `docker pull container-registry.oracle.com/database/ords-developer:latest` pour la récupérer. C'est une image de 2,2 Go. ```bash $ docker pull container-registry.oracle.com/database/ords-developer:latest ``` ## Créer un container Oracle ORDS {#create-an-oracle-ords-container} Nous allons procéder étape par étape. ### Créer un volume pour la configuration ORDS {#create-a-volume-for-the-ords-configuration} ORDS a besoin de deux dossiers : un pour ce qu'il appelle **ses secrets** et un pour **ses fichiers de configuration**. Utilisons un volume Docker auto-géré pour les fichiers de configuration ; lancez donc `docker volume create ords_config` dans votre console. ### Configurer la chaîne de connexion {#configure-the-connection-string} Pour pouvoir démarrer ORDS, la doc officielle indique qu'il faut créer un fichier appelé `conn_string.txt` avec notre chaîne de connexion. Dans notre cas, voici le fichier à créer : ```bash $ mkdir -p ords_secrets $ chmod 777 ords_secrets $ echo 'CONN_STRING=SYS/admin@oracle-db:1521/ORCLPDB1' > ords_secrets/conn_string.txt ``` > **Information:** > > Si vous avez un doute sur la valeur à utiliser comme `service_name` : démarrez une console sqlplus (`docker exec -it ` oracle-db ` sqlplus sys/admin@` ORCLPDB1 ` as sysdba`) et lancez `SELECT global_name FROM global_name;` dans SQL\*Plus. > > ![Récupérer le nom de service](./images/getting_service_name.webp) L'image Docker officielle ORDS est livrée avec APEX (l'abréviation d'*Oracle Application Express*). D'après la documentation, *Oracle Application Express (APEX) est une plateforme de développement low-code qui vous permet de construire des applications d'entreprise évolutives et sécurisées, dotées de fonctionnalités de premier plan, déployables n'importe où. Voir pour plus d'informations.* Pour nos besoins, nous voulons juste ORDS ; APEX ne nous sert à rien. Pour cela, nous allons initialiser la variable d'environnement `IGNORE_APEX` à `TRUE`. ### Créer le container {#create-the-container} Pour créer notre container ORDS (sans APEX), lancez : ```bash $ docker run -d --rm \ --name oracle-ords \ --network oracle \ -p 8181:8181 \ -e IGNORE_APEX=TRUE \ -v ./ords_secrets/:/opt/oracle/variables \ -v ords_config:/etc/ords/config/ \ container-registry.oracle.com/database/ords-developer:latest ``` > **Le `docker run` expliqué:** > > * `-d`: ORDS tournera comme service daemon, > * `--rm`: une fois le service terminé, le container Docker sera supprimé, > * `--network`: nous devons faire tourner ORDS sur le même réseau que notre base de données, c'est-à-dire `oracle` > * `-p > 8181 > :8181`: nous devons mapper le port interne `8181` utilisé par ORDS sur notre host et, par simplicité, nous exposerons aussi le port 8181, > * `-e IGNORE_APEX=TRUE`: comme dit, nous voulons ORDS, pas APEX ; on dit donc à Docker de ne pas installer APEX, > * `-v ./ords_secrets/:/opt/oracle/variables`: comme indiqué dans la documentation, nous devons fournir un fichier appelé `conn_string.txt` et mapper ce fichier dans `/opt/oracle/variables` à l'intérieur du container et, enfin, > * `-v ords_config:/etc/ords/config/`: nous utiliserons un volume Docker auto-géré pour conserver les fichiers de configuration d'ORDS. Cette commande va ajouter le layer ORDS dans votre base de données (sur base de la chaîne de connexion, soit ORCLPDB1 chez nous). ![Installation d'ORDS](./images/ords_installation.webp) > **Aux lancements suivants, il ne faut plus fournir la chaîne de connexion.:** > > Une fois ORDS installé comme ci-dessus, si vous devez relancer le container ORDS, il ne faut plus fournir le secret : il faut donc retirer le flag `-v ./ords_secrets/:/opt/oracle/variables`. > > Si vous devez relancer le container, voici la ligne de commande : > > ```bash > $ docker run -d --rm \ > --name oracle-ords \ > --network oracle \ > -p 8181:8181 \ > -v ords_config:/etc/ords/config/ \ > container-registry.oracle.com/database/ords-developer:latest > ``` ### Nous devons créer notre utilisateur dans la base de données {#we-need-to-create-our-user-in-our-database} Avant de pouvoir utiliser ORDS et accéder à des objets via une requête HTTP, nous devons créer un nouvel utilisateur dans notre base et lui donner quelques droits. Lancez `docker exec -it ` oracle-db ` sqlplus sys/admin@` ORCLPDB1 ` as sysdba` pour démarrer une console SQL\*Plus et vous connecter à la base PDB. Nous devons créer un utilisateur pour ORDS ; disons l'utilisateur `hr`. Lancez cette instruction : `CREATE USER hr IDENTIFIED BY admin;` Puis accordez les permissions : `GRANT CONNECT, RESOURCE, UNLIMITED TABLESPACE TO hr;` ![Créer l'utilisateur ORDS](./images/ords_create_user.webp) Une fois créé, nous devons quitter la console (démarrée avec l'utilisateur `sys`) et nous reconnecter avec notre utilisateur. Tapez donc `exit` pour quitter la console sqlplus et lancez `docker exec -it ` oracle-db ` sqlplus hr/admin@` ORCLPDB1. De retour dans la console sqlplus (connecté en tant qu'utilisateur `hr`), lancez : `EXECUTE ORDS.ENABLE_SCHEMA;`. ![Activation du schéma ORDS](./images/ords_enable_schema.webp) *(lisez [ORDS 101: Enabling Oracle Schemas for HTTPS/REST](https://www.thatjeffsmith.com/archive/2023/09/ords-101-enabling-oracle-schemas-for-https-rest/) si vous voulez plus de détails)* > **Caution:** > > Si vous obtenez une erreur à ce stade, cela signifie qu'ORDS n'a pas été installé dans votre base de données. Relisez le chapitre **Créer le container**. #### Démarrer l'interface web d'ORDS {#start-ords-web-interface} À ce stade, nous avons installé ORDS, configuré notre base pour l'utiliser et créé un utilisateur appelé `hr`. Nous pouvons nous rendre sur `http://localhost:` 8181`/ords` et nous connecter au dashboard ORDS : > Screenshot — http\://localhost:8181/ords/\_/landing > > Page d'accueil d'ORDS > **Note:** > > APEX n'a pas été installé et est donc désactivé Utilisez `hr` et `admin`, notre utilisateur personnalisé, sur la page de login : > Screenshot — http\://localhost:8181/ords/\_/landing > > Page de login d'ORDS Dès à présent, nous pouvons demander à ORDS la liste des objets déjà accessibles ; rendez-vous simplement sur `http://localhost:` 8181`/ords/hr/open-api-catalog/` pour obtenir... une liste vide. C'est normal puisque nous devons indiquer quel objet (une table, une vue, une procédure stockée) est accessible ou non. Mais oui, en accédant à `http://localhost:` 8181`/ords/hr/open-api-catalog/` et en obtenant une réponse JSON, nous pouvons confirmer qu'ORDS tourne correctement. La page `http://localhost:` 8181`/ords/hr/open-api-catalog/` s'appelle le **Schema Metadata** ([documentation](https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/21.4/aelig/developing-REST-applications.html#GUID-55736274-502E-4511-B232-829924334FA2)). #### Activer REST sur les objets de la base {#rest-enable-database-objects} Nous devons donc activer chaque objet un par un. Le plus simple est d'utiliser [Oracle SQL Developer](https://www.oracle.com/be/database/sqldeveloper). Si vous ne l'avez pas, téléchargez-le gratuitement. ![Oracle SQL Developer en cours d'exécution](./images/starting_sql_dev.webp) Créez une nouvelle connexion avec ces paramètres : | Label | Value | | ----------------- | ------------------------ | | Nom | `Human Resources - ORDS` | | Nom d'utilisateur | `hr` | | Rôle | `default` | | Mot de passe | `admin` | | Nom d'hôte | `127.0.0.1` | | Port | `1521` | | Nom de service | `orclpdb1` | ![SQL Developer - Connexion à la base de données](./images/sql_dev_login_page.webp) Cliquez sur le bouton `Save` puis sur `Connect`. Juste après le login, en cliquant sur la liste des tables ou des vues, il n'y a rien. C'est normal : nous venons de créer notre utilisateur `hr` et le schéma `hr` associé, et il n'y a aucun objet dans ce schéma. ![Pour l'instant, il n'y a rien sur quoi activer REST](./images/sql_dev_hr_no_objects.webp) ##### Ajouter une vue employee dans notre schéma hr {#adding-an-employee-view-in-our-hr-schema} Connectons-nous à notre base et affichons la liste des employés : lancez `docker exec -it ` oracle-db ` sqlplus sys/admin@` ORCLPDB1 ` as sysdba`, puis `SELECT EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL FROM SYSTEM.EMPLOYEES;`. ![Récupérer la liste des employés](./images/system_employees.webp) Maintenant que nous sommes sûrs d'avoir accès à la table, créons une vue dans notre schéma `hr` pour afficher le contenu de la table : * lancez `GRANT SELECT ON SYSTEM.EMPLOYEES TO hr;` et * créez la vue en lançant `CREATE VIEW HR.EMPLOYEES AS SELECT EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB_ID, SALARY, COMMISSION_PCT, MANAGER_ID, DEPARTMENT_ID FROM SYSTEM.EMPLOYEES;`. Si nous voulons vérifier notre vue : * Tapez `exit` dans la console sqlplus puisque vous êtes connecté en tant que `sys`, * Dans la console Linux, lancez `docker exec -it ` oracle-db ` sqlplus hr/admin@` ORCLPDB1 pour vous connecter en tant que `hr`, puis * lancez `SELECT * FROM HR.EMPLOYEES;`. Comme vous le voyez, vous obtenez la liste des employés. À ce stade, nous avons donc ajouté une vue appelée `employees` dans notre schéma `hr` et elle fonctionne. > **N'utilisez pas SELECT \* FROM:** > > C'est une très mauvaise pratique d'utiliser `SELECT * FROM ...`, veillez toujours à sélectionner uniquement les champs nécessaires. Retournez dans l'interface Oracle SQL Developer, faites un clic droit sur le nœud `Views` et choisissez `Refresh`. Vous verrez la vue ajoutée : ![La vue employees](./images/sql_dev_employees_view.webp) Clic droit sur la vue `EMPLOYEES` puis cliquez sur `Enable REST Service...`. ![Activation de REST](./images/sql_dev_enable_rest.webp) Veillez à cocher la case `Enable object`, donnez un nom à votre objet et, pour ce petit tutoriel, décochez la case `Authorization required`. ![Activer REST sur la vue employees](./images/rest_enable_employees_1.webp) Cliquez sur le bouton `Next` et prêtez attention à l'onglet `SQL` : ![Activer REST sur la vue employees - partie DDL](./images/rest_enable_employees_2.webp) Vous obtiendrez le DDL à exécuter dans une console si vous souhaitez activer REST sur la table par le code, plutôt qu'avec l'interface graphique. La dernière étape est de passer sur le site ORDS (`http://localhost:` 8181`/ords/hr/_sdw/?nav=rest-workshop`). Rafraîchissez la page et vous verrez maintenant que vous avez un objet dans la zone `AUTOREST` : > Screenshot — http\://localhost:8181/ords/hr/\_sdw/?nav=rest-workshop > > Il y a un objet dans la zone AUTOREST Cliquez sur cette zone et vous verrez l'objet (qui est une vue) sur lequel nous avons activé REST précédemment : > Screenshot — http\://localhost:8181/ords/hr/\_sdw/?nav=rest-workshop > > REST est activé sur Employees via AUTOREST Regardez l'icône *Open in a new tab* sur l'image ci-dessus. Cliquez sur ce bouton et vous obtiendrez vos enregistrements en réponse JSON : > Screenshot — http\://localhost:8181/ords/hr/employees/ > > Récupérer la liste des employés en réponse REST JSON L'URL `http://localhost:` 8181`/ords/hr/employees/` s'appelle l'**Object Data** ([documentation](https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/21.4/aelig/developing-REST-applications.html#GUID-0B17836D-E5B5-4B45-A9DA-0ABF62426EDF)) Comme ce n'est rien de plus qu'une URL, vous pouvez l'utiliser avec n'importe quel outil, par exemple `curl`. La commande à lancer est `curl http://localhost : 8181 /ords/hr/employees/ | jq` — la même sortie que celle montrée en début d'article. ###### Et qu'en est-il du catalogue OpenAPI {#and-what-about-the-openapi-catalog} Retournez dans votre navigateur et accédez à nouveau à `http://localhost:` 8181`/ords/hr/open-api-catalog/`. Cette fois, vous verrez que vous avez un élément appelé `EMPLOYEES`. Si vous voulez obtenir la description de cette *ressource*, la page `http://localhost:` 8181`/ords/hr/open-api-catalog/employees/` peut être visitée pour obtenir par exemple la liste des champs exposés. Cette page s'appelle l'**Object Metadata** ([documentation](https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/21.4/aelig/developing-REST-applications.html#GUID-B870CF7E-4A19-4646-ACB4-84CC9FD5967E)) Créons une nouvelle vue pour le plaisir : * Lancez `docker exec -it ` oracle-db ` sqlplus sys/admin@` ORCLPDB1 ` as sysdba` puis * Dans la console SQL, lancez `GRANT SELECT ON SYSTEM.DEPARTMENTS TO HR;` suivi de * `CREATE VIEW HR.DEPARTMENTS AS SELECT DEPARTMENT_ID, DEPARTMENT_NAME, MANAGER_ID, LOCATION_ID FROM SYSTEM.DEPARTMENTS;` et * Passez dans l'interface Oracle SQL Developer, rafraîchissez la liste des vues, faites un clic droit sur la nouvelle vue `departments` Maintenant, en rafraîchissant à nouveau `http://localhost:` 8181`/ords/hr/open-api-catalog/`, vous verrez que vous avez désormais un deuxième élément appelé `DEPARTMENTS`. > Screenshot — http\://localhost:8181/ords/hr/open-api-catalog/ > > Accès au metadata-catalog ##### Travailler avec REST {#working-with-rest} Rendez-vous sur `http://localhost:` 8181`/ords/hr/_sdw/?nav=rest-workshop` ou, depuis le menu hamburger, cliquez sur l'élément `REST`. > Screenshot — http\://localhost:8181/ords/hr/\_sdw/?nav=rest-workshop > > Dashboard REST ### Utiliser les options de pagination, de filtrage et de tri dans la querystring (facultatif — passez si l'endpoint par défaut suffit) {#using-paging-filtering-and-ordering-options-on-the-querystring-optional--skip-if-the-default-endpoint-is-enough} Lors de l'accès à un endpoint (comme `http://localhost:` 8181`/ords/hr/employees/`), nous pouvons manipuler l'URL pour ajouter des paramètres de pagination, de filtrage ou de tri. On parle de **JSON QBE**, pour JSON query-by-example. Voyez la documentation officielle : #### Pagination {#pagination} > [Doc](https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/21.4/aelig/developing-REST-applications.html#GUID-6B5B51E6-0F35-4FB6-B271-FCC31E835347) Sauf configuration spécifique, ORDS utilise la pagination, c'est-à-dire qu'il limite la liste des enregistrements à 25 rows. Donc, si vous avez plus de 25 employés, l'endpoint `http://localhost:` 8181`/ords/hr/employees/` n'en retournera que les 25 premiers. En accédant à une page « complète » comme `http://localhost:` 8181`/ords/hr/employees/`, nous ne recevrons en fait qu'un nombre précis d'enregistrements (selon la configuration d'ORDS). À la fin de la réponse JSON, il y aura des liens de navigation : > Screenshot — http\://localhost:8181/ords/hr/employees/ > > Pagination Comme vous le voyez sur l'image, la réponse JSON fournit une liste d'enregistrements (dans un tableau nommé `items`) puis quelques propriétés comme `hasMore` (true/false), `limit` c'est-à-dire le nombre de rows à chaque appel, `offset` qui est la « page » (à 0 les 25 premières rows sont affichées, à 1 les rows 26 à 50, ...), `count` qui est le nombre de rows dans la réponse. Donc, tant que `hasMore` vaut `true`, il reste des enregistrements à récupérer avec une requête suivante. Il y a deux paramètres pour contrôler la pagination des résultats : `offset` et `limit`. Nous pouvons donc lancer plusieurs requêtes comme `http://localhost:` 8181`/ords/hr/employees/?offset=25`, `http://localhost:` 8181`/ords/hr/employees/?offset=50` et ainsi de suite. Nous pouvons définir le nombre d'enregistrements par page avec le paramètre `limit` de la querystring : `http://localhost:` 8181`/ords/hr/employees/?limit=100`. > **Note:** > > La documentation officielle déconseille fortement de supprimer la limite (et donc de demander tous les enregistrements d'un coup). C'est pourquoi il n'existe aucun moyen de la supprimer. Si vous voulez vraiment obtenir la liste complète, essayez avec un nombre très élevé comme `limit=1000000` (un million). #### Filtrage {#filtering} La syntaxe générale est `/?q={"column":{"$eq":"value"}}`. ##### Supérieur à {#greater-than} Par exemple, qui gagne plus de 20 000 € de salaire ? `http://localhost:` 8181`/ords/hr/employees/?q={"salary":{"$gt":20000}}` appliquera un filtre sur `salary > 20000`. > Screenshot — http\://localhost:8181/ords/hr/employees/?q={"salary":{"$gt":20000}} > > Qui gagne plus de 20k ##### Égal {#equal} Pour trouver tous les employés appelés `Steven` : `http://localhost:` 8181`/ords/hr/employees/?q={"first_name":{"$eq":"Steven"}}` > Screenshot — http\://localhost:8181/ords/hr/employees/?q={"first\_name":{"$eq":"Steven"}} > > Steven ##### Instring / contains / like {#instring--contains--like} Les employés ayant le motif `alex` dans leur prénom : `http://localhost:` 8181`/ords/hr/employees/?q={"first_name":{"$instr":"alex"}}` (correspondra par exemple à `Alexander` ou `Alexis`). > Screenshot — http\://localhost:8181/ords/hr/employees/?q={"first\_name":{"$instr":"alex"}} > > Contient Alex Qui travaille dans l'ICT ? Ici, c'est l'opérateur like qu'il faut utiliser : `http://localhost:` 8181`/ords/hr/employees/?q={"job_id":{"$like":"IT_%"}}` (`IT_%` signifie *commence par*) > Screenshot — http\://localhost:8181/ords/hr/employees/?q={"job\_id":{"$like":"IT\_%"}} > > Qui travaille pour l'IT ##### Filtrage complexe {#complex-filtering} Nous pouvons aussi utiliser AND comme dans cet exemple : `http://localhost:` 8181`/ords/hr/employees/?q={"first_name":{"$instr":"alex"},"salary":{"$gt":5000}}`, c'est-à-dire récupérer toutes les personnes ayant `alex` dans leur prénom et gagnant plus de 5 000 € > Screenshot — http\://localhost:8181/ords/hr/employees/?q={"first\_name":{"$instr":"alex"},"salary":{"$gt":5000}} > > Contient Alex et gagne plus de 5 000 > **Utiliser le filtrage complexe:** > > L'URL suivante `http://localhost:` 8181`/ords/hr/employees/?q={"job_id":{"$like":"%CLERK"},"salary":{"$gt":2899},"hire_date":{"$gt":{"$date":"2016-12-31T12:59:59Z"}}}` retournera tous les employés qui : > > * travaillent comme clerk (`job_id` se terminant par le mot `CLERK`), > * ont un salaire supérieur à 2 899 € et > * ont été engagés à partir du 1er janvier 2017. > > > Screenshot — http\://localhost:8181/ords/hr/employees/?q={"job\_id":{"$like":"%CLERK"},"salary":{"$gt":2899},"hire\_date":{"$gt":{"$date":"2016-12-31T12:59:59Z"}}} > > > > Utiliser une combinaison de filtres #### Tri {#sorting} La syntaxe générale est `/?q={"$orderby":{"fieldname1":"asc","fieldname2":"desc"}}`. Pour trier sur le prénom en desc puis sur le salaire (le plus élevé en premier) : `http://localhost:` 8181`/ords/hr/employees/?q={"$orderby":{"first_name":"desc","salary":"desc"}}`. > Screenshot — http\://localhost:8181/ords/hr/employees/?q={"$orderby":{"first\_name":"desc","salary":"desc"}} > > Tri sur le prénom et le salaire DESC ### Swagger {#swagger} ORDS peut générer un fichier `openapi.json` à utiliser avec Swagger. Pour en savoir plus : [Manage & Monitor Oracle Database with REST APIs](https://www.thatjeffsmith.com/archive/2022/08/manage-monitor-your-oracle-database-with-rest-apis/) ## Pour aller plus loin {#further-reading} * [ORDS best practices topics (www.oracle.com)](https://www.oracle.com/database/technologies/appdev/rest/best-practices/) * [Installation, Configuration, and Development Guide (docs.oracle.com)](https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/21.4/aelig/toc.htm#GUID-A1CD111F-724B-4E91-8202-FA899EE521F1) * [Blog de Jeff Smith - Articles sur ORDS](https://www.thatjeffsmith.com/archive/tag/ords/) * [Adding HTTP(S) Access Logs for ORDS Standalone](https://www.thatjeffsmith.com/archive/2022/09/adding-https-access-logs-to-ords-standalone/) --- # Accéder à une base de données Oracle avec .Net, NodeJS, PHP et Python > Guide pas à pas pour accéder à un container de base de données Oracle avec .Net, Node.js, PHP et Python. Inclut le Dockerfile et des exemples de code complets pour chaque langage. --- ![Accéder à une base de données Oracle avec .Net, NodeJS, PHP et Python](/img/v2/oracle.webp) **TL;DR** Ce guide pas à pas montre comment se connecter à une base de données Oracle depuis .Net, Node.js, PHP et Python. Pour chaque langage, vous trouverez un exemple complet, prêt à l'emploi, avec le `Dockerfile` permettant de construire un container contenant toutes les dépendances nécessaires, ainsi qu'un script d'exemple pour interroger la base. Cet article fournit un template de démarrage rapide pour toute personne devant intégrer une base Oracle dans son application, quel que soit le langage de programmation. Dans un article précédent ([Running Oracle Database Server as a Docker container](/blog/docker-oracle-database-server)), nous avons créé un container Docker appelé `oracle-db` dans lequel tourne une base de données « Human Resources ». Cette fois, amusons-nous avec .Net, NodeJS, PHP et Python pour y accéder et afficher des enregistrements. Pour chaque langage, nous allons construire une image Docker avec tout le nécessaire, comme l'Oracle Instant Client, puis nous écrirons un petit script pour nous connecter à la table `employees` et afficher les enregistrements. L'idée est de fournir un squelette très rapide pour démarrer un projet dans ces langages lorsque vous avez besoin de vous connecter directement à une base Oracle. *Si vous préférez ne pas écrire de connecteur du tout, [Transform an Oracle DB as OpenData using Oracle REST Data Services](/blog/docker-oracle-ords) expose les mêmes tables sous forme d'API REST que n'importe quel langage peut consommer avec un simple appel HTTP.* Voici le résultat, quel que soit le langage choisi ci-dessous : un petit container Docker qui se connecte et affiche la table des employés. ![Utiliser .Net pour accéder à la liste des employés](./images/using_dotnet.webp) Dans l'article [Running Oracle Database Server as a Docker container](/blog/docker-oracle-database-server), nous avons créé un container Docker appelé `oracle-db` dans lequel tourne une base de données « Human Resources ». Merci de lire cet article et d'en suivre les étapes afin d'avoir, avant de continuer ici, un container Docker fonctionnel tel que décrit ci-dessous. ## Quelques prérequis {#some-prerequisites} **Avant de commencer, assurez-vous que...** 1. Vous avez un container Docker en cours d'exécution appelé `oracle-db`, 2. Le container tourne donc sur votre `localhost`, 3. Le numéro de port pour accéder à votre base de données est `1521`, 4. Les identifiants à utiliser sont `SYS` (ou `SYSTEM`) et `admin` comme mot de passe, 5. Le container contient une base de données appelée `ORCLPDB1` (aussi appelée nom de service) qui est l'exemple Human Resources d'Oracle (voir l'article mentionné) et 6. Notre base `oracle-db` tourne sur un réseau appelé `oracle`. ## Les exemples de code ont été largement créés avec l'IA {#the-code-samples-were-largely-created-using-ai} Parce que c'est rapide et aussi parce que je ne connais pas .Net et très peu NodeJS, j'ai utilisé Google Gemini pour construire mes exemples ci-dessous. Parfois, comme pour .Net, c'était presque prêt à l'emploi. Parfois, comme pour PHP, c'était plus compliqué et il m'a fallu pas mal de bidouillage pour trouver la bonne configuration. L'objectif de cet article était néanmoins de fournir le minimum de fichiers pour se connecter à une base Oracle, exécuter une requête SELECT et afficher les résultats ; pas d'avoir une base de code exemplaire. ## Accéder à notre container Oracle DB avec .Net {#access-our-oracle-db-container-using-net} Premier langage par ordre alphabétique ; jouons avec un container .Net : ```bash $ mkdir -p /tmp/oracle/dotnet && cd $_ ``` Créez aussi ce répertoire : ```bash $ mkdir -p /tmp/oracle/dotnet/OracleConnector ``` Créons un Dockerfile : **Dockerfile** ```docker # cspell:ignore libaio1,instantclient,libclntsh,libocci,ldconfig FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /app COPY OracleConnector/*.csproj ./OracleConnector/ RUN dotnet restore ./OracleConnector/OracleConnector.csproj COPY OracleConnector/. ./OracleConnector/ RUN dotnet publish ./OracleConnector/OracleConnector.csproj -c Release -o out FROM mcr.microsoft.com/dotnet/runtime:8.0 AS final WORKDIR /app COPY --from=build /app/out . # Install necessary Oracle Instant Client libraries ARG ORACLE_INSTANT_CLIENT_VERSION=21.12 ARG ORACLE_DOWNLOAD_URL=https://download.oracle.com/otn_software/linux/instantclient/instantclient-basic-linuxx64.zip RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ unzip \ libaio1 \ && rm -rf /var/lib/apt/lists/* RUN curl -sSL "${ORACLE_DOWNLOAD_URL}" -o instantclient.zip \ && mkdir -p /usr/lib/oracle/instantclient \ && unzip -qq instantclient.zip -d /usr/lib/oracle/instantclient \ && rm instantclient.zip \ && ln -s /usr/lib/oracle/instantclient/libclntsh.so.${ORACLE_INSTANT_CLIENT_VERSION}.1 /usr/lib/oracle/instantclient/libclntsh.so \ && ln -s /usr/lib/oracle/instantclient/libocci.so.${ORACLE_INSTANT_CLIENT_VERSION}.1 /usr/lib/oracle/instantclient/libocci.so \ && echo "/usr/lib/oracle/instantclient" > /etc/ld.so.conf.d/oracle-instantclient.conf \ && ldconfig ENTRYPOINT ["dotnet", "OracleConnector.dll"] ``` Passons maintenant à la partie DotNet. Nous avons besoin de deux fichiers : `OracleConnector/OracleConnector.csproj` et `OracleConnector/main.cs`. **OracleConnector/OracleConnector.csproj** ```csproj Exe net8.0 enable enable ``` **OracleConnector/main.cs** ```cs // cspell:ignore orclpdb1 using Oracle.ManagedDataAccess.Client; using System; namespace OracleConnector { class Program { static void Main(string[] args) { string dbHost = Environment.GetEnvironmentVariable("ORACLE_HOST") ?? "oracle-db"; string dbPort = Environment.GetEnvironmentVariable("ORACLE_PORT") ?? "1521"; string dbService = Environment.GetEnvironmentVariable("ORACLE_SERVICE") ?? "orclpdb1"; string dbUser = Environment.GetEnvironmentVariable("ORACLE_USER") ?? "system"; string dbPassword = Environment.GetEnvironmentVariable("ORACLE_PASSWORD") ?? "admin"; string connectionString = $"Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST={dbHost})(PORT={dbPort})))(CONNECT_DATA=(SERVICE_NAME={dbService})));User ID={dbUser};Password={dbPassword};"; try { using (OracleConnection connection = new OracleConnection(connectionString)) { connection.Open(); string sql = "SELECT employee_id, first_name, last_name, email FROM employees WHERE ROWNUM <= 25"; using (OracleCommand command = new OracleCommand(sql, connection)) using (OracleDataReader reader = command.ExecuteReader()) { Console.WriteLine("\nEmployee ID | First Name | Last Name | Email"); Console.WriteLine("------------|----------------------|----------------------|-------------------------"); while (reader.Read()) { int employeeId = reader.GetInt32(0); string firstName = reader.GetString(1); string lastName = reader.GetString(2); string email = reader.GetString(3); Console.WriteLine($"{employeeId,-12}| {firstName,-21}| {lastName,-21}| {email}"); } } } } catch (OracleException ex) { Console.WriteLine($"Error connecting to or querying Oracle: {ex.Message}"); } catch (Exception ex) { Console.WriteLine($"An unexpected error occurred: {ex.Message}"); } } } } ``` Voici à quoi ressemble votre projet dans VSCode : ![Projet .Net dans VSCode](./images/vscode_dotnet.webp) Toujours dans la console, dans le dossier `/tmp/oracle/dotnet`, construisez l'image Docker et lancez le container (et donc le script) : `clear ; docker build -t oracle-dotnet . && docker run --rm -it --network oracle oracle-dotnet`. Et voilà — le même résultat que celui affiché en haut de cet article, notre code .Net a bien accédé à la liste. ## Accéder à notre container Oracle DB avec NodeJS {#access-our-oracle-db-container-using-nodejs} Deuxième par ordre alphabétique, jouons avec un container NodeJS : ```bash $ mkdir -p /tmp/oracle/nodejs && cd $_ ``` Créons un Dockerfile : **Dockerfile** ```docker # cspell:ignore libaio FROM node:20-alpine WORKDIR /app COPY package.json . RUN npm install -g npm@latest RUN npm install RUN apk add --no-cache --update curl unzip libaio COPY main.js . CMD ["npm", "start"] ``` Nous avons aussi besoin de ces fichiers : **package.json** ```json { "name": "oracle-connector-nodejs", "version": "1.0.0", "description": "Node.js script to connect to Oracle", "main": "app.js", "scripts": { "start": "node main.js" }, "dependencies": { "oracledb": "^6.3.0" } } ``` **main.js** ```javascript // cspell:ignore orclpdb1, oracledb const oracledb = require("oracledb"); async function run() { let connection; try { const dbHost = process.env.ORACLE_HOST || "oracle-db"; const dbPort = process.env.ORACLE_PORT || "1521"; const dbService = process.env.ORACLE_SERVICE || "orclpdb1"; const dbUser = process.env.ORACLE_USER || "SYSTEM"; const dbPassword = process.env.ORACLE_PASSWORD || "admin"; const connectConfig = { user: dbUser, password: dbPassword, connectString: `${dbHost}:${dbPort}/${dbService}`, }; connection = await oracledb.getConnection(connectConfig); const result = await connection.execute( `SELECT employee_id, first_name, last_name, email FROM employees WHERE ROWNUM <= 25`, // Assuming your table is named 'EMPLOYEES' ); console.log( "\nEmployee ID | First Name | Last Name | Email", ); console.log( "------------|----------------------|-----------------------|-------------------------", ); for (const row of result.rows) { const employeeId = row[0]; const firstName = row[1]; const lastName = row[2]; const email = row[3]; console.log( `${employeeId.toString().padEnd(12)}| ${firstName.padEnd(21)}| ${lastName.padEnd(21)}| ${email}`, ); } } catch (err) { console.error("Error connecting to or querying Oracle:", err); } finally { if (connection) { try { await connection.close(); } catch (err) { console.error("Error closing connection:", err); } } } } run(); ``` Toujours dans la console, dans le dossier `/tmp/oracle/nodejs`, construisez l'image Docker et lancez le container (et donc le script) : `clear ; docker build -t oracle-nodejs . && docker run --rm -it --network oracle oracle-nodejs`. ![Utiliser NodeJS pour accéder à la liste des employés](./images/using_nodejs.webp) Sympa ! Comme vous pouvez le voir, on accède assez facilement à notre base Oracle avec NodeJS. ## Accéder à notre container Oracle DB avec PHP {#access-our-oracle-db-container-using-php} Et, dernier par ordre alphabétique ; terminons avec un container PHP : ```bash $ mkdir -p /tmp/oracle/php && cd $_ ``` Et là, créons un Dockerfile : **Dockerfile** ```docker # cspell:ignore instantclient,libaio1,libncurses5,libreadline8,ldconfig,pecl FROM php:fpm RUN apt-get update && apt-get install -y --no-install-recommends \ libaio1 \ libncurses5 \ libreadline8 \ locales \ unzip \ && rm -rf /var/lib/apt/lists/* RUN locale-gen en_US.UTF-8 ENV LANG=en_US.UTF-8 ENV LANGUAGE=en_US:en ENV LC_ALL=en_US.UTF-8 ARG ORACLE_INSTANT_CLIENT_VERSION=21.12 # Downloading Oracle Instant Client RUN curl -o /tmp/instantclient-basic.zip -L "https://download.oracle.com/otn_software/linux/instantclient/instantclient-basic-linuxx64.zip" \ && curl -o /tmp/instantclient-sdk.zip -L "https://download.oracle.com/otn_software/linux/instantclient/instantclient-sdk-linuxx64.zip" \ && mkdir -p /usr/lib/oracle \ && unzip -o /tmp/instantclient-basic.zip -d /usr/lib/oracle \ && unzip -o /tmp/instantclient-sdk.zip -d /usr/lib/oracle \ && rm /tmp/*.zip \ && ln -s /usr/lib/oracle/instantclient_* /usr/lib/oracle/instantclient \ && sh -c "echo /usr/lib/oracle/instantclient > /etc/ld.so.conf.d/oracle-instantclient.conf" \ && ldconfig # Install OCI8 Extension RUN echo 'instantclient,/usr/lib/oracle/instantclient' | pecl install oci8 \ && docker-php-ext-enable oci8 WORKDIR /app COPY main.php . CMD [ "php", "main.php" ] ``` Créons ensuite un script PHP (*généré avec l'IA*) **main.php** ```php getMessage() . "\n"; } finally { if (is_resource($statement)) { oci_free_statement($statement); } if (is_resource($connection)) { oci_close($connection); } } ?> ``` Toujours dans la console, dans le dossier `/tmp/oracle/php`, construisez l'image Docker et lancez le container (et donc le script) : `clear ; docker build -t oracle-php . && docker run --rm -it --network oracle oracle-php`. ![Utiliser PHP pour accéder à la liste des employés](./images/using_php.webp) ## Accéder à notre container Oracle DB avec Python {#access-our-oracle-db-container-using-python} Créons maintenant un script Python : ```bash $ mkdir -p /tmp/oracle/python && cd $_ ``` Et son Dockerfile ; la seule chose dont nous avons besoin (à part Python) est d'installer la dépendance `oracledb`. Plutôt simple. **Dockerfile** ```docker # cspell:ignore oracledb FROM python:slim WORKDIR /app RUN pip install --no-cache-dir oracledb COPY main.py main.py CMD ["python", "main.py"] ``` Créons ensuite un script Python (*généré avec l'IA*) **main.py** ```python from typing import Optional, Tuple, List import oracledb # cspell:ignore oracledb,orclpdb1,sysdba,rownum DB_USER: str = "SYS" DB_PASSWORD: str = "admin" DB_HOST: str = "oracle-db" DB_PORT: int = 1521 DB_SERVICE_NAME: str = "orclpdb1" connection: Optional[oracledb.Connection] = None cursor: Optional[oracledb.Cursor] = None try: dsn: str = f"{DB_HOST}:{DB_PORT}/{DB_SERVICE_NAME}" connection = oracledb.connect( user=DB_USER, password=DB_PASSWORD, dsn=dsn, mode=oracledb.AuthMode.SYSDBA, ) cursor = connection.cursor() sql: str = ( "SELECT employee_id, first_name, last_name, email " "FROM system.employees " "WHERE ROWNUM <= :max_rows" ) num_rows: int = 25 cursor.execute(sql, max_rows=num_rows) records: List[Tuple[int, str, str, str]] = cursor.fetchall() print("Employee ID | First Name | Last Name | Email") print("-" * 80) employee_id: int first_name: str last_name: str email: str for record in records: employee_id, first_name, last_name, email = record print(f"{employee_id:<12}| {first_name:<20}| {last_name:<20}| {email:<26}") except oracledb.Error as exception: print(f"Error connecting to Oracle: {exception}") finally: if cursor: try: cursor.close() except oracledb.Error as exception: print(f"Error closing cursor: {exception}") if connection: try: connection.close() except oracledb.Error as exception: print(f"Error closing connection: {exception}") ``` Toujours dans la console, dans le dossier `/tmp/oracle/python`, construisez l'image Docker et lancez le container (et donc le script) : `clear ; docker build -t oracle-python . && docker run --rm -it --network oracle oracle-python`. ![Utiliser Python pour accéder à la liste des employés](./images/using_python.webp) Sympa ! Comme vous pouvez le voir, on accède assez facilement à notre base Oracle avec Python. ## Conclusion {#conclusion} Vous savez quoi ? J'ai plus de 15 ans d'expérience avec PHP et seulement 6 mois avec Python et, si on regarde le Dockerfile des deux langages, aucun doute possible : celui de Python est tellement plus simple à lire et à configurer. J'ai eu beaucoup de mal à faire fonctionner celui de PHP, et aucune difficulté avec Python puisqu'il s'agit d'une seule dépendance à installer, et c'est tout. Et si on compare le code PHP et le code Python, ils sont assez similaires mais, je dois l'admettre, celui de Python a ma préférence : pas besoin de vérifier les erreurs à plusieurs endroits, on peut définir plus facilement les types de données des variables, l'instruction echo est plus lisible, ... ---