0

我有一个 Java Swing 应用程序,它使用简单的单例模式访问 Postgres 数据库:

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)
)

由于此应用程序将在本地网络中的许多机器上运行,我希望每个特定用户只有一个连接实例。也就是说,如果 userA 从一台机器登录,而 userA 稍后从另一台机器登录,则通知应该出现在两台机器上,第二次登录可以选择继续连接 - 在这种情况下,现有连接将被删除/丢失的。

我想我必须在我的用户表中添加一个新列(logged_on boolean)......在这种情况下,通过查找logged_on 的值并采取适当的行动来处理第二次登录。我的问题是,我如何才能关闭第一个连接?如何在数据库级别最多维护一个连接 - 每个用户?

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
)

到目前为止,我现在有三个主要的存储功能......明天可能会添加到它们。要迟到了。

第一个涉及请求用户登录。这将返回用户 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,并且其他所有属性都支持成功登录,那么前端会通知用户存在一个现有的连接,以及是否继续(断开现有的连接)。如果它是假的,那么它只是继续(没有任何提示)这个函数:

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';

userLoginHistoryId 存储在前端,因为我的 Java Swing 项目使用 MVC 架构,所以我的抽象模型类将在其构造函数中调用以下函数。我已听取您的建议,并将在每种方法中关闭连接。

-- 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你的问题更深,很难实现,我会建议你一些方法:当用户登录时,你应该更新两个字段(last_access_timestamp,computer_id),每次访问数据库你都应该更新last_access_timestamp。computer_id 和 last_access_time 应该有效可能 1 小时或更短,具体取决于应用程序的使用。当其他人尝试使用相同的 user_id 登录时,如果 now() - 1 hour < last_access_timestamp 则不应授予该用户访问权限。computer_id 是从应用程序生成的,每台计算机都应该是唯一的,并且始终生成相同的 computer_id。

我希望这个能帮上忙

于 2012-06-22T13:51:59.687 回答