web-dev-qa-db-ja.com

Javaでデータベースに接続するための適切なクラスを作成する方法

現在、アプリケーションを作成しており、データベースに接続するためのクラスを正しく設計する方法の決定に苦労しています。私はこのようなものを思いつきました:

public class DatabaseConnector {
    private Connection databaseConnection = null;

    public DatabaseConnector(String url, String user, String password) {
        databaseConnection = DriverManager.getConnection(url, user, password);
    }

    public void close() throws SQLException {
        databaseConnection.close();
    }
}

さらに、このクラスには、データベースから何かをプルしたり挿入したりするメソッドがあり、メソッドごとにPrepareStatementResultSetおよびその他のオブジェクトを作成します。

私の質問は、このアプローチが正しいか、どういうわけか間違っているか、ひどく間違っているかです。良いコミュニケーションクラスを設計するためのあらゆるヒントと、データベースを正しく操作する方法を喜んで教えます。

MySQLデータベースとJDBCを通信に使用しています。

4
Piter _OS

データベースへの接続には2つのクラスの設計を好みます。このアプローチは、単一のアプリケーション内で複数のデータベースと通信する場合に特に効率的です。

  • 最初のクラス(CommDB)には、DBに接続してデータを操作するための汎用コードが含まれています。
  • 2番目のクラスは、DB固有のコードを運ぶDBプロキシ(MyDB)です。明らかに、複数のDBの場合、各DBは独自のプロキシクラスを持つ必要があります。

具体的には、たとえば、CommDBのselectメソッドは次のようになります。

public class CommDB
{/** This class contains a generic code for data manipulation */

    public TreeMap<String,HashMap<String,String>> 
    select(Connection conn, String selectQuery) 
    { /** This is a generic method for the select operation */

        TreeMap<String,HashMap<String,String>> selectResult = 
                                    new TreeMap<String,HashMap<String,String>>();
        String[] keys = selectQuery.replace(" ","").replace("SELECT", "").split("FROM")[0].split(",");

        try {
            PreparedStatement stmt = conn.prepareStatement(selectQuery);
            ResultSet rs = stmt.executeQuery();
            while (rs.next()) {
                HashMap<String,String> eachResult = new HashMap<String,String>();
                for (int i=1; i<keys.length; i++) { 
                    eachResult.put(keys[i],rs.getString(i+1));              
                } // for
                selectResult.put(rs.getString(1),eachResult);
            } // while

        } catch(SQLException sqlExc) {
            System.out.println(sqlExc.getMessage());
        } // try-catch

        return selectResult;
    } // select()

} // class CommDB

myDBからユーザー情報を取得するための特定のコードは次のようになります。

public class MyDB
{ /** This is MyDB Proxy Class */
    String myDbUrl = "jdbc:mysql://MyDB/mySchema";
    String myDbDriver = "com.mysql.jdbc.Driver";
    String myDbUser = "myName";
    String myDbPwd = "myPassword";
    CommDB db = new CommDB();

    public TreeMap<String,HashMap<String,String>> 
    getUsers(String namePattern) 
    { /** This method is specific for USERS data */     
        TreeMap<String,HashMap<String,String>> users = 
                new TreeMap<String,HashMap<String,String>>();           
        String selectUsers = 
         "SELECT userID, firstName, lastName, address, phone FROM USERS " + 
            "WHERE lastName like '%" + namePattern + "%'";

        Connection conn = null;
        try {           
            conn = db.connect(myDbUrl,myDbDriver,myDbUser,myDbPwd);
            users = db.select(conn, selectUsers);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            try {
                if (conn != null) { conn.close(); }
            } catch (SQLException sqlExc) {
                System.out.println(sqlExc.getMessage());
            } // try-catch
        } // try-catch-finally

        return users;
    } // getUsers()

} // class MyDB
2
Noviff

リレーショナルデータベースに接続して情報を保存および取得するには、さまざまな方法があります。ニーズに応じて、低レベルの実装またはより高いレベルの実装を使用できます。

JDBCを直接使用する を使用できます。特定のデータベースと通信する方法を知っている driver が必要です。接続を開き、SQLクエリを使用してステートメントを準備し、ステートメントに必要なパラメーターを設定し(ある場合)、ステートメントを実行します、結果セットを取得し、結果セットを反復して結果からオブジェクトを作成してから、使用したリソース(結果セット、ステートメント、接続)を閉じます。

