0

単純なシングルトンパターンを使用してPostgresデータベースにアクセスするJavaSwingアプリケーションがあります。

public class DatabaseConnection {
    private static final String uname = "*******";
    private static final String pword = "*******";
    private static final String url = "*******************************";

    Connection connection;
    // load jdbc driver
    public DatabaseConnection(){
        try{
            Class.forName("org.postgresql.Driver");
            establishConnection();
        } catch (ClassNotFoundException ce) {
            System.out.println("Could not load jdbc Driver: ");
            ce.printStackTrace();
        }
    }
    public Connection establishConnection() {
        // TODO Auto-generated method stub
        try{
            connection = DriverManager.getConnection(url, uname, pword);
        } catch (SQLException e){
            System.out.println("Could not connect to database: ");
            e.printStackTrace();
        }
        return connection;
    }
}

public class SingletonConnection {

    private static DatabaseConnection con;

    public SingletonConnection(){}

    public static DatabaseConnection instance(){

        assert con == null;
            con = new DatabaseConnection();
        return con;
    }
}

これは、Pgadmin3によって作成された私のユーザーテーブルです(したがって、醜い大文字):

CREATE TABLE "user"
(
  id serial NOT NULL,
  "userRoleId" integer NOT NULL,
  "employeeId" bigint NOT NULL,
  "subjectId" bigint NOT NULL,
  username text NOT NULL,
  cryptpwd text NOT NULL,
  "userStatusId" integer NOT NULL,
  md5pwd text NOT NULL,
  CONSTRAINT pk_user PRIMARY KEY (id),
  CONSTRAINT "subjectId" FOREIGN KEY ("subjectId")
      REFERENCES subject (id) MATCH FULL
      ON UPDATE RESTRICT ON DELETE RESTRICT DEFERRABLE INITIALLY IMMEDIATE,
  CONSTRAINT user_employee_id FOREIGN KEY ("employeeId")
      REFERENCES employee (id) MATCH FULL
      ON UPDATE RESTRICT ON DELETE RESTRICT DEFERRABLE INITIALLY IMMEDIATE,
  CONSTRAINT "user_userRole_id" FOREIGN KEY ("userRoleId")
      REFERENCES "userRole" (id) MATCH FULL
      ON UPDATE RESTRICT ON DELETE RESTRICT DEFERRABLE INITIALLY IMMEDIATE,
  CONSTRAINT "user_userStatus_id" FOREIGN KEY ("userStatusId")
      REFERENCES "userStatus" (id) MATCH FULL
      ON UPDATE RESTRICT ON DELETE RESTRICT DEFERRABLE INITIALLY IMMEDIATE,
  CONSTRAINT "unique_user_userName" UNIQUE (username)
)

このアプリケーションはローカルネットワーク内の多くのマシンで実行されるため、特定のユーザーごとに1つの接続インスタンスのみを使用したいと思います。つまり、userAが1つのマシンからログインし、userAが別のマシンからログインした場合、通知は両方のマシンに表示され、2番目のログインには接続を続行するオプションがあります。この場合、既存の接続は切断されます。 /失った。

ユーザーテーブルに新しい列(logged_on boolean)を追加する必要があると思います...この場合、2番目のログインはlogged_onの値を見つけて適切に動作することによって処理されます。私の質問は、どうすれば最初の接続を閉じることができるのでしょうか?データベースレベルで、ユーザーごとに最大1つの接続を維持するにはどうすればよいですか?

4

2 に答える 2

1

わかりました、これは私が取り組んでいるものです。驚いたことに、私はあなたが Zamezela に言及した線に沿って何かを考えていました... まだ機能していませんが、これは機能すると思います.

私のユーザーテーブル:

CREATE TABLE "user"
(
  id serial NOT NULL,
  "userRoleId" integer NOT NULL,
  "employeeId" bigint NOT NULL,
  "subjectId" bigint NOT NULL,
  username text NOT NULL,
  cryptpwd text NOT NULL,
  "userStatusId" integer NOT NULL,
  md5pwd text NOT NULL,
  "loggedIn" boolean NOT NULL DEFAULT false,
  CONSTRAINT pk_user PRIMARY KEY (id),
  CONSTRAINT "subjectId" FOREIGN KEY ("subjectId")
      REFERENCES subject (id) MATCH FULL
      ON UPDATE RESTRICT ON DELETE RESTRICT DEFERRABLE INITIALLY IMMEDIATE,
  CONSTRAINT user_employee_id FOREIGN KEY ("employeeId")
      REFERENCES employee (id) MATCH FULL
      ON UPDATE RESTRICT ON DELETE RESTRICT DEFERRABLE INITIALLY IMMEDIATE,
  CONSTRAINT "user_userRole_id" FOREIGN KEY ("userRoleId")
      REFERENCES "userRole" (id) MATCH FULL
      ON UPDATE RESTRICT ON DELETE RESTRICT DEFERRABLE INITIALLY IMMEDIATE,
  CONSTRAINT "user_userStatus_id" FOREIGN KEY ("userStatusId")
      REFERENCES "userStatus" (id) MATCH FULL
      ON UPDATE RESTRICT ON DELETE RESTRICT DEFERRABLE INITIALLY IMMEDIATE,
  CONSTRAINT "unique_user_userName" UNIQUE (username)
)

すべてのユーザー ログインを記録するテーブルを作成しました。ユーザー アクティビティの追跡に役立ちます。

