質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.19%

JavaプログラムからMySQLのJDBCドライバを読み込みたい。

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 7,238

na_ka

score 4

前提・実現したいこと

JavaプログラムからMysqlにJDBCインターフェースで接続すること。

発生している問題・エラーメッセージ

コンパイルは正しく終了したが、実行時に「ドライバを読み込めませんでした java.lang.ClassNotFoundException: com.mysql.jdbc.Driver」になる。

該当のソースコード

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
* localhost上のデータベースと接続し、取得したデータをコンソール出力する。
*/
public class UseJdbc {

    public static void main( String args[] ) throws Exception {

        /*接続先サーバー名を"localhost"で与えることを示している*/
        String servername     = "localhost";

        /*接続するデータベース名をsenngokuとしている*/
        String databasename   = "shop";

        /*データベースの接続に用いるユーザ名をrootユーザとしている*/
        String user = "root";

        /*データベースの接続に用いるユーザのパスワードを指定している*/
        String password = "xxxxxx";

        /*取り扱う文字コードをUTF-8文字としている*/
        String serverencoding = "UTf8mb4";

        /*データベースをあらわすURLを設定している*/
        String url =  "jdbc:mysql://localhost/" + databasename;

        /*MySQLの場合、URLの形式は次のようになります。
          jdbc:mysql://(サーバ名)/(データベース名)*/

        /*↑データベースをあらわすURL(データベースURL)は、データベースに接続する場合に
        必要となる情報をセットした文字列である。
        この文字列の構造は、"jdbc"、サブプロトコル、サブネームの3つの部分から構成される。*/

        /*接続を表すConnectionオブジェクトを初期化*/
        Connection con = null;

        try{

            /*クラスローダによりJDBCドライバを読み込んでいることを示している。
            引数は、データベースにアクセスするためのJDBCドライバのクラス名である。*/
            Class.forName( "com.mysql.jdbc.Driver" ).newInstance();

            /*DriverManagerクラスのgetConnectionメソッドを使ってデータベースに接続する。*/
            con = DriverManager.getConnection( url, user, password );

            System.out.println( "Connected...." );

            /*データベースの接続後に、sql文をデータベースに直接渡すのではなく、
            sqlコンテナの役割を果たすオブジェクトに渡すためのStatementオブジェクトを作成する。*/
            Statement st = con.createStatement();

            /*SQL文を作成する*/
            String sqlStr = "SELECT * FROM busyou";

            /*SQL文を実行した結果セットをResultSetオブジェクトに格納している*/
            ResultSet result = st.executeQuery( sqlStr );

            /*クエリ結果を1レコードずつ出力していく*/
            while( result.next() )
            {
                /*getString()メソッドは、引数に指定されたフィールド名(列)の値をStringとして取得する*/
                String str1 = result.getString( "shohin_id" );
                String str2 = result.getString( "shohin_mei" );
                String str3 = result.getString( "shohin_bunrui" );
                String str4 = result.getString( "hanbai_tanka" );
                String str5 = result.getString( "shiire_tanka" );
                String str6 = result.getString( "torokubi_tanka" );
                System.out.println( str1 + ", " + str2 + ", " + str3 + "," + str4+ "," +str5+ "," +str6);
            }

            /*ResultSetオブジェクトを閉じる*/
            result.close();

            /*Statementオブジェクトを閉じる*/
            st.close();

            /*Connectionオブジェクトを閉じる*/
            con.close();
        }
        catch( SQLException e ){

            /*エラーメッセージ出力*/
            System.out.println( "Connection Failed. : " + e.toString() );

            /*例外を投げちゃうぞ*/
            throw new Exception();

        }catch (ClassNotFoundException e){

            /*エラーメッセージ出力*/
            System.out.println("ドライバを読み込めませんでした " + e);
        }
        finally{
            try{
                if( con != null ){ 
                    con.close();
                }
            }
            catch(Exception e){

                /*エラーメッセージ出力*/
                System.out.println( "Exception2! :" + e.toString() );

                /*例外を投げちゃうぞ*/
                throw new Exception();
            }
        }
    }
}

試したこと

mysql-connector-java-8.0.17.jarをダウンロードし、Usejdbcクラスと同じディレクトリに格納したが、結果は同じでした。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • xebme

    2019/09/07 13:52

    クラスパスにカレントディレクトリを含めてください。クラス名が間違っています。
    java -classpath .;mysql-connector-java-8.0.17.jar UseJdbc
    このプログラムはレガシーコードです。もはや使われていません。

    キャンセル

  • na_ka

    2019/09/07 14:58

    正しく実行しましたが、以下のエラーになりました。
    Error: A JNI error has occurred, please check your installation and try again
    Exception in thread "main" java.lang.UnsupportedClassVersionError: UseJdbc has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at java.security.SecureClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.access$100(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)

    -------------------------------------------------------------------------
    >java -version
    java version "1.8.0_202"
    Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
    Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)
    -------------------------------------------------------------------------
    java runtimeが古いのでしょうか?

    キャンセル

  • xebme

    2019/09/07 15:21 編集

    echo %PATH%
    パスの先頭にJRE 8があり、パスの後にJDK 11があると思われます。Windowsのシステム環境設定で、JREのパスを削除するか、とりあえず、PATH=<jdk11のパス>;%PATH%として実行してください。JDK 8 のインストーラが、先頭にJREのパスを追加してしまうのでこうなります。

    キャンセル

回答 1

+2

レガシーコード
参考にしているソースはレガシーコードです。このソースを参考にしてはいけません。このソースを、JDK 11 を使ってコンパイルします。私の環境はLinux、ドライバーの場所は /usr/share/java/mysql-connector-java.jar

