oracledb

【Python】oracledbでOracleに接続して操作する方法(サンプルコード付き)

【Python】oracledbでOracleに接続して操作する方法(サンプルコード解説付き)

python-oracledbを用いてOracleデータベースへアクセスする方法について解説します。

PythonでOracleデータベースへ接続する

python-oracledbとは?

python-oracledbは、以前広く使われていて cx_Oracle の後継として Oracle 社が提供する公式の Python ライブラリです。ライブラリ名はシンプルに「oracledb」です。これにより、Oracleデータベースに対して Python から簡単に接続・操作することができます。

大きな特徴は、Oracle Client が不要なThinモードに対応しており、手軽に導入ができる点です。一方で、従来の cx_Oracle 同様の機能を活用したい場合は、Thick モードに切り替えての使用も可能です。

【注意】
かつては、PythonでOracle接続と言えばcx_Oracleでしたが、現在では非推奨となっているため、新規開発には使用しないようにしましょう。

oracledbのインストール方法

oracledb ライブラリは、PyPIで公開されており、以下のように pip install コマンドで簡単にインストールできます。

pip install oracledb

ThinモードとThickモードの違いと選び方

oracledbは、Thin モードと Thick モードの2つの動作モードを提供しています。

モード特徴Oracle Client
Thin軽量・インストール簡単で基本機能をカバー不要
Thick高機能であり、従来のcx_Oracle互換必要

Thin モードは Oracle Client も不要でとても便利です。Thin モードで問題ない場合は、そのままで十分ですが Advanced Queuing などの Oracle 特有機能を使いたい場合には、Thick モードの使用を検討する必要があります。

oracledbを用いたサンプルプログラム

以降では、oracledb を用いて Oracle データベースへアクセスするためのサンプルプログラムを紹介します。

前提条件

  • Python 3.7 以降、無償版の OracleXE がローカルPCにインストールされているものとします。(製品版の Oracle でも問題ありません。)
  • サービスは、Oracle XE デフォルトとして作成される XEPDB1 を使用しています。お使いの環境のサービスにあわせて変更してください。
  • ユーザーとして TEST が作成されておりパスワードは PAssw0rd としています。お使いの環境にあわせて変更してください。
  • 使用するユーザーには、データベースへのアクセス、テーブル作成、データ登録、検索といった権限が付与されているものとします。
  • DB接続クラスは db_connect_oracle.py に、設定情報は dbconfig.ini として、dbconfig.ini は、Pythonプログラムと同フォルダに配置とします。

サンプルコード

設定ファイルおよびDBアクセスクラス定義

oracledb を使用して Oracle データベースへアクセスするための設定ファイルとDBアクセス用クラスの例は以下のようになります。設定ファイルの内容はお使いの環境にあわせて修正してください。

dbconfig.ini】DBアクセス設定ファイル
[ORACLE_DB_SERVER]
host = localhost
port = 1521
service = XEPDB1
user = TEST
password = PAssw0rd
db_connect_oracle.py】DBアクセスクラス
import configparser

import oracledb


class DbConnectOracle:
    """Oracle DBアクセスクラス"""

    def __init__(self, mode: str = "thin", client_lib_dir: str = None) -> None:
        """コンストラクタ

        Args:
            mode: "thin" または "thick"
            client_lib_dir: Thick モードの場合は、Oracle Client のライブラリパス
        """
        # モード判定
        if mode == "thick":
            if client_lib_dir is None:
                raise ValueError(
                    "Thick モードでは client_lib_dir の指定が必要です。"
                )
            oracledb.init_oracle_client(client_lib_dir)
        elif mode != "thin":
            raise ValueError(
                "mode は、'thin' または 'Thick' を指定してください。"
            )

        # iniファイルの読み込み
        config_db = configparser.ConfigParser()
        config_db.read("dbconfig.ini")

        # 設定の読み込み
        host = config_db["ORACLE_DB_SERVER"]["host"]
        port = int(config_db["ORACLE_DB_SERVER"]["port"])
        service_name = config_db["ORACLE_DB_SERVER"]["service"]
        user = config_db["ORACLE_DB_SERVER"]["user"]
        password = config_db["ORACLE_DB_SERVER"]["password"]

        # DSN の作成
        self.dsn = oracledb.makedsn(host, port, service_name=service_name)

        # 接続の確立
        self.con = oracledb.connect(
            user=user,
            password=password,
            dsn=self.dsn,
        )
        self.cursor = self.con.cursor()

    def execute_non_query(self, sql: str, bind_var: dict = None) -> None:
        """SQL 実行メソッド
        (CREATE / INSERT / UPDATE / DELETE 用)

        Args:
            sql: 実行SQL
            bind_var: バインド変数

        Returns: None

        """
        # SQLの実行
        if bind_var is None:
            self.cursor.execute(sql)
        else:
            # バインド変数がある場合は指定して実行
            self.cursor.execute(sql, bind_var)

    def execute_query(
        self, sql: str, bind_var: dict = None, count: int = 0
    ) -> list:
        """SQL 実行メソッド
        (SELECT 用)

        Args:
            sql: 実行SQL
            bind_var: バインド変数
            count: データ取得件数

        Returns: 取得結果リスト

        """
        # SQLの実行
        if bind_var is None:
            self.cursor.execute(sql)
        else:
            # バインド変数がある場合は指定して実行
            self.cursor.execute(sql, bind_var)

        columns = [col[0] for col in self.cursor.description]
        self.cursor.rowfactory = lambda *args: dict(zip(columns, args))

        # 取得結果を返却 (件数指定がある場合は、指定された件数分返却)
        return (
            self.cursor.fetchall()
            if count == 0
            else self.cursor.fetchmany(count)
        )

    def commit(self) -> None:
        """コミット

        Returns: None

        """
        self.con.commit()

    def rollback(self) -> None:
        """ロールバック

        Returns: None

        """
        self.con.rollback()

    def __del__(self) -> None:
        """デストラクタ

        Returns: None

        """
        self.cursor.close()
        self.con.close()