それが最も低レベルの方法です。ただし、いくつかの欠点があります。

  • ボイラープレートコードがたくさんあります。接続の取得、ステートメントの作成、ステートメントの実行、結果のループ、オブジェクトのビルド、使用済みリソースの解放です。 SQLを実行するたびに、これらを実行する必要があります。 SQLだけが毎回異なります。残りは何度も何度も繰り返す必要があります。
  • クエリを実行するたびにデータベース接続を開きます。これにはいくつかのオーバーヘッドがあり、一度に実行するクエリの数によっては、結局、開いている接続が多すぎる場合があります。一部のデータベースにはクライアントごとに制限があるため、高すぎることはできません。

開く接続を制限するには、次のようにします 接続プールを使用 Apache DBCP、C3P0、HikariCPなど。同じボイラープレートコードが残っていますが、接続を作成して閉じるのではなく、借ります1つをプールに戻します。もっと効率的。

さて、ボイラープレートは毎回同じなので、SQLの作成に専念できるフレームワークやライブラリーに移動してください。これは、たとえば MyBatis のようなデータマッパーが行うことです。一度設定して、必要なSQLを記述してメソッドにマッピングし、MyBatisに行の結果をオブジェクトにマッピングする方法を伝えます。すべてのボイラープレートはMyBatisによって処理されます。メソッドを実行して、必要なオブジェクトを取得します。

MyBatisを使用すると、SQLを作成するだけで済みます。しかし、それを気にしたくない人もいます。ボイラープレートコードと、いくつかのライブラリ/フレームワークによって処理される接続がありますが、SQLも削除してみませんか?

これが、Hibernateのように ORMs が行うことです。クラスをテーブルにマップすると、Hibernateがすべてを処理します。データベースからデータを保存または取得するときに必要なSQLを生成します。 Hibernateを構成したら、データベースが存在しない(少なくともしばらくの間)ふりをすることができます。

これらの方法にはそれぞれ長所と短所があります。

  • jDBCを使用すると、大量のボイラープレートコードを記述し、トランザクションを自分で管理し、リソースをリークしないようにする必要があります。これは低レベルです。
  • データマッパーを使用して、SQLを作成する必要があります。データマッパーは、データベースがないことを装うわけではないので、対処する必要があります。
  • oRMを使用すると、関係するデータベースがないように見せかけることができます。オブジェクトのみを扱います。 ORMは [〜#〜] crud [〜#〜] アプリケーションには最適ですが、他のアプリケーションでは、ORMが問題を引き起こす可能性があります。 オブジェクトリレーショナルインピーダンスミスマッチ と呼ばれるこの問題は、醜い頭であることを示しています。そして、そうなると、通常、パフォーマンスが最初に低下します。

これらはオプションです。アプリケーションを見て、どのソリューションがより適切であるかを確認します。あなたの質問を考えると、軽量のものを使用したいかもしれません(直接JDBCほど低くなく、Hibernateほど高くもありません)。

しかし、車輪を再発明しないでください。たとえば Commons DbUtils のようなものが、出発点として適しています。データベースとのやり取りを大幅に変更/追加することなく、JDBCボイラープレートコードを取り除きます。

2
Bogdan

したがって、クエリを呼び出すには、次のようにします。

    String Querry = "SELECT seatID,movieID,Tanda,seatNum,isBooked,BookedBy FROM ROOT.SEATS";
    Map<String, List< Object>> map = getListQuerry(Querry);
    map.forEach((key, value) -> System.out.println(key + ":" + value));

メソッドは次のようになります。

Map<String, List< Object>> getListQuerry(String query) {
    System.out.println("getListQuerry will run SQL Querry:\n\t\t" + query);

    Map<String, List< Object>> result = new HashMap<>();
    String[] keys = query.replace(" ", "").replace("SELECT", "").split("FROM")[0].split(",");
    for (String Key : keys) {
        result.put(Key, new ArrayList<>());
    }

    try (Connection Connection = DriverManager.getConnection(Host, DBUsername, DBpassword)) {
        PreparedStatement stmt = Connection.prepareStatement(query, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
        ResultSet resultSet = stmt.executeQuery();

        while (resultSet.next()) {
            for (int i = 1; i < keys.length; i++) {
                result.get(keys[i]).add(resultSet.getInt(keys[i]));
            }
        }
    } catch (SQLException ex) {
        Logger.getLogger(Stage1Bootup.class.getName()).log(Level.SEVERE, null, ex);
        System.out.println("Query failed to run");
    }
    return result;
}
0