问题
I'd like to display a 'indeterminate JProgressBar' in my Java application each time it accesses the SQLite database for reading / writing. I think the most important thing is to realize a trigger signal from the database. Then implement a listener to this trigger to display the progress bar.
I use SQLite with the Xerial JDBC driver with JDK 1.7. The application is 'stand alone'. The SQLite database is on the hdd. I have a good basic understanding of Java, but i'm no expert. Approaches with Hibernate / Spring etc. are way too much for me.
What i've found so far from an internet search (click header links for details):
Idea 1 (Manual approach):
Manually start the progress bar each time needed. Then execute Swingworker. Use its overridden done() method to stop the progress bar.
Pro:
- Very easy approach
Con:
- A lot of coding necessary for every event you need the progress bar
Idea 2 (Overriding methods):
Override the commit()
and the close()
method of java.sql.Connection
and send a trigger signal everytime these methods are executed.
Pro:
- Very easy approach
- No extra coding necessary
- Exact start and stop trigger for database access
- JDBC approach
Con:
- Parsing of commits necessary to find out if read or write access
Idea 3 (Man in the middle):
Use jdbctools as a middle layer for monitoring. Use JDBCLogger
or JDBCCallFilter
to detect accesses.
Pro:
- No extra coding necessary
- Exact start and stop trigger for database access
Con:
- Little documentation / no examples
- Outdated (2008)?
Idea 4 (Pooling):
Use ConnectionEventListener
from javax.sql
to monitor database connection.
Pro:
- Built-in Java SQL functionality
Con:
- Needs a PooledConnection object which is a bit over the top for the task
- No trigger for connection start
Idea 5 (use SQLite C interface):
Use 'Query Progress Callbacks' from SQLite C Interface which is intended for the use case.
Pro:
- Native SQLite functionality
Con:
- Because it is in 'C' it must be implemented in the JDBC driver to use this functionality. The Xerial driver doesn't seem to have it implemented
- Limited to SQLite database
Summary: I'd tend to try 'idea 2' but i'd like to hear what the experts are saying first: Which approach would you prefer or how would you implement that kind of trigger signal?
回答1:
At first:
Idea 2 (Overriding methods) didn't work because the methods are static and cannot be overridden.
Idea 5 (use SQLite C interface) needs a Java implementation for 'Query Progress Callbacks'. There's already some work done but it has not been merged into the JDBC driver by Xerial yet. For more details see here.
- Idea 3 & 4 seem to be feasable but make my project too complicated and harder to maintain.
So i came across to extend Idea 1:
- The best triggers for a progress bar are the moments when you open and close a database connection.
- These triggers can fire events which can be caught by an event listener that will open a dialog with a progress bar
At first you need to create some classes for the event mechanisms. You can find details about the idea of EventObjects here and here.
Db_Status.java:
public class Db_Status
{
public final static Db_Status CONNECTED = new Db_Status("connected");
public final static Db_Status PENDING = new Db_Status("pending");
public final static Db_Status CLOSED = new Db_Status("closed"); // Closed database connection
public final static Db_Status IDLE = new Db_Status("idle"); // Initial status of database after app start
public final static Db_Status ERROR = new Db_Status("error"); // Error while accessing / closing database
private String status;
public String toString() {
return status;
}
private Db_Status(String statusstring) {
status = statusstring;
}
}
Db_ActionEvent.java:
public class Db_ActionEvent extends EventObject
{
private Db_Status status;
public Db_ActionEvent(Object source, Db_Status DBstate) {
super(source);
status = DBstate;
}
public Db_Status Status() {
return status;
}
}
Db_ActionListener.java:
public interface Db_ActionListener {
public void DbActionReceived(Db_ActionEvent event );
}
You must then implement the listener. This is the place for the reaction after the listener has caught a database event, here: ouput to console:
public class UI_DbProgressbar implements Db_ActionListener{
public void DbActionReceived(Db_ActionEvent event) {
if(event.Status() == Db_Status.PENDING)
{
System.out.println("DB is busy!" );
}
else if(event.Status() == Db_Status.CLOSED)
{
System.out.println("DB connection is closed!");
}
else if(event.Status() == Db_Status.CONNECTED)
{
System.out.println("DB is connected!");
}
else
{
System.out.println("DB idle!" );
}
}
}
Finally you must establish the trigger mechanism. You may have a central class that handles all of the database accesses. There you will have to
- Create two central methods to open and close connections
- Add some methods to control the listener
Adapt the constructor to add right from the creation of the class a listener to it.
public class Db_DataExchange{
// // Adaptions to the constructor // public Db_DataExchange() { this.DBStateListeners = new ArrayList(); // Your constructor code here ... UI_DbProgressbar listener = new UI_DbProgressbar(); this.addDbActionListener(listener); } // // Add some methods to control the listener // private Db_Status DBAccessStatus = Db_Status.IDLE; private List DBStateListeners; public synchronized void addDbActionListener(Db_ActionListener l) { DBStateListeners.add( l ); } public synchronized void removeDbActionListener(Db_ActionListener l) { DBStateListeners.remove( l ); } private synchronized void fireDbActionEvent(Db_Status AccessStatus) { DBAccessStatus = AccessStatus; Db_ActionEvent status = new Db_ActionEvent(this, DBAccessStatus); Iterator listeners = DBStateListeners.iterator(); while(listeners.hasNext()) { ((Db_ActionListener) listeners.next()).DbActionReceived(status); } } // // Create two central methods to open and close connections // /** * Your method to establish a jdbc database connection */ public Connection EstablishDBConnection(String ConnectionURL){ Connection c = null; try { // Load JDBC sqlite driver Class.forName(this.DBDriverName); // url - a database url of the form jdbc:subprotocol:subname c = DriverManager.getConnection(ConnectionURL); c.setAutoCommit(false); System.out.println("Database connected successfully!"); // this ist the important trigger: this.fireDbActionEvent(Db_Status.CONNECTED); return c; } catch ( Exception e ) { System.err.println( e.getClass().getName() + ": " + e.getMessage() ); this.fireDbActionEvent(Db_Status.ERROR); return c; } } /** * Your method to close a jdbc database connection */ public boolean CloseDBConnection(Connection c){ try { c.close(); // finally close // this ist the important trigger: this.fireDbActionEvent(Db_Status.CLOSED); return true; } catch ( Exception e ) { //System.err.println( e.getClass().getName() + ": " + e.getMessage() ); logger.error("Error in {}: {}",e.getClass().getName(),e.getMessage()); this.fireDbActionEvent(Db_Status.ERROR); return false; } }
}
来源:https://stackoverflow.com/questions/36306324/how-to-implement-a-listener-for-access-events-of-a-sqlite-database-in-java