CREATE TABLE "userLoginHistory"
(
  "userId" integer NOT NULL,
  _datetime timestamp without time zone NOT NULL,
  hostname text NOT NULL,
  "osUsername" text NOT NULL,
  id bigserial NOT NULL,
  CONSTRAINT "pk_userLoginHistory" PRIMARY KEY (id),
  CONSTRAINT "userLoginHistory_user_id" FOREIGN KEY ("userId")
      REFERENCES "user" (id) MATCH FULL
      ON UPDATE RESTRICT ON DELETE RESTRICT DEFERRABLE INITIALLY IMMEDIATE
)

これまでのところ、主なストアド関数が 3 つあります。明日追加される可能性があります。遅くなってきた。

1 つ目は、ユーザー ログインの要求です。これは、ユーザー ID、ロール、誰かがこのユーザー アカウントにログオンしているかどうか、およびこのユーザーがアクティブかどうかを返します。

create type userLoginRequestReturnType as
(
  userId integer, -- user.id
  userRoleId integer, -- user.roleId
  loggedIn boolean, -- user.loggedIn
  userActive boolean -- whether user is active
);

CREATE OR REPLACE FUNCTION "user_login_request"(usernameIn text, passwordIn text)
returns setof userLoginRequestReturnType as
$$
declare
    user_Id integer;
    user_RoleId integer;
    user_StatusId integer;
    user_loggedIn boolean;
    user_Active boolean;

    sql text;
begin
      user_Active = false;
      select into user_Id, user_RoleId, user_StatusId, user_loggedIn id, "userRoleId", "userStatusId", "loggedIn" from "user" where username = usernameIn and cryptpwd = crypt(passwordIn, cryptpwd);
      if (user_id > 0) then -- record found
    select into user_Active "user_is_active"(user_StatusId);
      else
    user_id = 0;
    user_RoleId = 0;
    user_loggedIn = false;
    user_Active = false;
      end if;
      sql =  'select ' || user_Id || ', ' || user_RoleId || ', ' || user_loggedIn || ', ' || user_Active ||';';
      return query execute sql;
end;
$$ language 'plpgsql';

これはフロントエンドに渡されます。user_loggedIn が true で、他のすべての属性が正常なログインをサポートしている場合、フロント エンドは、既存の接続があること、および続行する (既存の接続を切断する) かどうかをユーザーに通知します。false の場合は、(プロンプトなしで) この関数に進みます。

CREATE OR REPLACE FUNCTION "user_login_complete"(userIdIN integer, hostnameIN text, osUsernameIN text)
returns bigint as
$$
declare
    currentTime timestamp without time zone;
    userLoginHistoryId bigint;
begin
      -- update user.loggedIn
      update "user" set "loggedIn" = true where id = userIdIN;
      -- insert into userLoginHistory
      currentTime = NOW()::timestamp without time zone;
      insert into "userLoginHistory" ("userId", _datetime, hostname, "osUsername") values (userIdIN, currentTime, hostnameIN, osUsernameIN);
      select into userLoginHistoryId currval('"userLoginHistory_id_seq"');
      return userLoginHistoryId;
end;
$$ language 'plpgsql';

Java Swing プロジェクトに MVC アーキテクチャを使用しているため、userLoginHistoryId はフロント エンドに格納されます。抽象モデル クラスは、コンストラクターで次の関数を呼び出します。私はあなたのアドバイスを受けて、それぞれの方法で接続を閉じます。

-- function to check if the current logged in session is the last one recorded in database
-- to be run before each connection to the database as per userId
-- new userLoginHistoryId must be inserted into table userLoginHistory, and the id PK value stored in the front end
--
-- returns: true, if current session is the last session recorded in table userLoginHistory for this user_autosuggest_by_ID
--    : false, if another login session has been recorded.
-- MUST BE EXECUTED BEFORE EACH AND EVERY DATABASE TRANSACTION!!!!!
CREATE OR REPLACE FUNCTION "user_login_session_check"(userIdIN integer, userLoginHistoryIdIN bigint)
returns boolean as
$$
declare
    results boolean;
    userLoginHistoryId bigint;
begin
      results = true;
      select into userLoginHistoryId id from "userLoginHistory" where "userId" = userIdIN ORDER BY id DESC LIMIT 1;
      if (userLoginHistoryIdIN = userLoginHistoryId) then
    results = true;
      else
    results = false;
      end if;
end;
$$ language 'plpgsql';

明日テストしますが、うまくいくことを願っています。お気軽にコメントください。

ありがとう。

于 2012-06-22T16:21:42.920 に答える
0

@greatkaluあなたの問題ははるかに深く、非常に困難です。いくつかのアプローチをお勧めします。ユーザーがログインするときに、2つのフィールド(last_access_timestamp、computer_id)を更新し、データベースへのアクセスごとにlast_access_timestampを更新する必要があります。computer_id と last_access_time は、アプリケーションの使用状況に応じて、1 時間以下で有効になるはずです。他の人が同じ user_id でログインしようとしたときに、 now() - 1 時間 < last_access_timestamp の場合、そのユーザーはアクセスを許可されません。computer_id はアプリケーションから生成され、すべてのコンピューターに対して一意であり、常に同じ computer_id を生成する必要があります。

これが役立つことを願っています

于 2012-06-22T13:51:59.687 に答える