代码老外的,学习学习
该代码主要功能是通过JavaFX的concurrent实现异步的数据库操作。
代码里是通过java的多线程执行task,并不是通过javafx的service。代码很简单通过二个task,一个初始化数据库,一个查询数据库。结果返回显示。
没想到过task.getValue()既是call的返回值,学习到了。
该代码主要功能是通过JavaFX的concurrent实现异步的数据库操作。
package h2app;
import java.sql.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.*;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.*;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class H2Tasks extends Application {
private static final Logger logger = Logger.getLogger(H2Tasks.class
.getName());
private static final String[] SAMPLE_NAME_DATA = { "John", "Jill", "Jack",
"Jerry" };
public static void main(String[] args) {
launch(args);
}
// executes database operations concurrent to JavaFX operations.
private ExecutorService databaseExecutor;
// the future's data will be available once the database setup has been
// complete.
private Future databaseSetupFuture;
// initialize the program.
// setting the database executor thread pool size to 1 ensures
// only one database command is executed at any one time.
@Override
public void init() throws Exception {
databaseExecutor = Executors.newFixedThreadPool(1,
new DatabaseThreadFactory());
// run the database setup in parallel to the JavaFX application setup.
DBSetupTask setup = new DBSetupTask();
databaseSetupFuture = databaseExecutor.submit(setup);
}
// shutdown the program.
@Override
public void stop() throws Exception {
databaseExecutor.shutdown();
if (!databaseExecutor.awaitTermination(3, TimeUnit.SECONDS)) {
logger.info("Database execution thread timed out after 3 seconds rather than shutting down cleanly.");
}
}
// start showing the UI.
@Override
public void start(Stage stage) throws InterruptedException,
ExecutionException {
// wait for the database setup to complete cleanly before showing any
// UI.
// a real app might use a preloader or show a splash screen if this
// was to take a long time rather than just pausing the JavaFX
// application thread.
databaseSetupFuture.get();
final ListView<String> nameView = new ListView();
final ProgressIndicator databaseActivityIndicator = new ProgressIndicator();
databaseActivityIndicator.setVisible(false);
final Button fetchNames = new Button("Fetch names from the database");
fetchNames.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
fetchNamesFromDatabaseToListView(fetchNames,
databaseActivityIndicator, nameView);
}
});
final Button clearNameList = new Button("Clear the name list");
clearNameList.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
nameView.getItems().clear();
}
});
VBox layout = new VBox(10);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 15;");
layout.getChildren().setAll(
HBoxBuilder
.create()
.spacing(10)
.children(fetchNames, clearNameList,
databaseActivityIndicator).build(), nameView);
layout.setPrefHeight(200);
stage.setScene(new Scene(layout));
stage.show();
}
private void fetchNamesFromDatabaseToListView(final Button triggerButton,
final ProgressIndicator databaseActivityIndicator,
final ListView listView) {
final FetchNamesTask fetchNamesTask = new FetchNamesTask();
triggerButton.setDisable(true);
databaseActivityIndicator.setVisible(true);
databaseActivityIndicator.progressProperty().bind(
fetchNamesTask.progressProperty()); //进度条绑定任务
fetchNamesTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() { //task的成功执行事件
@Override
public void handle(WorkerStateEvent t) {
listView.setItems(fetchNamesTask.getValue()); //task.getValue()获取task.call()方法返回的值
}
});
fetchNamesTask.runningProperty().addListener(
new ChangeListener<Boolean>() {
@Override
public void changed(
ObservableValue<? extends Boolean> observable,
Boolean wasRunning, Boolean isRunning) {
if (!isRunning) {
triggerButton.setDisable(false);
databaseActivityIndicator.setVisible(false);
}
};
});
databaseExecutor.submit(fetchNamesTask);
}
abstract class DBTask<T> extends Task<T> {
DBTask() {
setOnFailed(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent t) {
logger.log(Level.SEVERE, null, getException());
}
});
}
}
class FetchNamesTask extends DBTask<ObservableList<String>> {
@Override
protected ObservableList<String> call() throws Exception {
// artificially pause for a while to simulate a long running
// database connection.
Thread.sleep(1000);
Connection con = getConnection();
return fetchNames(con);
}
private ObservableList<String> fetchNames(Connection con)
throws SQLException {
logger.info("Fetching names from database");
ObservableList<String> names = FXCollections.observableArrayList();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select name from employee");
while (rs.next()) {
names.add(rs.getString("name"));
}
logger.info("Found " + names.size() + " names");
return names;
}
}
class DBSetupTask<Void> extends DBTask {
@Override
protected Void call() throws Exception {
Connection con = getConnection();
if (!schemaExists(con)) {
createSchema(con);
populateDatabase(con);
}
return null;
}
private boolean schemaExists(Connection con) {
logger.info("Checking for Schema existence");
try {
Statement st = con.createStatement();
st.executeQuery("select count(*) from employee");
logger.info("Schema exists");
} catch (SQLException ex) {
logger.info("Existing DB not found will create a new one");
return false;
}
return true;
}
private void createSchema(Connection con) throws SQLException {
logger.info("Creating schema");
Statement st = con.createStatement();
String table = "create table employee(id integer, name varchar(64))";
st.executeUpdate(table);
logger.info("Created schema");
}
private void populateDatabase(Connection con) throws SQLException {
logger.info("Populating database");
Statement st = con.createStatement();
for (String name : SAMPLE_NAME_DATA) {
st.executeUpdate("insert into employee values(1,'" + name
+ "')");
}
logger.info("Populated database");
}
}
private Connection getConnection() throws ClassNotFoundException,
SQLException {
logger.info("Getting a database connection");
Class.forName("com.mysql.jdbc.Driver");
return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&noAccessToProcedureBodies=true", "root", "root");
}
static class DatabaseThreadFactory implements ThreadFactory {
static final AtomicInteger poolNumber = new AtomicInteger(1);
@Override
public Thread newThread(Runnable runnable) {
Thread thread = new Thread(runnable, "Database-Connection-"
+ poolNumber.getAndIncrement() + "-thread");
thread.setDaemon(true);
return thread;
}
}
}
代码里是通过java的多线程执行task,并不是通过javafx的service。代码很简单通过二个task,一个初始化数据库,一个查询数据库。结果返回显示。
fetchNamesTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() { //task的成功执行事件
@Override
public void handle(WorkerStateEvent t) {
listView.setItems(fetchNamesTask.getValue()); //task.getValue()获取task.call()方法返回的值
}
});
没想到过task.getValue()既是call的返回值,学习到了。