【问题标题】:How to work with multiple connections to a single database?如何使用多个连接到单个数据库?
【发布时间】:2022-12-18 08:35:43
【问题描述】:

我是堆栈溢出的新手,我目前正在使用 NetBeans 和 java 来制作一个桌面应用程序,多个用户可以在其中同时访问一个数据库。但不幸的是,我尝试了几种启用行级锁定和 TRANSACTION_READ_COMMITTED 的代码,这在 db.apache.org 中有说明,但没有用。
我目前在 derby 中使用嵌入式数据库,通过搜索互联网,我找到了这个 db.apache.org enter image description here 和这个 enter image description here

我无法理解如何通过启用行级锁定和 TRANSACTION_READ_COMMITTED 来设置多用户数据库访问。

我试过这段代码,我通过导入实现 TRANSACTION_READ_COMMITTED

import static java.sql.Connection.TRANSACTION_READ_COMMITTED;

con.setTransactionIsolation(TRANSACTION_READ_COMMITTED);

将隔离设置为 TRANSACTION_READ_COMMITTED。

这是我的程序。

`

import java.sql.*;
import javax.swing.*;
import java.util.logging.*;
import static java.sql.Connection.TRANSACTION_READ_COMMITTED;

public class Connect_data extends javax.swing.JFrame {
    
    String temp_use, temp_pass, temp_usertype, n, s , u;
    //FROM ACCOUNT
    
    Connection con;
    Statement stmt;
    ResultSet rs;
public void DoConnect(){

        try{
    
        //Connect TO THE DATABASE
    
        String host = "jdbc:derby:C:\\DATABSE_SUB\\VERe";
        String uName = "josh";
        String uPass = "1234";
        
        
        con = DriverManager.getConnection(host, uName, uPass);
        
        stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
    
        String sql = "SELECT * FROM ADD_BTN";
        
        rs = stmt.executeQuery(sql);
    
    
        con.setTransactionIsolation(TRANSACTION_READ_COMMITTED);
    
        }catch(SQLException err){
            JOptionPane.showMessageDialog(Connect_data.this, err.getMessage());
        } 
    }
}

`

我缺少的是启用行级锁定,老实说我不知道​​如何实现它。我试着把这个

-- database-level property
CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY(
    'derby.storage.rowLocking', 'true')

在数据库服务 NetBeans 的执行命令中,但它没有用。

当我运行它连接到数据库的程序时,但是当我运行第二个程序时出现错误,这就是我得到的输出。

Exception in thread "AWT-EventQueue-0" java.lang.SecurityException: sealing violation: package org.apache.derby.security is sealed
    at java.base/jdk.internal.loader.BuiltinClassLoader.getAndVerifyPackage(BuiltinClassLoader.java:906)
    at java.base/jdk.internal.loader.BuiltinClassLoader.defineOrCheckPackage(BuiltinClassLoader.java:877)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.defineOrCheckPackage(ClassLoaders.java:211)
    at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:849)
    at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
    at org.apache.derby.jdbc.AutoloadedDriver.connect(Unknown Source)
    at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:681)
    at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:229)
    at New_Pack.Connect_data.DoConnect(Connect_data.java:132)
    at New_Pack.Connect_data.jButton1ActionPerformed(Connect_data.java:624)
    at New_Pack.Connect_data$7.actionPerformed(Connect_data.java:580)
    at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1972)
    at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2313)
    at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405)
    at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
    at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:279)
    at java.desktop/java.awt.Component.processMouseEvent(Component.java:6626)
    at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3389)
    at java.desktop/java.awt.Component.processEvent(Component.java:6391)
    at java.desktop/java.awt.Container.processEvent(Container.java:2266)
    at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5001)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833)
    at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4948)
    at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4575)
    at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4516)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2310)
    at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4833)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:722)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:716)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:97)
    at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:746)
    at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:744)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
    at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:743)
    at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

我期待这会锻炼,但我找不到答案。我需要帮助。

【问题讨论】:

  • 您是否在一台计算机上同时运行同一个应用程序两次?
  • 是的,或者我应该在执行和编译这个程序时使用单独的计算机并使用网络连接到数据库?因为我在计划用户何时同时连接到同一个数据库。在这种情况下,我试图在执行和编译这个 java 程序之前修复它。
  • 我不确定如果我需要先编译然后尝试连接不同的计算机是否可行。还是我在一台计算机上同时运行同一个应用程序两次而做错了?
  • 我建议设置一些简单的 jmeter 测试来模拟多个用户。