$ javac -cp .:/usr/share/java/mysql-connector-java.jar UseJdbc.java
注意:UseJdbc.javaは推奨されないAPIを使用またはオーバーライドしています。
注意:詳細は、-Xlint:deprecationオプションを指定して再コンパイルしてください。


警告(Java 9より表示)が出てもここでは重要ではない。クラスファイルができているので実行します。

$ java -cp .:/usr/share/java/mysql-connector-java.jar UseJdbc
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
Connection Failed. : com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
Exception in thread "main" java.lang.Exception
    at UseJdbc.main(UseJdbc.java:91)


実行すると警告がでます。警告文の意味です。

  • com.mysql.jdbc.Driverは廃止される予定。新しいドライバーはcom.mysql.cj.jdbc.Driver。
  • ドライバーはSPIを介して自動的に登録されるので、ドラーバーを手動でロードする必要はない。
    com.mysql.jdbc.Driverは、staticイニシャライザーがこの警告文を出すためだけに存在します。それ以外何もしません。com.mysql.cj.jdbc.Driver が自動ロードされています。

その次のエラーは、mariadbサーバーが起動していないのでネットワークエラー。データベースが正常に動作すれば結果が出力されます。

[学習してほしいこと]

  • Java 6 以降では、JDBC 4がサポートされ、CLass.forName()は不要になった。(後方互換のために残される)
  • Java 7 より、AutoCloseableインターフェイスがサポートされ、リソースの明示的な閉じは不要になった。
  • Java 9 より、try-with-resources 構文が拡張された。

これを踏まえたソースを示します。

public static void main(String args[]) {

  String servername = "localhost";
  String databasename = "shop";
  String user = "root";
  String password = "nakaha11";

  String url = "jdbc:mysql://" + servername + "/" + databasename;

  try (Connection con = DriverManager.getConnection(url, user, password)) {
      System.out.println("Connected....");
      try (Statement st = con.createStatement()) {
          String sqlStr = "SELECT * FROM busyou";
          try (ResultSet result = st.executeQuery(sqlStr)) {
              while (result.next()) {
                  String str1 = result.getString("shohin_id");
                  String str2 = result.getString("shohin_mei");
                  String str3 = result.getString("shohin_bunrui");
                  String str4 = result.getString("hanbai_tanka");
                  String str5 = result.getString("shiire_tanka");
                  String str6 = result.getString("torokubi_tanka");
                  System.out.println(str1 + ", " + str2 + ", " + str3 + "," + str4 + "," + str5 + "," + str6);
              }
          }
      }
  } catch (SQLException e) {
      System.out.println("Connection Failed. : " + e.toString());
      throw new RuntimeException(e);
  }
}

注意事項

  • Webアプリケーションでは、WEB-INF/lib配下にドライバーを配置すると、SPI機能が使えずドライバーがロードされません。仕方がないので、Class.forName()するのがあたりまえだと思われていますが、問題が起きる可能性があるので避けるべきです。
  • 質問のJavaアプリケーションでは、SPI機能が働くので、Class.forName()は書いてはいけません。

(タイムゾーンエラー)2019-09-15

私のLinux環境では発生しないので、Windowsにサーバーをインストールました。JDBCのエラーメッセージは、サーバーのタイムゾーンの名前が不明または複数存在する、です。解決方法も書かれています(以下のどちらか)。

  • サーバーのタイムゾーンを設定する
  • JDBCドライバーのserverTimezoneプロパティを設定する

JavaプログラムでserverTimezoneを設定する解決法

コメントしたとおり、urlの末尾に"?serverTimezone=JST"を追加してください。

String url = "jdbc:mysql://" + servername + "/" + databasename + "?serverTimezone=JST";

サーバーのタイムゾーンを設定する解決法

管理者権限でmy.iniを変更。間違えるとサーバーが起動しなくなります。参考: MariaDB (MySQL) のタイムゾーン、タイムゾーンテーブルを設定する

A. WindowsのMySQLサーバー用パッケージ: MySQL Community Downloads
バージョン5.7+ のパッケージ(timezone_2019c_posix_sql.zip)をダウンロード。圧縮解除する。以下のコマンドでタイムゾーン情報を取り込む。

> mysql -u root -p mysql < timezone_posix.sql

B. 管理者権限でmy.iniを変更
1) MySQLサービスを停止
2) ProgramData\MySQL\MySQL Server 5.X\my.iniを[管理者権限で編集]バックアップ必須。[mysqld]グループの末尾に1行追加。

default-time-zone='Asia/Tokyo'


3) MySQLサービスを起動

C. MySQLクライアントを起動、設定を確認。タイムゾーンが設定した名前になっていること。

> mysql -u root -p
mysql> show variables like '%time_zone%';
+------------------+------------+
| Variable_name    | Value      |
+------------------+------------+
| system_time_zone |            |
| time_zone        | Asia/Tokyo |
+------------------+------------+


サーバーで設定すれば、JavaプログラムのJDBC接続プロパティは不要です。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/09/13 08:43

    おっしゃっているのは、META-INF/service/... のことだと思います。ServiceLoaderの機能ですね。「後方互換」という言葉を間違えたことに気づきました。「下位互換」とすべきでした。ありがとうございます。

    キャンセル

  • 2019/09/13 13:48

    補足します。『timezoneのエラーになりました』はドライバーがロードされた後に、サーバー接続で起きる問題。接続URLにパラメータを追加すれば解決できます。回答を追記するのは調べてから。早くて日曜日の夕方以降。

    キャンセル

  • 2019/09/15 23:20

    ご丁寧な回答ありがとうございました。おかげさまで無事実行できました。感謝申し上げます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.19%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る