使用日期显示以前的登录
Use date to display previous login
我有一个使用 MySql 数据库的 java 应用程序。在我的应用程序中,我有一个 JInternalFrame,用于显示当前用户信息,例如 "Logged in as: " 和 "Last Login: ".
loggedInUserLabel.setText("Logged in as: ");
getContentPane().add(loggedInUserLabel);
loggedInUserLabel.setBounds(30, 30, 80, 30);
loggedInUser.setText("Admin");
getContentPane().add(loggedInUser);
loggedInUser.setBounds(140, 30, 80, 30);
lastLoginLabel.setText("Last Login:");
getContentPane().add(lastLoginLabel);
lastLoginLabel.setBounds(30, 60, 80, 30);
lastLogin.setText("14-03-2015");
getContentPane().add(lastLogin);
lastLogin.setBounds(140, 60, 80, 30);
如何设置日期,我目前已将其设置为虚拟值“14-03-2015”以显示我上次登录?有没有办法通过 MySql 来解决这个问题?
更新
我创建了一个新的 table 来存储一些登录详细信息:
CREATE TABLE IF NOT EXISTS `last_login`(
`username` varchar(20) NOT NULL,
`date` varchar(20) NOT NULL
);
登录的地方是这样的:
if (rs.next()) {
if (rs.getString("usertype").equals("Admin")) {
setLoggedInUser(userBox.getText());
insertData();
AdminMenu adminMenu = new AdminMenu();
adminMenu.setVisible(true);
setVisible(false);
} else if (rs.getString("usertype").equals("Employee")) {
setLoggedInUser(userBox.getText());
insertData();
EmployeeMenu employeeMenu = new EmployeeMenu();
employeeMenu.setVisible(true);
setVisible(false);
}
使用 "insertData" 将新数据插入我的 table,看起来像这样
public static void insertData() {
String user = "root";
String pass = "pass";
String schmea = "db";
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
String currentDate = dateFormat.format(date);
try {
Connection conn = MySql.getConnection(user, pass, "/" + schmea);
try {
String q = "insert into last_login(username,date) values(?,?)";
PreparedStatement stmt = conn.prepareStatement(q);
stmt.setString(1, loggedInUser);
stmt.setString(2, currentDate);
stmt.execute();
} catch (Exception ex) {
ex.printStackTrace();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
但是问题:我使用这段代码从数据库中检索日期
public void internalFrameActivated(InternalFrameEvent e) {
String user = "root";
String pass = "pass";
String schema = "db";
String loggedInUser = Login.getLoggedInUser();
try {
Connection conn = MySql.getConnection(user, pass, "/" + schema);
try {
String q = "select date from last_login where username=?";
PreparedStatement stmt = (PreparedStatement) conn.prepareStatement(q);
stmt.setString(1, loggedInUser);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
lastLogin.setText(rs.getString(1));
} else {
JOptionPane.showMessageDialog(rootPane, "Unable to retrieve information for user: " + loggedInUser + "!");
}
} catch (Exception ex) {
JOptionPane.showMessageDialog(rootPane, "Error in Query: " + ex.getMessage());
}
} catch (Exception ex) {
JOptionPane.showMessageDialog(rootPane, "Error in Database Connection: " + ex.getMessage());
}
}
我知道我总是会返回该用户的当前日期。我如何确定该用户的上一个日期?
想一想你想要多少持久性,你需要保存多少数据。您是否希望此信息在服务器重新启动后保留并仍然可用?
这将告诉您是否需要持久存储(磁盘、数据库等)或者是否可以在 RAM 中维护它。
然后,你有多少数据,访问频率等会告诉你应该如何组织它。
使用 Date-Time 类型
不要将字符串用于 date-time 值。使用 date-time 类型作为 date-time 值。
在 Java 8 及更高版本中,使用 java.time types such as Instant
(a moment on the timeline in UTC). If Java 8 technology is not available (Android etc.), then use the venerable Joda-Time 库。
要将数据传入和传出数据库,请使用 java.sql 类型,例如 java.sql.Timestamp
. Hopefully JDBC drivers will be updated to support java.time types directly, but until then use the java.time
-java.sql
conversion methods such as from( Instant )
and toInstant
。
在您的数据库中,了解其 date-time 类型。 SQL 规范定义了一些类型,但对这些类型的支持差异很大。一些数据库如 Postgres have excellent date-time support. Some such as SQLite have week support. Some such as H2 在中间。仔细研究数据库的文档。然后进行实验以确保您了解它的行为。 Date-time工作可能很棘手。
我不明白您使用没有 time-of-day 的 date-only 来跟踪登录。为什么您只关心一整天的登录?此外,date-only 是模糊的,因为日期的定义因时区而异。通常我们会在 UTC 时区跟踪 date-time 值等业务数据。然后,为了演示,由用户调整到特定时区expected/desired。
示例应用程序
以下是将登录尝试存储在数据库中的完整应用程序的源代码。此应用程序是人为设计的,旨在用于演示而非部署。使用风险自负。
您应用程序的 Swing 部分会让人分心。这个示例应用程序更简单,没有用户界面。每次您 运行 应用程序的 main
方法时,它都会报告之前的登录尝试,然后进行新的尝试。新的尝试随机选择一个用户名。
package com.example.logintracker;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
/**
* Contrived app to demonstrate saving login attempts to a database.
*
* This source code available via the Free Public License 1.0.0. http://opensource.org/licenses/FPL-1.0.0
*
* @author Basil Bourque
*/
public class App {
public static void main ( String[] args ) {
App app = new App ();
// Report the most recent login attempt.
Authenticator authenticator_Reporter = new Authenticator ();
LoginAttempt loginAttempt_Recent = authenticator_Reporter.getMostRecentAttempt ();
System.out.println ( "Recent loginAttempt: " + loginAttempt_Recent );
// Attempt a login.
Authenticator authenticator_Login = new Authenticator ();
ArrayList<String> userNames = new ArrayList<> ( Arrays.asList ( "Wendy" , "Lisa" , "Jesse" , "Oliver" , "Jasmine" , "Jean-Luc" , "Jarrod" , "Evonne" , "Elise" ) ); // Pick a name at random.
String userName = userNames.get ( new Random ().nextInt ( userNames.size () ) ); // From user-interface.
String password = "pw"; // From user-interface.
LoginAttempt loginAttempt_Now = authenticator_Login.authenticate ( userName , password );
System.out.println ( "Fresh loginAttempt: " + loginAttempt_Now );
if ( loginAttempt_Now.getSuccessful () ) {
// Run the app for this user.
// TODO: implement.
} else {
// Else block this user from running this app.
// TODO: implement.
}
}
}
Authenticator
是主要业务逻辑的入口。
package com.example.logintracker;
import java.time.Instant;
import java.util.UUID;
/**
* The business logic for handling login attempts. Called by separate user interface code.
*
* This source code available via the Free Public License 1.0.0. http://opensource.org/licenses/FPL-1.0.0
*
* @author Basil Bourque
*
*/
public class Authenticator {
// User-interface makes this call to execute a user’s login attempt.
public LoginAttempt authenticate ( String userName , String password ) {
Boolean successful = Boolean.FALSE;
// TODO: Add business logic to perform authentication. Hard-coded here to always succeed for this demonstration code.
successful = Boolean.TRUE;
LoginAttempt loginAttempt = new LoginAttempt ( userName , Instant.now () , successful , UUID.randomUUID () );
// Remember this attempt.
Persister persister = new Persister ();
persister.saveLoginAttempt ( loginAttempt );
return loginAttempt;
}
public LoginAttempt getMostRecentAttempt () {
Persister persister = new Persister ();
LoginAttempt loginAttempt = persister.fetchMostRecentLoginAttempt ();
return loginAttempt;
}
}
每次登录尝试的数据被存储为一个简单的 LoginAttempt
class,如 value objects。
package com.example.logintracker;
import java.time.Instant;
import java.util.UUID;
/**
*
* Value object holding the data describing each login attempt: who was the user, when attempted, was the login successful.
*
* This source code available via the Free Public License 1.0.0. http://opensource.org/licenses/FPL-1.0.0
*
* @author Basil Bourque
*
*/
public class LoginAttempt {
private String userName;
private Instant whenAttempted;
private Boolean successful;
private UUID uuid = null;
public LoginAttempt ( String userNameArg , Instant whenAttemptedArg , Boolean successfulArg , UUID uuidArg ) {
// System.out.println ( "Constructor of LoginAttempt: " + whenAttemptedArg + " user: " + userNameArg );
this.userName = userNameArg;
this.whenAttempted = whenAttemptedArg;
this.successful = successfulArg;
this.uuid = uuidArg;
}
// Getters. Read-only object.
public String getUserName () {
return this.userName;
}
public Instant getWhenAttempted () {
return this.whenAttempted;
}
public Boolean getSuccessful () {
return this.successful;
}
public UUID getUuid () {
return this.uuid;
}
// Override Object.
@Override
public String toString () {
return "LoginAttempt{ " + "userName=" + this.userName + " | whenAttempted=" + this.whenAttempted + " | successful=" + this.successful + " | uuid=" + this.uuid + " }";
}
}
这些登录尝试存储在基本 SQL table.
中
CREATE TABlE IF NOT EXISTS login_attempt_
(
uuid_ UUID DEFAULT RANDOM_UUID() PRIMARY KEY,
when_written_ TIMESTAMP DEFAULT NOW() NOT NULL,
username_ VARCHAR_IGNORECASE(255) NOT NULL,
when_attempted_ TIMESTAMP NOT NULL,
success_ BOOLEAN NOT NULL
)
;
每次此应用 运行 秒,它都会检查 table 是否存在。如果未找到,该应用会自动创建 table。包含 table 的数据库以及数据库用户和密码也是自动创建的。因此,只需 运行 这个应用程序即可查看演示。无需额外设置。
此示例数据库使用 H2
database、free-of-cost open-source pure-Java SQL 数据库。 H2 在嵌入模式或 client/server 模式下可以是 运行。在这个应用程序中,我们使用嵌入式模式。在 Maven 项目中,只需添加 com.h2database
.
的依赖项
Persister
class 处理与数据库的所有交互,保存和检索登录尝试。
package com.example.logintracker;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Handles all persisting of data to database or other storage mechanism. Called from other code handling business logic.
*
* This source code available via the Free Public License 1.0.0. http://opensource.org/licenses/FPL-1.0.0
*
* @author Basil Bourque
*
*/
public class Persister {
public void saveLoginAttempt ( LoginAttempt loginAttempt ) {
// Get database connection.
// Make prepared statement.
// Transfer pieces of data from LoginAttempt into PreparedStatement.
// Execute database transaction.
StringBuilder sql = new StringBuilder ();
sql.append ( "INSERT INTO login_attempt_ ( username_ , when_attempted_ , success_ , uuid_ ) " + " \n" );
sql.append ( "VALUES ( ? , ? , ? , ? )" + " \n" );
sql.append ( ";" + " \n" );
try ( Connection conn = this.fetchConnection (); ) {
// System.out.println ( "conn: " + conn );
try ( PreparedStatement ps = conn.prepareStatement ( sql.toString () ); ) {
ps.setString ( 1 , loginAttempt.getUserName () );
java.sql.Timestamp ts = java.sql.Timestamp.from ( loginAttempt.getWhenAttempted () ); // Converting from java.time.Instant to java.sql.Timestamp.
ps.setTimestamp ( 2 , ts );
ps.setBoolean ( 3 , loginAttempt.getSuccessful () );
ps.setObject ( 4 , loginAttempt.getUuid () );
ps.executeUpdate ();
} catch ( SQLException ex ) {
System.err.println ( "SQLException: " + ex.getMessage () );
// TODO: Handle exception.
}
} catch ( SQLException ex ) {
System.err.println ( "SQLException on this.fetchConnection: " + ex.getMessage () );
// TODO: Handle exception.
}
}
public LoginAttempt fetchMostRecentLoginAttempt () {
// Get database connection.
// Make prepared statement.
// Execute query.
// Transfer pieces of data from ResultSet to new LoginAttempt object.
// Return object (or null if failure occurred).
StringBuilder sql = new StringBuilder ();
sql.append ( "SELECT * " + " \n" );
sql.append ( "FROM login_attempt_" + " \n" );
sql.append ( "ORDER BY when_attempted_ DESC" + " \n" );
sql.append ( "LIMIT 1" + " \n" );
sql.append ( ";" + " \n" );
LoginAttempt loginAttempt = null;
try ( Connection conn = this.fetchConnection (); ) {
// System.out.println ( "conn: " + conn );
try ( Statement stmt = conn.createStatement (); ) {
ResultSet rs = stmt.executeQuery ( sql.toString () );
int count = 0;
while ( rs.next () ) {
count ++;
String userName = rs.getString ( "username_" );
java.sql.Timestamp whenWritten = rs.getTimestamp ( "when_attempted_" );
Boolean success = rs.getBoolean ( "success_" );
UUID uuid = ( UUID ) rs.getObject ( "uuid_" );
loginAttempt = new LoginAttempt ( userName , whenWritten.toInstant () , success , uuid ); // Converting from java.sql.Timestamp to java.time.Instant.
}
if ( count > 1 ) {
// TODO: Handle problem where more than one row returned.
}
} catch ( SQLException ex ) {
System.err.println ( "SQLException: " + ex.getMessage () );
// TODO: Handle exception.
}
} catch ( SQLException ex ) {
System.err.println ( "SQLException on this.fetchConnection: " + ex.getMessage () );
// TODO: Handle exception.
}
return loginAttempt;
}
private Connection fetchConnection () throws SQLException {
Connection conn = null;
try {
Class.forName ( "org.h2.Driver" );
} catch ( ClassNotFoundException e ) {
// TODO: Handle exception.
System.out.println ( "Database failure: " + e );
return null;
}
// Specify a database named 'LoginTracker.mv.db' in the Unix user’s home folder.
String dbFolderPath = "~/";
String dbName = "LoginTracker";
String dbUrl = "jdbc:h2:" + dbFolderPath + dbName;
String dbUserName = "h2";
String dbPassword = "pw";
try {
// If database does not yet exist, it is automatically created.
conn = DriverManager.getConnection ( dbUrl , dbUserName , dbPassword );
} catch ( SQLException ex ) {
System.out.println ( "SQLException on DriverManager.getConnection: " + ex.getMessage () );
// TODO: Handle exception when no Connection is made.
}
if ( null == conn ) {
System.out.println ( "Database error. No Connection." );
// TODO: Handle exception when no Connection is made.
} else {
// ELSE got database connection. Normal.
this.updateDatabaseStructureIfNeedBe ( conn );
}
return conn;
}
private void updateDatabaseStructureIfNeedBe ( Connection conn ) {
// Update database structure if needed.
// 'login_attempt_' Table.
StringBuilder sql = new StringBuilder ();
sql.append ( "CREATE TABlE IF NOT EXISTS login_attempt_" + " \n" );
sql.append ( "(" + " \n" );
sql.append ( "uuid_ UUID DEFAULT RANDOM_UUID() PRIMARY KEY," + " \n" ); // Primary key, UUID type.
sql.append ( "when_written_ TIMESTAMP DEFAULT NOW() NOT NULL," + " \n" ); // Record when this record was written to database. Apparently H2 only provides txn start time, not current time.
sql.append ( "username_ VARCHAR_IGNORECASE(255) NOT NULL," + " \n" ); // User’s name. Case-insensitive.
sql.append ( "when_attempted_ TIMESTAMP NOT NULL," + " \n" ); // When this login attempt was made.
sql.append ( "success_ BOOLEAN NOT NULL" + " \n" ); // Did this login attempt succeed or fail?
sql.append ( ")" + " \n" );
sql.append ( ";" + " \n" );
try ( Statement stmt = conn.createStatement () ) {
stmt.executeUpdate ( sql.toString () );
stmt.close ();
} catch ( SQLException ex ) {
System.err.println ( "SQLException: " + ex.getMessage () );
// TODO: Handle exception.
}
}
}
我有一个使用 MySql 数据库的 java 应用程序。在我的应用程序中,我有一个 JInternalFrame,用于显示当前用户信息,例如 "Logged in as: " 和 "Last Login: ".
loggedInUserLabel.setText("Logged in as: ");
getContentPane().add(loggedInUserLabel);
loggedInUserLabel.setBounds(30, 30, 80, 30);
loggedInUser.setText("Admin");
getContentPane().add(loggedInUser);
loggedInUser.setBounds(140, 30, 80, 30);
lastLoginLabel.setText("Last Login:");
getContentPane().add(lastLoginLabel);
lastLoginLabel.setBounds(30, 60, 80, 30);
lastLogin.setText("14-03-2015");
getContentPane().add(lastLogin);
lastLogin.setBounds(140, 60, 80, 30);
如何设置日期,我目前已将其设置为虚拟值“14-03-2015”以显示我上次登录?有没有办法通过 MySql 来解决这个问题?
更新
我创建了一个新的 table 来存储一些登录详细信息:
CREATE TABLE IF NOT EXISTS `last_login`(
`username` varchar(20) NOT NULL,
`date` varchar(20) NOT NULL
);
登录的地方是这样的:
if (rs.next()) {
if (rs.getString("usertype").equals("Admin")) {
setLoggedInUser(userBox.getText());
insertData();
AdminMenu adminMenu = new AdminMenu();
adminMenu.setVisible(true);
setVisible(false);
} else if (rs.getString("usertype").equals("Employee")) {
setLoggedInUser(userBox.getText());
insertData();
EmployeeMenu employeeMenu = new EmployeeMenu();
employeeMenu.setVisible(true);
setVisible(false);
}
使用 "insertData" 将新数据插入我的 table,看起来像这样
public static void insertData() {
String user = "root";
String pass = "pass";
String schmea = "db";
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
String currentDate = dateFormat.format(date);
try {
Connection conn = MySql.getConnection(user, pass, "/" + schmea);
try {
String q = "insert into last_login(username,date) values(?,?)";
PreparedStatement stmt = conn.prepareStatement(q);
stmt.setString(1, loggedInUser);
stmt.setString(2, currentDate);
stmt.execute();
} catch (Exception ex) {
ex.printStackTrace();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
但是问题:我使用这段代码从数据库中检索日期
public void internalFrameActivated(InternalFrameEvent e) {
String user = "root";
String pass = "pass";
String schema = "db";
String loggedInUser = Login.getLoggedInUser();
try {
Connection conn = MySql.getConnection(user, pass, "/" + schema);
try {
String q = "select date from last_login where username=?";
PreparedStatement stmt = (PreparedStatement) conn.prepareStatement(q);
stmt.setString(1, loggedInUser);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
lastLogin.setText(rs.getString(1));
} else {
JOptionPane.showMessageDialog(rootPane, "Unable to retrieve information for user: " + loggedInUser + "!");
}
} catch (Exception ex) {
JOptionPane.showMessageDialog(rootPane, "Error in Query: " + ex.getMessage());
}
} catch (Exception ex) {
JOptionPane.showMessageDialog(rootPane, "Error in Database Connection: " + ex.getMessage());
}
}
我知道我总是会返回该用户的当前日期。我如何确定该用户的上一个日期?
想一想你想要多少持久性,你需要保存多少数据。您是否希望此信息在服务器重新启动后保留并仍然可用?
这将告诉您是否需要持久存储(磁盘、数据库等)或者是否可以在 RAM 中维护它。
然后,你有多少数据,访问频率等会告诉你应该如何组织它。
使用 Date-Time 类型
不要将字符串用于 date-time 值。使用 date-time 类型作为 date-time 值。
在 Java 8 及更高版本中,使用 java.time types such as Instant
(a moment on the timeline in UTC). If Java 8 technology is not available (Android etc.), then use the venerable Joda-Time 库。
要将数据传入和传出数据库,请使用 java.sql 类型,例如 java.sql.Timestamp
. Hopefully JDBC drivers will be updated to support java.time types directly, but until then use the java.time
-java.sql
conversion methods such as from( Instant )
and toInstant
。
在您的数据库中,了解其 date-time 类型。 SQL 规范定义了一些类型,但对这些类型的支持差异很大。一些数据库如 Postgres have excellent date-time support. Some such as SQLite have week support. Some such as H2 在中间。仔细研究数据库的文档。然后进行实验以确保您了解它的行为。 Date-time工作可能很棘手。
我不明白您使用没有 time-of-day 的 date-only 来跟踪登录。为什么您只关心一整天的登录?此外,date-only 是模糊的,因为日期的定义因时区而异。通常我们会在 UTC 时区跟踪 date-time 值等业务数据。然后,为了演示,由用户调整到特定时区expected/desired。
示例应用程序
以下是将登录尝试存储在数据库中的完整应用程序的源代码。此应用程序是人为设计的,旨在用于演示而非部署。使用风险自负。
您应用程序的 Swing 部分会让人分心。这个示例应用程序更简单,没有用户界面。每次您 运行 应用程序的 main
方法时,它都会报告之前的登录尝试,然后进行新的尝试。新的尝试随机选择一个用户名。
package com.example.logintracker;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
/**
* Contrived app to demonstrate saving login attempts to a database.
*
* This source code available via the Free Public License 1.0.0. http://opensource.org/licenses/FPL-1.0.0
*
* @author Basil Bourque
*/
public class App {
public static void main ( String[] args ) {
App app = new App ();
// Report the most recent login attempt.
Authenticator authenticator_Reporter = new Authenticator ();
LoginAttempt loginAttempt_Recent = authenticator_Reporter.getMostRecentAttempt ();
System.out.println ( "Recent loginAttempt: " + loginAttempt_Recent );
// Attempt a login.
Authenticator authenticator_Login = new Authenticator ();
ArrayList<String> userNames = new ArrayList<> ( Arrays.asList ( "Wendy" , "Lisa" , "Jesse" , "Oliver" , "Jasmine" , "Jean-Luc" , "Jarrod" , "Evonne" , "Elise" ) ); // Pick a name at random.
String userName = userNames.get ( new Random ().nextInt ( userNames.size () ) ); // From user-interface.
String password = "pw"; // From user-interface.
LoginAttempt loginAttempt_Now = authenticator_Login.authenticate ( userName , password );
System.out.println ( "Fresh loginAttempt: " + loginAttempt_Now );
if ( loginAttempt_Now.getSuccessful () ) {
// Run the app for this user.
// TODO: implement.
} else {
// Else block this user from running this app.
// TODO: implement.
}
}
}
Authenticator
是主要业务逻辑的入口。
package com.example.logintracker;
import java.time.Instant;
import java.util.UUID;
/**
* The business logic for handling login attempts. Called by separate user interface code.
*
* This source code available via the Free Public License 1.0.0. http://opensource.org/licenses/FPL-1.0.0
*
* @author Basil Bourque
*
*/
public class Authenticator {
// User-interface makes this call to execute a user’s login attempt.
public LoginAttempt authenticate ( String userName , String password ) {
Boolean successful = Boolean.FALSE;
// TODO: Add business logic to perform authentication. Hard-coded here to always succeed for this demonstration code.
successful = Boolean.TRUE;
LoginAttempt loginAttempt = new LoginAttempt ( userName , Instant.now () , successful , UUID.randomUUID () );
// Remember this attempt.
Persister persister = new Persister ();
persister.saveLoginAttempt ( loginAttempt );
return loginAttempt;
}
public LoginAttempt getMostRecentAttempt () {
Persister persister = new Persister ();
LoginAttempt loginAttempt = persister.fetchMostRecentLoginAttempt ();
return loginAttempt;
}
}
每次登录尝试的数据被存储为一个简单的 LoginAttempt
class,如 value objects。
package com.example.logintracker;
import java.time.Instant;
import java.util.UUID;
/**
*
* Value object holding the data describing each login attempt: who was the user, when attempted, was the login successful.
*
* This source code available via the Free Public License 1.0.0. http://opensource.org/licenses/FPL-1.0.0
*
* @author Basil Bourque
*
*/
public class LoginAttempt {
private String userName;
private Instant whenAttempted;
private Boolean successful;
private UUID uuid = null;
public LoginAttempt ( String userNameArg , Instant whenAttemptedArg , Boolean successfulArg , UUID uuidArg ) {
// System.out.println ( "Constructor of LoginAttempt: " + whenAttemptedArg + " user: " + userNameArg );
this.userName = userNameArg;
this.whenAttempted = whenAttemptedArg;
this.successful = successfulArg;
this.uuid = uuidArg;
}
// Getters. Read-only object.
public String getUserName () {
return this.userName;
}
public Instant getWhenAttempted () {
return this.whenAttempted;
}
public Boolean getSuccessful () {
return this.successful;
}
public UUID getUuid () {
return this.uuid;
}
// Override Object.
@Override
public String toString () {
return "LoginAttempt{ " + "userName=" + this.userName + " | whenAttempted=" + this.whenAttempted + " | successful=" + this.successful + " | uuid=" + this.uuid + " }";
}
}
这些登录尝试存储在基本 SQL table.
中CREATE TABlE IF NOT EXISTS login_attempt_
(
uuid_ UUID DEFAULT RANDOM_UUID() PRIMARY KEY,
when_written_ TIMESTAMP DEFAULT NOW() NOT NULL,
username_ VARCHAR_IGNORECASE(255) NOT NULL,
when_attempted_ TIMESTAMP NOT NULL,
success_ BOOLEAN NOT NULL
)
;
每次此应用 运行 秒,它都会检查 table 是否存在。如果未找到,该应用会自动创建 table。包含 table 的数据库以及数据库用户和密码也是自动创建的。因此,只需 运行 这个应用程序即可查看演示。无需额外设置。
此示例数据库使用 H2
database、free-of-cost open-source pure-Java SQL 数据库。 H2 在嵌入模式或 client/server 模式下可以是 运行。在这个应用程序中,我们使用嵌入式模式。在 Maven 项目中,只需添加 com.h2database
.
Persister
class 处理与数据库的所有交互,保存和检索登录尝试。
package com.example.logintracker;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Handles all persisting of data to database or other storage mechanism. Called from other code handling business logic.
*
* This source code available via the Free Public License 1.0.0. http://opensource.org/licenses/FPL-1.0.0
*
* @author Basil Bourque
*
*/
public class Persister {
public void saveLoginAttempt ( LoginAttempt loginAttempt ) {
// Get database connection.
// Make prepared statement.
// Transfer pieces of data from LoginAttempt into PreparedStatement.
// Execute database transaction.
StringBuilder sql = new StringBuilder ();
sql.append ( "INSERT INTO login_attempt_ ( username_ , when_attempted_ , success_ , uuid_ ) " + " \n" );
sql.append ( "VALUES ( ? , ? , ? , ? )" + " \n" );
sql.append ( ";" + " \n" );
try ( Connection conn = this.fetchConnection (); ) {
// System.out.println ( "conn: " + conn );
try ( PreparedStatement ps = conn.prepareStatement ( sql.toString () ); ) {
ps.setString ( 1 , loginAttempt.getUserName () );
java.sql.Timestamp ts = java.sql.Timestamp.from ( loginAttempt.getWhenAttempted () ); // Converting from java.time.Instant to java.sql.Timestamp.
ps.setTimestamp ( 2 , ts );
ps.setBoolean ( 3 , loginAttempt.getSuccessful () );
ps.setObject ( 4 , loginAttempt.getUuid () );
ps.executeUpdate ();
} catch ( SQLException ex ) {
System.err.println ( "SQLException: " + ex.getMessage () );
// TODO: Handle exception.
}
} catch ( SQLException ex ) {
System.err.println ( "SQLException on this.fetchConnection: " + ex.getMessage () );
// TODO: Handle exception.
}
}
public LoginAttempt fetchMostRecentLoginAttempt () {
// Get database connection.
// Make prepared statement.
// Execute query.
// Transfer pieces of data from ResultSet to new LoginAttempt object.
// Return object (or null if failure occurred).
StringBuilder sql = new StringBuilder ();
sql.append ( "SELECT * " + " \n" );
sql.append ( "FROM login_attempt_" + " \n" );
sql.append ( "ORDER BY when_attempted_ DESC" + " \n" );
sql.append ( "LIMIT 1" + " \n" );
sql.append ( ";" + " \n" );
LoginAttempt loginAttempt = null;
try ( Connection conn = this.fetchConnection (); ) {
// System.out.println ( "conn: " + conn );
try ( Statement stmt = conn.createStatement (); ) {
ResultSet rs = stmt.executeQuery ( sql.toString () );
int count = 0;
while ( rs.next () ) {
count ++;
String userName = rs.getString ( "username_" );
java.sql.Timestamp whenWritten = rs.getTimestamp ( "when_attempted_" );
Boolean success = rs.getBoolean ( "success_" );
UUID uuid = ( UUID ) rs.getObject ( "uuid_" );
loginAttempt = new LoginAttempt ( userName , whenWritten.toInstant () , success , uuid ); // Converting from java.sql.Timestamp to java.time.Instant.
}
if ( count > 1 ) {
// TODO: Handle problem where more than one row returned.
}
} catch ( SQLException ex ) {
System.err.println ( "SQLException: " + ex.getMessage () );
// TODO: Handle exception.
}
} catch ( SQLException ex ) {
System.err.println ( "SQLException on this.fetchConnection: " + ex.getMessage () );
// TODO: Handle exception.
}
return loginAttempt;
}
private Connection fetchConnection () throws SQLException {
Connection conn = null;
try {
Class.forName ( "org.h2.Driver" );
} catch ( ClassNotFoundException e ) {
// TODO: Handle exception.
System.out.println ( "Database failure: " + e );
return null;
}
// Specify a database named 'LoginTracker.mv.db' in the Unix user’s home folder.
String dbFolderPath = "~/";
String dbName = "LoginTracker";
String dbUrl = "jdbc:h2:" + dbFolderPath + dbName;
String dbUserName = "h2";
String dbPassword = "pw";
try {
// If database does not yet exist, it is automatically created.
conn = DriverManager.getConnection ( dbUrl , dbUserName , dbPassword );
} catch ( SQLException ex ) {
System.out.println ( "SQLException on DriverManager.getConnection: " + ex.getMessage () );
// TODO: Handle exception when no Connection is made.
}
if ( null == conn ) {
System.out.println ( "Database error. No Connection." );
// TODO: Handle exception when no Connection is made.
} else {
// ELSE got database connection. Normal.
this.updateDatabaseStructureIfNeedBe ( conn );
}
return conn;
}
private void updateDatabaseStructureIfNeedBe ( Connection conn ) {
// Update database structure if needed.
// 'login_attempt_' Table.
StringBuilder sql = new StringBuilder ();
sql.append ( "CREATE TABlE IF NOT EXISTS login_attempt_" + " \n" );
sql.append ( "(" + " \n" );
sql.append ( "uuid_ UUID DEFAULT RANDOM_UUID() PRIMARY KEY," + " \n" ); // Primary key, UUID type.
sql.append ( "when_written_ TIMESTAMP DEFAULT NOW() NOT NULL," + " \n" ); // Record when this record was written to database. Apparently H2 only provides txn start time, not current time.
sql.append ( "username_ VARCHAR_IGNORECASE(255) NOT NULL," + " \n" ); // User’s name. Case-insensitive.
sql.append ( "when_attempted_ TIMESTAMP NOT NULL," + " \n" ); // When this login attempt was made.
sql.append ( "success_ BOOLEAN NOT NULL" + " \n" ); // Did this login attempt succeed or fail?
sql.append ( ")" + " \n" );
sql.append ( ";" + " \n" );
try ( Statement stmt = conn.createStatement () ) {
stmt.executeUpdate ( sql.toString () );
stmt.close ();
} catch ( SQLException ex ) {
System.err.println ( "SQLException: " + ex.getMessage () );
// TODO: Handle exception.
}
}
}