java - Using threads to make database requests -
i'm trying understand how threads works in java. simple database request returns resultset. i'm using javafx.
package application; import java.sql.resultset; import java.sql.sqlexception; import javafx.fxml.fxml; import javafx.scene.control.button; import javafx.scene.control.label; import javafx.scene.control.textfield; public class controller{ @fxml private button getcoursebtn; @fxml private textfield courseid; @fxml private label coursecodelbl; private modelcontroller mcontroller; private void requestcoursename(){ string coursename = ""; course c = new course(); c.setccode(integer.valueof(courseid.gettext())); mcontroller = new modelcontroller(c); try { resultset rs = mcontroller.<course>get(); if(rs.next()){ coursecodelbl.settext(rs.getstring(1)); } } catch (sqlexception e) { // todo auto-generated catch block e.printstacktrace(); } // return coursename; } public void getcoursenameonclick(){ try { // coursecodelbl.settext(requestcoursename()); thread t = new thread(new runnable(){ public void run(){ requestcoursename(); } }, "thread a"); t.start(); } catch (numberformatexception e) { // todo auto-generated catch block e.printstacktrace(); } } } this returns exception:
exception in thread "thread a" java.lang.illegalstateexception: not on fx application thread; currentthread = thread a
how correctly implement threading every database request executed in second thread instead of main thread?
i've heard of implementing runnable how invoke different methods in run method?
never worked threading before thought it's time it.
threading rules javafx
there 2 basic rules threads , javafx:
- any code modifies or accesses state of node part of scene graph must executed on javafx application thread. other operations (e.g. creating new
stages) bound rule. - any code may take long time run should executed on background thread (i.e. not on fx application thread).
the reason first rule that, ui toolkits, framework written without synchronization on state of elements of scene graph. adding synchronization incurs performance cost, , turns out prohibitive cost ui toolkits. 1 thread can safely access state. since ui thread (fx application thread javafx) needs access state render scene, fx application thread thread on can access "live" scene graph state. in javafx 8 , later, methods subject rule perform checks , throw runtime exceptions if rule violated. (this in contrast swing, can write "illegal" code , may appear run fine, in fact prone random , unpredictable failure @ arbitrary time.) this cause of illegalstateexception seeing: calling coursecodelbl.settext(...) thread other fx application thread.
the reason second rule fx application thread, being responsible processing user events, responsible rendering scene. if perform long-running operation on thread, ui not rendered until operation complete, , become unresponsive user events. while won't generate exceptions or cause corrupt object state (as violating rule 1 will), (at best) creates poor user experience.
thus if have long-running operation (such accessing database) needs update ui on completion, basic plan perform long-running operation in background thread, returning results of operation when complete, , schedule update ui on ui (fx application) thread. single-threaded ui toolkits have mechanism this: in javafx can calling platform.runlater(runnable r) execute r.run() on fx application thread. (in swing, can call swingutilities.invokelater(runnable r) execute r.run() on awt event dispatch thread.) javafx (see later in answer) provides higher-level api managing communication fx application thread.
general practices multithreading
the best practice working multiple threads structure code executed on "user-defined" thread object initialized fixed state, has method perform operation, , on completion returns object representing result. using immutable objects initialized state , computation result highly desirable. idea here eliminate possibility of mutable state being visible multiple threads far possible. accessing data database fits idiom nicely: can initialize "worker" object parameters database access (search terms, etc). perform database query , result set, use result set populate collection of domain objects, , return collection @ end.
in cases necessary share mutable state between multiple threads. when absolutely has done, need synchronize access state avoid observing state in inconsistent state (there other more subtle issues need addressed, such liveness of state, etc). strong recommendation when needed use high-level library manage these complexities you.
using javafx.concurrent api
javafx provides concurrency api designed executing code in background thread, api designed updating javafx ui on completion of (or during) execution of code. api designed interact java.util.concurrent api, provides general facilities writing multithreaded code (but no ui hooks). key class in javafx.concurrent task, represents single, one-off, unit of work intended performed on background thread. class defines single abstract method, call(), takes no parameters, returns result, , may throw checked exceptions. task implements runnable run() method invoking call(). task has collection of methods guaranteed update state on fx application thread, such updateprogress(...), updatemessage(...), etc. defines observable properties (e.g. state , value): listeners these properties notified of changes on fx application thread. finally, there convenience methods register handlers (setonsucceeded(...), setonfailed(...), etc); handlers registered via these methods invoked on fx application thread.
so general formula retrieving data database is:
- create
taskhandle call database. - initialize
taskstate needed perform database call. - implement task's
call()method perform database call, returning results of call. - register handler task send results ui when complete.
- invoke task on background thread.
for database access, recommend encapsulating actual database code in separate class knows nothing ui (data access object design pattern). have task invoke methods on data access object.
so might have dao class (note there no ui code here):
public class widgetdao { // in real life, might want connection pool here, though // desktop applications single connection suffices: private connection conn ; public widgetdao() throws exception { conn = ... ; // initialize connection (or connection pool...) } public list<widget> getwidgetsbytype(string type) throws sqlexception { try (preparedstatement pstmt = conn.preparestatement("select * widget type = ?")) { pstmt.setstring(1, type); resultset rs = pstmt.executequery(); list<widget> widgets = new arraylist<>(); while (rs.next()) { widget widget = new widget(); widget.setname(rs.getstring("name")); widget.setnumberofbigredbuttons(rs.getstring("btncount")); // ... widgets.add(widget); } return widgets ; } } // ... public void shutdown() throws exception { conn.close(); } } retrieving bunch of widgets might take long time, calls ui class (e.g controller class) should schedule on background thread. controller class might this:
public class mycontroller { private widgetdao widgetaccessor ; // java.util.concurrent.executor typically provides pool of threads... private executor exec ; @fxml private textfield widgettypesearchfield ; @fxml private tableview<widget> widgettable ; public void initialize() throws exception { widgetaccessor = new widgetdao(); // create executor uses daemon threads: exec = executors.newcachedthreadpool(runnable -> { thread t = new thread(runnable); t.setdaemon(true); return t ; }); } // handle search button: @fxml public void searchwidgets() { final string searchstring = widgettypesearchfield.gettext(); task<list<widget>> widgetsearchtask = new task<list<widget>>() { @override public list<widget> call() throws exception { return widgetaccessor.getwidgetsbytype(searchstring); } }; widgetsearchtask.setonfailed(e -> { widgetsearchtask.getexception().printstacktrace(); // inform user of error... }); widgetsearchtask.setonsucceeded(e -> // task.getvalue() gives value returned call()... widgettable.getitems().setall(widgetsearchtask.getvalue())); // run task using thread thread pool: exec.execute(widgetsearchtask); } // ... } notice how call (potentially) long-running dao method wrapped in task run on background thread (via accessor) prevent blocking ui (rule 2 above). update ui (widgettable.setitems(...)) executed on fx application thread, using task's convenience callback method setonsucceeded(...) (satisfying rule 1).
in case, database access performing returns single result, might have method like
public class mydao { private connection conn ; // constructor etc... public course getcoursebycode(int code) throws sqlexception { try (preparedstatement pstmt = conn.preparestatement("select * course c_code = ?")) { pstmt.setint(1, code); resultset results = pstmt.executequery(); if (results.next()) { course course = new course(); course.setname(results.getstring("c_name")); // etc... return course ; } else { // maybe throw exception if want insist course given code exists // or consider using optional<course>... return null ; } } } // ... } and controller code like
final int coursecode = integer.valueof(courseid.gettext()); task<course> coursetask = new task<course>() { @override public course call() throws exception { return mydao.getcoursebycode(coursecode); } }; coursetask.setonsucceeded(e -> { course course = coursetask.getcourse(); if (course != null) { coursecodelbl.settext(course.getname()); } }); exec.execute(coursetask); the api docs task have many more examples, including updating progress property of task (useful progress bars..., etc.
Comments
Post a Comment