4

データベース レコードを操作するための単純な GUI アプリケーションを実装しようとしています。レコードのビューとそれらを編集する可能性が含まれます。私のコードは次のようになります。

class MainPanel extends JPanel {

    private CachedRowSet crs;
    private List<JTextField> fields = new LinkedList<>();

    MainPanel() {
        try {
            //Fill result set with data
            crs = new CachedRowSetImpl();
            crs.setUrl("jdbc:postgresql:testdb");
            crs.setUsername("username");
            crs.setPassword("password");
            crs.setCommand("SELECT * FROM products");
            crs.execute();

            //Create text fields with labels
            ResultSetMetaData meta = crs.getMetaData();
            for (int a = 1; a <= meta.getColumnCount(); a++) {
                JTextField field = new JTextField(10);
                add(new JLabel(meta.getColumnLabel(a)));
                fields.add(field);
                add(field);
            }

            //Fill fields on startup
            crs.next();
            updateFields();

            //Buttons
            JButton nextButton = new JButton("Next");
            nextButton.addActionListener(...);
            add(nextButton);

            JButton prevButton = new JButton("Previous");
            prevButton.addActionListener(...);
            add(prevButton);

            JButton saveButton = new JButton("Save changes");
            saveButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent event) {
                    try {
                        for (int a=0; a<fields.size(); a++) {
                            crs.updateString(a+1, fields.get(a).getText());
                        }
                        crs.acceptChanges();
                    } catch (SQLException ex) {
                        ex.printStackTrace();
                    }
                }
            });
            add(saveButton);
        }
        catch (SQLException ex) {
            ex.printStackTrace();
        }
    }

    private void updateFields() throws SQLException {
        for (int a = 0; a < fields.size(); a++) {
            fields.get(a).setText(crs.getString(a + 1).trim());
        }
    }
}

次と前の動作は正しく動作します。しかし、レコードを保存しようとすると例外が発生します。スタック トレースは次のとおりです。

org.postgresql.util.PSQLException: Cannot commit when autoCommit is enabled.
    at org.postgresql.jdbc2.AbstractJdbc2Connection.commit(AbstractJdbc2Connection.java:705)
    at com.sun.rowset.internal.CachedRowSetWriter.commit(CachedRowSetWriter.java:1396)
    at com.sun.rowset.CachedRowSetImpl.acceptChanges(CachedRowSetImpl.java:893)
    at test.MainPanel$3.actionPerformed(MainPanel.java:85)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2018)
    ...

この例外を防ぐにはどうすればよいですか? 自動コミットとは?Connection クラスには false に設定するオプションがあることは知っていますが、Connection クラスを使用したくありません。CachedResultSet を使用してすべてを達成したいと考えています。この問題を解決するにはどうすればよいですか?

4

4 に答える 4

1

追加するだけ

crs.getConnection ().setAutoCommit (false);

于 2013-03-20T17:31:17.453 に答える
1

URLの末尾に次を追加します。

"?relaxAutoCommit=true"
于 2014-01-03T18:31:28.107 に答える
0

私は同じ問題を抱えていました- CachedRowSetImpl.acceptChanges() は自動コミットをオフにできず、変更をコミットしようとすると、それらはすでに個別にコミットされているため、例外がスローされます。

これは、PostgreSQL が自動コミットに関して少しうるさいためかもしれません。PostgreSQL では、SQL ステートメントを使用して自動コミットをオフにすることは許可されていません (初期の頃は許可されていましたが)。代わりに、グループとしてコミットする必要がある SQL ステートメントは、SQL ステートメント BEGIN と COMMIT の間に挟む必要があります。PostgreSQL の JDBC ドライバーは Connection.setAutoCommit(false) をサポートしていますが、これはドライバーに背後で BEGIN-COMMIT サンドイッチを作成するように促すだけだと思います。私の推測では、 CachedRowSetImpl.acceptChanges() は setAutoCommit(false) を使用する代わりに SQL ステートメントを使用して自動コミットをオフにしようとしているため、失敗しています。

この問題を回避するために私が見つけた唯一の方法は、一時的な接続を作成し、自動コミットをオフにして、acceptChanges の 1 つの引数バージョンに接続を渡すことでした。したがって、これの代わりに:

crs.acceptChanges()

次のようなものがあります。

try (Connection con 
        = DriverManager.getConnection(url, username, password)) {
    con.setAutoCommit(false);
    crs.acceptChanges(con);
}

これには、回避しようとしていた Connection クラスを使用する必要がありますが、代替手段がない可能性があります (JDBC を使用しない以外に)。CachedRowSetImpl.getConnection() は、外部で作成されてから渡された接続のみを取得するため、使用できません。PostgreSQL で許可されていないため、自動コミットをオフにする URL を指定することはできません。CachedRowSetImpl.acceptChanges() を更新して、自動コミットを自動的にオフにすることができれば素晴らしいことです。ただし、コンパイラが CachedRowSetImpl が将来のリリースで削除される可能性があると警告していることを考えると、これはややありそうにありません。

于 2013-06-18T01:03:20.280 に答える
0

CachedRowset を使用しているときに、同様の問題が発生しました。MySQLデータベースを使用しています。多くの解決策を試しながら。私は最終的に私のために働いた2つの回避策を考え出しました. ここでそれらについて言及したいと思います:

  1. execute(con)Connection オブジェクトを使用して、 メソッドとメソッドに渡しますacceptChanges(con)。もセットsetAutoCommit(false)。例:

    Connection conn = DriverManager.getConnection(databaseUrl, username, password);
    conn.setAutoCommit(false);
    CachedRowSet rowSet=new CachedRowSetImpl();
    rowSet.setCommand("SELECT * FROM tableName");
    int [] keys = {1};  // Set column 1 as the key column in the RowSet
    rowSet.setKeyColumns(keys);
    rowSet.execute(conn);  // Execute on connection
    rowSet.moveToInsertRow();
    rowSet.updateInt(1, 89);  // Use column number
    rowSet.updateString(2, "Programming");
    rowSet.updateInt(3, 77);
    rowSet.insertRow();
    // Need to move away from insert row before apply changes
    rowSet.moveToCurrentRow();
    // Reconnect to data source to apply change in the RowSet.
    rowSet.acceptChanges(conn);  // On non-autocommit Connection
    

注: この場合 setURL()setUser()やなどの CachedRowset のプロパティを設定する必要はありませんsetPassword()。CachedRowset オブジェクトは Connection オブジェクトを使用して、手動で作成されたデータベースに接続するためです。 autocommit=false

  1. bobby-pauloseで言及されているようにURLに追加?relaxAutoCommit=trueします

    Example:
    String url = "jdbc:mysql://localhost:3306/DatabaseName?relaxAutoCommit=true";
    
于 2014-07-07T14:30:37.987 に答える