标签: java swing netbeans derby embedded-database


【解决方案1】:

这里有多个问题:

首要的

您在类路径上有两次德比。解决这个问题。

中学

德比几乎没有维护,不要使用它。更重要的是,数据库可以管理多个同时连接,但当你像这样使用它时就不行了,这是一个“进程内”设置,JVM 本身成为数据库服务器。在同一硬件上同时运行/使用相同的底层文件作为数据存储的任何其他应用程序基本上是不可能的 - 只有一个“数据库引擎”可以同时运行。这意味着如果只有 1 个 JVM 托管多个连接,则可以执行此操作,但如果您打算启动多个 JVM 并让它们使用 db 相互同步,则不能这样做。解决方案是单独运行一个实际的数据库服务器。如果你这样做,你不妨选择一个好的数据库引擎,比如 postgres。

如果一定要进程内,hsqldb或者h2都是全java的DB引擎,比较方便。如果您在进程内运行,您仍然无法使用相同的后备数据存储运行 2 个 JVM,您必须有一个单独的“服务器”(如果您愿意,h2 也可以作为服务器运行)。

大专

数据库不是这样工作的,至少,我所知道的没有数据库引擎。您为每个“数据存储”运行 1 个数据库引擎,您不能启动 2 个单独的进程,每个进程都从相同的源数据文件运行数据库查询。这不是事务的目的,也不是隔离级别配置的目的。也许那里有一个数据库引擎使用文件系统或管道或本地 TCP/IP 连接来传达细节,但如果没有这样的“通信管道”,这就不可能工作。

因此,您需要 1 个 java 进程(或非 java 进程,当然 postgres 也可以)作为数据库引擎,然后每个运行的客户端应用程序都需要 1 个 java 进程,它连接到数据库引擎。设置起来很复杂(例如,我不知道有任何库会在数据库引擎未运行时启动它,并在需要时将其拆除)。您仍然可以使用 derby 或 h2 或 hsql 来托管“所有 java,进程内数据库引擎”,但是您需要检查另一个 java 进程是否已经在使用该数据库,并进行设置以启动仅本地主机响应TCP/IP 服务器套接字,以便其他应用程序可以连接到第一个应用程序,以便第一个应用程序是唯一直接接触数据库文件的应用程序。这一切都非常非常复杂,我不知道有多少图书馆有这个概念。可能是因为“java 桌面应用程序”很少见,而“旨在同时运行不止一次的 java 桌面应用程序”更是如此。

四分之一

“行级锁定”的概念已经过时。测试几乎是不可能的,并且需要非常容易出错和挑剔的确定要锁定的行,或者非常广泛的锁定策略,这意味着应用程序无法扩展。

正确的方法是使用乐观锁定和 SERIALIZABLE 事务级别。任何低于 SERIALIZABLE 的事务级别都有各种真正令人讨厌的警告。例如,如果这是运行 ATM 机的伪代码:

AccountId accountId = cardReader.getAccountIdFromInsertedCard();
int requested = askUserToPickAmount();
db.startTransaction();
int balance = db.select("SELECT balance FROM account WHERE accountId = ?", accountId).singleInt();
if (balance < requested) throw new InsufficientBalanceException();
int newBalance = balance - requested;
db.update("UPDATE account SET balance = ? WHERE accountId = ?", newBalance, accountId);
db.commit();
cashEmitter.emit(requested);

这看起来不错,有交易等等,它是确实除了 SERIALIZABLE 之外的所有东西都坏了。如果你做到了SELECT .. FOR UPDATE这取决于。

可序列化的一个缺点是数据库偶尔会抛出一个 SQLException,其状态类型代码指示(并且这些代码不统一,因此您需要为每个数据库引擎编写一个检测器)“重试异常”。对此的回应就是它所说的:重新开始。除了,因为计算机是可靠的,它会像你即将在街上撞到某人一样结束,除了它是一面镜子。你向左闪,他们向左闪。你们撞到了对方。解决方案:引入随机性。请参阅以太网协议。关键是,要做到这一点真的很复杂,你做到了不是想动手做这些。

因此,切勿直接使用 JDBC,它并不意味着作为最终产品的 API 使用。请改用JDBIJOOQ;他们有更好的 API 并且大部分都内置了这个(文档解释了如何正确设置它并获得事务性,自动重试 lambda 等)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-13
    • 1970-01-01
    • 1970-01-01
    • 2021-07-02
    • 1970-01-01
    • 2021-12-09
    • 2014-07-04
    相关资源
    最近更新 更多