﻿# 如何快速建立 Oracle Database Server 12c R2 資料庫容器並建立使用者

最近越來越多 ASP.NET Core 企業內訓需求，有客戶提到希望能在上課的時候展示 Entity Framework Core 與 Oracle 資料庫的整合能力，我第一時間就想到要在本機安裝 Oracle 資料庫容器起來，快速建立可開發測試的環境。不過，雖然建立 Oracle 資料庫容器相當簡單方便，但不太熟悉 Oracle 資料庫特性的我，卻連第一步「建立使用者」都沒辦法成功，錯誤訊息也相當難以理解，花了一些時間才找到解決之道。

* * *

> 這篇文章已經過時，請移駕 [如何在 Windows 快速啟動一個 Oracle Database 23c Free 資料庫容器](/post/2024/02/29/Oracle-Database-23c-Free-for-Docker-Desktop-on-Windows) 觀看最新版文章。

* * *

### 執行 Oracle Database Server 12c R2 容器

> 要在本機執行 Docker 容器之前，請先參考 [Install Docker Desktop on Windows](https://docs.docker.com/docker-for-windows/install/) 文件進行 Docker 安裝。

由於 Oracle 官方提供的 [Oracle Database Server 12c R2](https://github.com/oracle/docker-images/blob/main/OracleDatabase/SingleInstance/README.md) image 放在 Docker Hub 上面，而且必須先登入 Docker Store 才能下載，所以在執行容器之前，必須先用 docker 命令進行登入：

```bash
docker login
```

只要登入完成，就可以進行 `docker pull` 下載 Oracle 授權的容器映像 (Container images)，目前官方提供兩個版本：

1.  Oracle Database Server 12.2.0.1 Enterprise Edition (完整版)
2.  Oracle Database Server 12.2.0.1 Enterprise Edition Slim Variant (精簡版)

```bash
docker pull store/oracle/database-enterprise:12.2.0.1
docker pull store/oracle/database-enterprise:12.2.0.1-slim
```

> 如果你只想測試簡單的資料庫功能，建議安裝 **Slim Variant** 版本即可，不但 image 體積小，啟動速度也較快。

接著就直接執行容器，大約 1 ~ 2 分鐘才會啟動完畢：

```bash
docker run -d -it --name oracle -p 1521:1521 -p 5500:5500 store/oracle/database-enterprise:12.2.0.1-slim
```

如果你希望保留本次容器執行所建立的資料庫，可以透過 docker volume mapping 做到：

```bash
docker volume create OracleDBData

docker run -d -it --name oracle -p 1521:1521 -p 5500:5500 -v OracleDBData:/ORCL store/oracle/database-enterprise:12.2.0.1-slim
```

官方容器的預設帳號密碼如下：

-   SID：`ORCLCDB`
-   帳號：`SYS`
-   密碼：`Oradoc_db1`

如果不從外部連線的話，可以直接透過 `docker exec` 命令從 Oracle 容器中執行 `sqlplus` 工具：

```bash
docker exec -it oracle bash -c "source /home/oracle/.bashrc; sqlplus /nolog"
```

接著可以透過以下 SQL 命令登入 `SYS` 為 `SYSDBA` 角色：

```sql
CONNECT SYS/Oradoc_db1 AS SYSDBA;
```

或是直接在執行 `sqlplus` 時就直接登入：

```bash
docker exec -it oracle bash -c "source /home/oracle/.bashrc; sqlplus sys/Oradoc_db1@ORCLCDB as sysdba"
```

> 建議第一次登入就先修改 `SYS` 預設密碼，請參考以下命令進行修改：`ALTER USER SYS IDENTIFIED BY <new-password>;`

登入後，可以透過以下 SQL 命令，查詢目前資料庫的**服務名稱**(`ORCLCDB.localdomain`)：

```sql
SELECT VALUE FROM V$PARAMETER WHERE NAME='service_names';
```

如果要透過 [Oracle SQL Developer](https://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/index.html) 登入，可以參考以下截圖進行設定：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/3d4d6b56e958b8fd7c6a-image_3198.png)

### 建立使用者

當 Oracle 容器完全啟動之後，悲劇也就開始發生了，因為我連一個使用者都無法建立！

因為 Oracle 資料庫跑在容器中，有著不同的安全性架構，從 Oracle 12cR1 開始，因為新增了多租戶架構(multitenant architecture)，可以讓你在一個**資料庫容器**中執行多個**租戶資料庫**(tenant databases)，所以建立使用者的 SQL 命令稍微有點不一樣，而這個問題對一個不常使用 Oracle 的開發者來說也太困難了。

如果你用傳統的 SQL 命令建立一個新用戶，就會得到這個錯誤：`ORA-65096: invalid common user or role name`

```sql
SQL> CREATE USER WILL IDENTIFIED BY yourpassword;
CREATE USER WILL IDENTIFIED BY yourpassword
            *
ERROR at line 1:
ORA-65096: invalid common user or role name
```

而正確建立使用者的方法，應該用以下 SQL 命令：

```sql
ALTER SESSION SET "_ORACLE_SCRIPT"=TRUE;
CREATE USER will IDENTIFIED BY yourpassword;
GRANT CONNECT, RESOURCE, DBA TO WILL;
ALTER SESSION SET "_ORACLE_SCRIPT"=FALSE;
```

接著就可以用新帳號登入連線：

```sql
CONNECT WILL/yourpassword AS SYSDBA;
```

### 刪除使用者

基本上，只要跟「使用者」相關的操作，在 Oracle 容器中都會遇到問題，所以若要刪除上一節所建立的 `WILL` 帳號的話，必須使用以下 SQL 命令：

```sql
ALTER SESSION SET "_ORACLE_SCRIPT"=TRUE;
DROP USER WILL CASCADE;
ALTER SESSION SET "_ORACLE_SCRIPT"=FALSE;
```

### 建立範例資料庫

我從 [Getting Started with Oracle Database](https://www.oracletutorial.com/getting-started/) 找到一個 [Oracle Sample Database](https://www.oracletutorial.com/getting-started/oracle-sample-database/) 非常適合用來學習與測試 Oracle 資料庫連線，以下我將用這個範例進行解說。

下載範例資料庫之後，你將會看到四個檔案：

1.  `ot_create_user.sql`
    
    你首先要解決的困難就是將 `ot_create_user.sql` 稍微修改一下，前後都加上 `ALTER SESSION` 命令：
    
    ```sql
    ALTER SESSION SET "_ORACLE_SCRIPT"=TRUE;
    
    -- create new user
    CREATE USER OT IDENTIFIED BY yourpassword;
    -- grant priviledges
    GRANT CONNECT, RESOURCE, DBA TO OT;
    
    ALTER SESSION SET "_ORACLE_SCRIPT"=FALSE;
    ```
    
2.  `ot_schema.sql`
    
    建立 Schema 時並不會發生問題。
    
    建立好之後，可以透過 `SELECT table_name FROM user_tables;` 密令列出所有建立的表格。([參考](https://stackoverflow.com/questions/205736/get-list-of-all-tables-in-oracle))
    
3.  `ot_data.sql`
    
    匯入資料時，你會遇到 `ORA-01843: 不是有效的月份` 錯誤，那是因為這個範例資料的 SQL 語法，使用的是美國的日期表示法，你必須在匯入資料前，先執行以下 SQL 才可以成功匯入所有資料：
    
    ```sql
    ALTER SESSION SET NLS_DATE_LANGUAGE='american';
    ```
    
4.  `ot_drop.sql`
    
    等測試完成後，就可以執行這個 SQL 檔案，刪除所有表格。
    
    > 如果想刪除 `OT` 使用者，請記得手動刪除。
    

### 同場加映：透過 [Oracle.EntityFrameworkCore](https://www.nuget.org/packages/Oracle.EntityFrameworkCore/) 產生 EFCore 實體模型

```bash
dotnet add package Oracle.EntityFrameworkCore

dotnet ef dbcontext scaffold "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ORCLCDB.localdomain)));User Id=OT;Password=yourpassword;" Oracle.EntityFrameworkCore -o Models
```

> 上述 Oracle 連接字串的 **Data Source** 部分，可以透過 `docker logs oracle` 查詢相對應的提示訊息
> 
> ![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/3a42a23ace25d50aa621-image_3199.png)

### 相關連結

-   [Docker Images from Oracle](https://github.com/oracle/docker-images)
    -   [Oracle Database Enterprise Edition - Docker Hub](https://github.com/oracle/docker-images/blob/main/OracleDatabase/SingleInstance/README.md)
    -   [Oracle Database on Docker](https://github.com/oracle/docker-images/tree/master/OracleDatabase)
    -   [Oracle Database Documentation](https://docs.oracle.com/en/database/oracle/oracle-database/index.html)
-   [Getting Started with Oracle Database](https://www.oracletutorial.com/getting-started/)
    -   [Oracle Sample Database](https://www.oracletutorial.com/getting-started/oracle-sample-database/)
-   [Hacking Oracle 12c COMMON Users](http://www.petefinnigan.com/weblog/archives/00001370.htm)
-   [Create Oracle database docker image. - DZone Database](https://dzone.com/articles/oracle-12c-image-installation-in-docker)
-   [Oracle 12c: Creating users without container database](https://jaykhimani.blogspot.com/2015/12/oracle-12c-creating-users-without.html)
-   [David's Logger: TO\_DATE ORA-01843:not a valid month 錯誤處理](http://dljeng.blogspot.com/2017/06/todate-ora-01843not-valid-month.html)
-   [Database Providers - EF Core | Microsoft Docs](https://learn.microsoft.com/en-us/ef/core/providers/?tabs=dotnet-core-cli)
    -   [NuGet Gallery | Oracle.EntityFrameworkCore](https://www.nuget.org/packages/Oracle.EntityFrameworkCore/)
    -   [Oracle connection strings - ConnectionStrings.com](https://www.connectionstrings.com/oracle/)
-   [Persist and retrieve relational data with Entity Framework Core - Learn | Microsoft Docs](https://learn.microsoft.com/en-us/learn/modules/persist-data-ef-core/)