if __name__ == "__main__":
    pass

細かな例外処理はしていませんのでご注意ください。プログラムを参考にして使用いただいても構いませんが、ご自身の責任でご利用ください。

使用例(Thin モード)

oracledb の Thin モードを使用して上記で作成したクラスを用いてOracleへの接続をするには以下のように使用します。

from db_connect_oracle import DbConnectOracle


def main():
    # Thin モードでアクセスする場合
    db_oracle = DbConnectOracle()

    # ===== テーブルを作成する
    create_sql = (
        "CREATE TABLE SAMPLE_TABLE"
        "("
        "ID NUMBER GENERATED ALWAYS AS IDENTITY,"
        "STR1 VARCHAR2(50), "
        "VALUE1 NUMBER, "
        "LAST_UPDATE_DATETIME TIMESTAMP, "
        "PRIMARY KEY (ID)"
        ")"
    )
    db_oracle.execute_non_query(create_sql)
    # コミット
    db_oracle.commit()

    # ===== データをINSERTする
    insert_sql = (
        "INSERT INTO SAMPLE_TABLE "
        "(STR1, VALUE1, LAST_UPDATE_DATETIME) "
        "VALUES(:str1, :val1, CURRENT_TIMESTAMP)"
    )
    for i in range(5):
        input_str = f"test_str{i}"
        input_val = 10 * i
        bind = {"str1": input_str, "val1": input_val}
        db_oracle.execute_non_query(insert_sql, bind)
    # コミット
    db_oracle.commit()

    # ===== データを検索する (全て検索)
    print("===== 全件検索")
    select_sql = "SELECT * FROM SAMPLE_TABLE"
    select_result = db_oracle.execute_query(select_sql)
    print(select_result)

    # ===== 件数を指定して検索する (以下の例は3件)
    print("===== 取得する件数の指定")
    select_sql = "SELECT * FROM SAMPLE_TABLE"
    select_result = db_oracle.execute_query(select_sql, count=3)
    print(select_result)

    # ===== 条件を指定して検索する
    print("===== 条件を指定して実行する")
    select_sql = "SELECT * FROM SAMPLE_TABLE WHERE VALUE1 > :val1"
    print("条件1")
    select_result = db_oracle.execute_query(select_sql, {"val1": 10})
    print(select_result)
    print("条件2")
    select_result = db_oracle.execute_query(select_sql, {"val1": 30})
    print(select_result)


if __name__ == "__main__":
    main()

使用例(Thick モード)

oracledb の Thick モードを使用する場合には、クラスをインスタンス化する際に以下の引数を設定してください。

  • mode"thick"
  • client_lib_dir:Oracleクライアントライブラリが格納されているパス

以下の例では、Oracle の Instant Client (軽量版クライアント) を使用した場合の例です。フルのOracle Clientの場合は、環境にあわせて r"C:\oracle\product\19.0.0\client_1\bin" のような指定をしていただければいいかと思います。

from db_connect_oracle import DbConnectOracle


def main():
    mode = "thick"
    client_lib_dir = r"C:\oracle\instantclient_21_18"

    # Thick モードでアクセスする場合
    db_oracle = DbConnectOracle(
        mode=mode,
        client_lib_dir=client_lib_dir,
    )

    # 以降は、Thin モードと同じ
    # ・・・


if __name__ == "__main__":
    main()

サンプルコードのポイント解説

DBアクセス用クラス DBConnectOracle

DBアクセス用のクラスとしてDbConnectOracleというクラスを定義しています。メソッドとして定義しているものは以下になります。

メソッド名概要
__init__コンストラクタでデフォルトは Thin モードでDBのコネクション確立とカーソル作成を行います。Thick モードで使う場合は、mode の指定と Oracle Client のライブラリパスの指定が必要です。
execute_non_queryCREATE / INSERT / UPDATE / DELETEの処理用のSQL実行メソッドです。SQL文と必要に応じてバインド変数を渡して処理を実行します。
execute_querySELECT用のSQL実行メソッドです。返却値は辞書 (dict) のリストになります。SQL文と必要に応じてバインド変数、データ取得件数を渡して処理を実行します。
commitコミットして変更内容を確定します。
rollbackロールバックして変更内容を破棄します。
__del__デストラクタです。接続終了時にデータベースのカーソルとコネクションをクローズします。

まとめ

python-oracledbを用いてOracleデータベースへアクセスする方法について解説しました。

このライブラリは、Oracle公式であり、Oracleアクセス手段として非常に有力です。これまで使われていた cx_Oracle の後継として使用できます。

Thin モードと Thick モードが用意されており、Thin モードではOracle Client不要で手軽に使用することができます。また、高度な機能が必要であれば Thick モードの使用も検討できます。

この記事では、汎用的に使えるDBアクセスクラスを作成してOracleDBへのアクセスをできるようにしてみました。ぜひ、参考にしていただいてOracle接続に活用していただければと思います。