Files and persistence (File တွေနဲ့ ဒေတာ ရေရှည် သိမ်းဆည်းခြင်း)
IA အတွက် tool တွေပါ၊ စာမေးပွဲ ခေါင်းစဉ် မဟုတ်ပါဘူး။ ဒါတွေက ကိုယ့် IA (Internal Assessment: IB သင်တန်းမှာ ကျောင်းသားတစ်ယောက်ချင်း ကိုယ်တိုင် တည်ဆောက်ရပြီး ကျောင်းက ဆရာက အမှတ်ပေးတဲ့ အပိုင်း) နဲ့ ကိုက်အောင် ပြင်သုံးရမယ့် JavaFX (Java နဲ့ desktop app တည်ဆောက်တဲ့ library) နဲ့ standard library (Java မှာ အသင့်ပါတဲ့ library) code ပုံစံတွေပါ။ ဒီမှာ ပြထားတဲ့ text file (စာသားပဲ ပါတဲ့ file) ကိုင်တွယ်ပုံက syllabus (IB သင်ရိုး) ထဲမှာ တွေ့ရတဲ့ B2.5 file processing (file ဖတ်ခြင်းနဲ့ ရေးခြင်း) အကြောင်းအရာလည်း ဖြစ်လို့ ပြန်လေ့ကျင့်ဖို့အတွက်ပါ အသုံးဝင်ပါတယ်။ B2.5 က IB Computer Science (နိုင်ငံတကာ ကျောင်းတွေမှာ သင်တဲ့ ကွန်ပျူတာသိပ္ပံ သင်ရိုး) ရဲ့ သင်ရိုးထဲက အပိုင်း နံပါတ်ပါ။ ကိုယ့် IA က ကိုယ်တိုင် လုပ်ထားတဲ့ အလုပ် ဖြစ်ရပါမယ်။
ပိတ်လိုက်တာနဲ့ အရာအားလုံးကို မေ့သွားတဲ့ app တစ်ခုက IA အတွက် လုံလောက်တာ ရှားပါတယ်။ ဒီစာမျက်နှာမှာ user ကို file ရွေးခွင့်ပေးတာ၊ ရိုးရိုး စာသားကို save လုပ်တာနဲ့ ပြန် load လုပ်တာတွေကို ပြပြီး list တစ်ခုက text file နဲ့ မလောက်တော့တဲ့အခါ database (ဒေတာ သိမ်းဆည်းတဲ့ စနစ်) အသေး တစ်ခုဆီ အဆင့်မြှင့်ပုံကိုလည်း ပြပါတယ်။
ဒီ cookbook (recipe တွေကို စုထားတဲ့ လမ်းညွှန်) ကို ပထမဆုံး ဖတ်တာလား။ Start here: how every recipe fits together (ဒီမှာ စပါ: recipe တိုင်း ဘယ်လို ချိတ်ဆက်ထားလဲ) ကို အရင် ဖတ်ပါ။ အဲဒီ စာမျက်နှာက ကိုယ် ပြင်ရမယ့် နေရာ နှစ်ခုကို ရှင်းပြထားပြီး recipe (ချက်ချင်း ယူသုံးလို့ရတဲ့ code နမူနာ) တိုင်း အားကိုးရတဲ့ onAction (button နှိပ်ရင် run မယ့် method ကို သတ်မှတ်တဲ့ setting) ချိတ်ဆက်မှုကိုလည်း ရှင်းပြထားပါတယ်။
User ကို file ရွေးခွင့်ပေးခြင်း
ဒါက ဘာလုပ်ပေးလဲ: Operating system (ကွန်ပျူတာကို စီမံတဲ့ အဓိက software) ရဲ့ ကိုယ်ပိုင် file dialog (user ကို file ရွေးခိုင်းတဲ့ window အသေး) ကို ဖွင့်ပေးပါတယ်။ User က ဘယ် file ကနေ ဖတ်မလဲ ဒါမှမဟုတ် ဘယ်နေရာမှာ save လုပ်မလဲ ရွေးပြီးရင် ရွေးလိုက်တဲ့ File ကို ကိုယ့်ဆီ ပြန်ပေးပါတယ်။
IA မှာ ဘယ်အချိန် သုံးမလဲ: menu ထဲက Save As ဒါမှမဟုတ် Open ရွေးစရာ တစ်ခုအတွက် သုံးပါတယ်။ Client (product ကို သုံးမယ့်သူ) ရဲ့ ဒေတာ ဒါမှမဟုတ် report ကို မမြင်ရတဲ့ path (file ရှိရာ လမ်းကြောင်း) တစ်ခုတည်းမှာ ပုံသေ မထားဘဲ client ကိုယ်တိုင် ဘယ်မှာ သိမ်းမလဲ ရွေးခွင့်ပေးဖို့လည်း သုံးပါတယ်။
SceneBuilder မှာ (layout): SceneBuilder (FXML layout ကို ပုံနဲ့ မြင်ရင်း ပြင်တဲ့ editor) ထဲက layout (control တွေကို screen ပေါ်မှာ စီစဉ်ထားပုံ) မှာ dialog ကို ဖွင့်ပေးမယ့် button တစ်ခုကလွဲပြီး ဘာမှ ထည့်စရာ မလိုပါဘူး။ Button တစ်ခု ထည့်ပြီး သူ့ onAction ကို handleOpen လို့ သတ်မှတ်ပါ။ Dialog ကိုယ်တိုင်ကိုတော့ code ထဲမှာ ဖန်တီးပါတယ်။
<Button text="Open..." onAction="#handleOpen" />
Controller (user လုပ်တာတွေကို ကိုင်တွယ်ပြီး Model နဲ့ screen တွေကို ချိတ်ပေးတဲ့ class) မှာ (.java file):
Import (တခြားနေရာက code ကို ယူသုံးမယ်လို့ ကြေညာခြင်း) line များ:
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
File ဖွင့်တဲ့ dialog:
@FXML
private void handleOpen(ActionEvent event) {
FileChooser chooser = new FileChooser();
chooser.setTitle("Open a file");
chooser.getExtensionFilters().add(
new FileChooser.ExtensionFilter("Text files", "*.txt"));
Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
File file = chooser.showOpenDialog(stage);
if (file == null) {
return; // the user pressed Cancel
}
// your IA logic here: read from or write to the chosen file
loadFrom(file);
}
Save dialog အတွက်ဆိုရင် ခေါ်တဲ့ method (သီးခြား အလုပ်တစ်ခု လုပ်ပေးတဲ့ code အပိုင်း) တစ်ခုပဲ ပြောင်းပါတယ်:
File file = chooser.showSaveDialog(stage);
ကိုယ်ပိုင် logic (ဘာလုပ်မလဲ ဆုံးဖြတ်တဲ့ code) ထည့်ရမယ့် နေရာ: // your IA logic here (ကိုယ့် IA ရဲ့ logic ကို ဒီမှာ ထည့်ပါ) လို့ မှတ်ထားတဲ့ line မှာ ထည့်ပါ။ အဲဒီ line က user က file ကို ရွေးပြီးတာနဲ့ run ပါတယ်။ Dialog ဖွင့်တာနဲ့ Cancel ကို ကိုင်တွယ်တာကတော့ ပြန်သုံးလို့ရတဲ့ wiring (screen နဲ့ code ကို ချိတ်ပေးတဲ့၊ ဘယ် project မှာမဆို တူတဲ့ code) ပါ။
ကိုယ့် project နဲ့ ကိုက်အောင် ပြင်ပါ:
- Dialog ရဲ့ ခေါင်းစဉ်နဲ့ extension filter (ဘယ် file အမျိုးအစားတွေကိုပဲ ပြမလဲ စစ်ထုတ်ပေးတဲ့ setting) ကို ကိုယ့် ဒေတာနဲ့ ကိုက်အောင် ပြောင်းပါ။ ဥပမာ CSV (comma နဲ့ ခြားထားတဲ့ စာသား file) export (app ထဲက ဒေတာကို တခြား program တွေ သုံးလို့ရတဲ့ file အဖြစ် ထုတ်ပေးခြင်း) အတွက်ဆိုရင်
"*.csv"ကို သုံးပါ။ loadFrom(file)ဒါမှမဟုတ် save လုပ်တဲ့ method ခေါ်မှုကို ကိုယ်ပိုင် ဖတ်တဲ့ ဒါမှမဟုတ် ရေးတဲ့ code နဲ့ အစားထိုးပါ။
သတိထားရမယ့် အချက်များ:
onActionမှာ ပေးထားတဲ့ နာမည်နဲ့ method (handleOpen) က controller ထဲမှာ ရှိရပါမယ်၊ မရှိရင် screen က load မဖြစ်ပါဘူး။- User က Cancel နှိပ်ရင် dialog နှစ်ခုလုံးက
null(ဘာ object ကိုမှ မညွှန်းတဲ့ တန်ဖိုး) ကို return (ရလဒ်အဖြစ် ပြန်ပေး) လုပ်ပါတယ်။ ဒါကြောင့် file ကို မသုံးခင်nullဟုတ်မဟုတ် အမြဲ စစ်ပါ။ showSaveDialogက ရွေးလိုက်တဲ့ path ကို return လုပ်ပေမယ့် file ကို မဖန်တီးပေးပါဘူး။ File ထဲကို နောက် recipe နဲ့ ကိုယ်တိုင် ရေးရပါသေးတယ်။- Dialog က သူ့ကို ပိုင်တဲ့ window ကို သိဖို့ လိုပါတယ်။ အပေါ်မှာ ပြထားသလို event source (event ဖြစ်စေတဲ့ control၊ ဒီမှာ နှိပ်လိုက်တဲ့ button) ကနေ
Stage(window ကိုယ်တိုင်) ကို ယူတာက window ကို ပေးတဲ့ ပုံမှန် နည်းပါ။
တွဲသုံးလို့ရတဲ့ recipe များ: စာသားကို save လုပ်ပြီး ပြန် load လုပ်ခြင်း၊ ကိုယ့် ဒေတာကို CSV file အဖြစ် ထုတ်ပေးခြင်း၊ User ကို ပုံ ရွေးခိုင်းပြီး ပြခြင်း။
စာသားကို save လုပ်ပြီး ပြန် load လုပ်ခြင်း
ဒါက ဘာလုပ်ပေးလဲ: စာသားကို file ထဲ ရေးပြီး ပြန်ဖတ်ပါတယ်။ ရေးတာ၊ ဖတ်တာ တစ်ခုစီကို try/catch (error ဖြစ်ရင် ဖမ်းပြီး ကိုင်တွယ်ပေးတဲ့ ပုံစံ) ထဲ ထည့်ထားလို့ file မရှိရင် ဒါမှမဟုတ် ဖတ်လို့ မရရင်လည်း app က crash (program ရုတ်တရက် ပျက်ပြီး ရပ်သွားခြင်း) မဖြစ်ပါဘူး။
IA မှာ ဘယ်အချိန် သုံးမလဲ: app ကို ပိတ်ပြီး ပြန်ဖွင့်တဲ့ကြားမှာ app ရဲ့ လက်ရှိ အခြေအနေကို သိမ်းထားတာ၊ ရိုးရှင်းတဲ့ log (ဖြစ်ခဲ့တာတွေကို အစဉ်လိုက် မှတ်ထားတဲ့ မှတ်တမ်း) တစ်ခု ထားတာ ဒါမှမဟုတ် record (အချက်အလက် အများကြီး စုထားတဲ့ ဒေတာ တစ်ခု) စာရင်း တစ်ခုကို စာသား line တွေအဖြစ် သိမ်းတာတွေမှာ သုံးပါတယ်။ ဒါက B2.5 file processing ကျွမ်းကျင်မှုကို တကယ့် app တစ်ခုမှာ အသုံးချတာပါ။
SceneBuilder မှာ (layout): ဒီ method တွေကို ခေါ်ပေးမယ့် Save နဲ့ Load button တွေကလွဲပြီး ဘာမှ ထည့်စရာ မလိုပါဘူး။
Controller မှာ (.java file):
Import line များ:
import java.io.File;
import java.io.PrintWriter;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
File ထဲ ရေးခြင်း:
private void saveText(File file, String content) {
try (PrintWriter out = new PrintWriter(file)) {
out.println(content);
} catch (IOException e) {
// show the user that saving failed
}
}
File ကို ဖတ်ခြင်း:
private String loadText(File file) {
StringBuilder builder = new StringBuilder();
try (BufferedReader in = new BufferedReader(new FileReader(file))) {
String line;
while ((line = in.readLine()) != null) {
builder.append(line).append("\n");
}
} catch (IOException e) {
// show the user that loading failed
}
return builder.toString();
}
ကိုယ်ပိုင် logic ထည့်ရမယ့် နေရာ: ကိုယ့် ဒေတာကို save လုပ်မယ့် စာသားအဖြစ် ပြောင်းတာနဲ့ load လုပ်လိုက်တဲ့ စာသားကနေ ကိုယ့် object (class တစ်ခုကနေ ဖန်တီးထားတဲ့ instance) တွေကို ပြန်တည်ဆောက်တာက ကိုယ်ပိုင် logic ပါ:
// your IA logic here: build the text to save, or parse the text you loaded
ကိုယ့် project နဲ့ ကိုက်အောင် ပြင်ပါ:
- File ရွေးတဲ့ recipe ကနေ ရလာတဲ့
Fileကို ပေးပါ၊ ဒါမှမဟုတ် user မမြင်ရတဲ့ save file တစ်ခုအတွက် ပုံသေnew File("data.txt")ကို ပေးပါ။ - List တစ်ခုကို save လုပ်ဖို့ list ကို loop (ထပ်ခါထပ်ခါ run တဲ့ code အပိုင်း) နဲ့ ပတ်ပြီး line တစ်ကြောင်းအတွက်
out.println(...)ကို တစ်ကြိမ်စီ ခေါ်ပါ။ ပြန် load လုပ်ဖို့ line တစ်ကြောင်းချင်း ဖတ်ပြီး object တစ်ခုစီကို ပြန်တည်ဆောက်ပါ။
သတိထားရမယ့် အချက်များ:
- ဒီမှာ သုံးထားတဲ့
try (...)ပုံစံက file ကို ဖွင့်ပေးပြီး block ပြီးဆုံးတဲ့အခါ error ဖြစ်ခဲ့ရင်တောင် ကိုယ့်အစား ပိတ်ပေးပါတယ်။ File တွေကို အမြဲ ပိတ်ပါ။ ဒီပုံစံက သီးခြားclose()line မလိုဘဲ ပိတ်ပေးပါတယ်။ - File ရဲ့ အဆုံး ရောက်ရင်
readLine()ကnullကို return လုပ်ပါတယ်။whileloop က ဘယ်အချိန် ရပ်ရမလဲ ဆိုတာကို ဒီနည်းနဲ့ သိပါတယ်။ - မရှိတဲ့ file ကို ဖတ်ရင်
IOException(file ဖတ်တာ၊ ရေးတာ မအောင်မြင်တဲ့အခါ ဖြစ်တဲ့ exception) ကို throw (exception ဖြစ်ကြောင်း အချက်ပြ) လုပ်ပါတယ်။catchက အဲဒါကို ကိုင်တွယ်ပေးလို့ ဒေတာ အလွတ်နဲ့ စမလား၊ user ကို သတိပေးမလား ဆိုတာကို အဲဒီမှာ ဆုံးဖြတ်ပါ။
တွဲသုံးလို့ရတဲ့ recipe များ: User ကို file ရွေးခွင့်ပေးခြင်း၊ ကိုယ့်ဒေတာနဲ့ table ကို ဖြည့်ခြင်း၊ List က file နဲ့ မလောက်တော့ရင် database ကို ပြောင်းသုံးခြင်း။
List က file နဲ့ မလောက်တော့ရင် database ကို ပြောင်းသုံးခြင်း
ဒါက ဘာလုပ်ပေးလဲ: ကိုယ့် ဒေတာကို text file အစား SQLite (file တစ်ခုတည်းထဲမှာ သိမ်းတဲ့ database အသေး) database file တစ်ခုထဲမှာ ကိုယ် သိပြီးသား SQL (database ကို မေးမြန်း စီမံဖို့ သုံးတဲ့ language) ကို သုံးပြီး သိမ်းပါတယ်။ SQLite က disk ပေါ်က file တစ်ခုတည်း ဖြစ်ပြီး run ထားရမယ့် database server (database ကို စီမံပေးတဲ့ သီးခြား program) မလိုလို့ text file ကနေ အဆင့်မြှင့်ဖို့ အခက်အခဲ အနည်းဆုံး နည်းပါ။
IA မှာ ဘယ်အချိန် သုံးမလဲ: ဒေတာက ရိုးရိုး list တစ်ခုထက် ပိုလာတဲ့အခါ သုံးပါ: ဆက်စပ်နေတဲ့ table (database ထဲက ဇယား) အများ၊ စာသားပေါ်မှာ ကိုယ်တိုင် code ရေးလုပ်ရင် ခက်မယ့် ရှာတာနဲ့ sorting (အစဉ်တကျ စီခြင်း)၊ ဒါမှမဟုတ် ရှိတဲ့ နေရာမှာတင် update လုပ်ရမယ့် record တွေ။ Save လုပ်ဖို့ပဲ လိုတဲ့ ရိုးရိုး list တစ်ခုအတွက်ဆိုရင် အပေါ်က text file recipe က setup လုပ်စရာ နည်းပါတယ်။
ကြိုလုပ်ရမယ့် setup တစ်ခုတည်း: sqlite-jdbc driver (Java program နဲ့ database ကို ချိတ်ပေးတဲ့ library) ရဲ့ jar file (Java class တွေကို စုထုပ်ထားတဲ့ file) ကို ကိုယ့် BlueJ (Java code ရေးပြီး run ဖို့ ကျောင်းတွေမှာ သုံးတဲ့ software) project ရဲ့ library (အသင့်ရေးထားတဲ့ code အစု) တွေထဲ ထည့်ပါ။ BlueJ မှာ Tools၊ ပြီးရင် Preferences၊ ပြီးရင် Libraries ကို ဖွင့်ပြီး jar ကို ထည့်ပါ၊ ဒါမှမဟုတ် project ထဲက +libs folder ထဲကို ထည့်လိုက်ပါ။ ပုံမှန် JavaFX မှာ database driver မပါလို့ ဒီ jar က ဒီ cookbook တစ်ခုလုံးမှာ third-party dependency (Java နဲ့ JavaFX မှာ မပါဘဲ တခြားသူ ရေးထားတဲ့၊ project က မှီခိုရတဲ့ library) တစ်ခုတည်းပါ။
SceneBuilder မှာ (layout): ဒီ method တွေကို ခေါ်ပေးမယ့် button တွေ (Save button၊ Add button စသည်) ကလွဲပြီး ဘာမှ ထည့်စရာ မလိုပါဘူး။
Controller မှာ (.java file):
Import line များ:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.SQLException;
Database နဲ့ ချိတ်ပြီး table တစ်ခု ဖန်တီးခြင်း:
private void setUpDatabase() {
try (Connection conn = DriverManager.getConnection("jdbc:sqlite:app.db")) {
Statement stmt = conn.createStatement();
// your IA logic here: the table(s) your data needs
stmt.execute("CREATE TABLE IF NOT EXISTS item (name TEXT, quantity INTEGER)");
} catch (SQLException e) {
// show the user that the database could not be opened
}
}
PreparedStatement ကို သုံးပြီး user ထည့်ပေးတဲ့ input ကနေ row (table ထဲက အလျားလိုက် အတန်း) တစ်ခု ထည့်ခြင်း:
private void addItem(String name, int quantity) {
String sql = "INSERT INTO item (name, quantity) VALUES (?, ?)";
try (Connection conn = DriverManager.getConnection("jdbc:sqlite:app.db");
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, name); // fills the first ?
ps.setInt(2, quantity); // fills the second ?
ps.executeUpdate();
} catch (SQLException e) {
// handle the error
}
}
Row တွေကို ပြန်ဖတ်ခြင်း:
private void readItems() {
String sql = "SELECT name, quantity FROM item";
try (Connection conn = DriverManager.getConnection("jdbc:sqlite:app.db");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
String name = rs.getString("name");
int quantity = rs.getInt("quantity");
// your IA logic here: build an Item from name and quantity and use it
}
} catch (SQLException e) {
// handle the error
}
}
ကိုယ်ပိုင် logic ထည့်ရမယ့် နေရာ: table ဒီဇိုင်း၊ SQL နဲ့ row တစ်ခုစီကို ဘာလုပ်မလဲ ဆိုတာတွေပါ၊ အားလုံးကို // your IA logic here လို့ မှတ်ထားပါတယ်။ Database ချိတ်ဆက်မှု၊ statement object တွေနဲ့ try/catch ကတော့ ပြန်သုံးလို့ရတဲ့ wiring ပါ။
ကိုယ့် project နဲ့ ကိုက်အောင် ပြင်ပါ:
app.dbကို ကိုယ့် database file နာမည်နဲ့ ပြောင်းပါ။ ပထမဆုံး ချိတ်ဆက်တဲ့အခါ အဲဒီ file ကို အလိုအလျောက် ဖန်တီးပေးပါတယ်။- Table နာမည်၊ column (ဒေါင်လိုက် ကော်လံ) တွေနဲ့ SQL ကို ကိုယ့် ဒေတာနဲ့ ကိုက်အောင် ပြောင်းပါ။ SQL က ကိုယ် သိပြီးသား
CREATE၊INSERTနဲ့SELECTတွေပါပဲ။ while (rs.next())loop ထဲမှာ row တစ်ခုချင်းကို ဖတ်ပြီး ကိုယ့် model object (app ရဲ့ ဒေတာကို ကိုယ်စားပြုတဲ့ object) တစ်ခုအဖြစ် ပြောင်းပါ။
သတိထားရမယ့် အချက်များ:
- User ဆီက လာတဲ့ တန်ဖိုး မှန်သမျှအတွက် insert မှာ ပြထားသလို
?placeholder (နောက်မှ တန်ဖိုး ထည့်မယ့် နေရာ) တွေ ပါတဲ့PreparedStatementကို သုံးပါ။ ဒါက apostrophe (') လို ထူးဆန်းတဲ့ character (စာလုံး ဒါမှမဟုတ် သင်္ကေတ) တွေကြောင့် query (database ထဲက ဒေတာကို ရှာ၊ ပြင်ဖို့ ပေးတဲ့ အမိန့်) ပျက်မသွားအောင် ကာကွယ်ပေးပြီး စောစောကတည်းက ကျင့်ထားသင့်တဲ့ ပိုလုံခြုံတဲ့ အလေ့အထ ဖြစ်ပါတယ်။ - Database ကို ခေါ်တဲ့ code တိုင်းက
SQLException(database နဲ့ အလုပ်လုပ်ရင်း ဖြစ်တဲ့ exception) ကို throw လုပ်နိုင်လို့ အဲဒါတွေကိုtry/catchထဲမှာပဲ ထားပါ။ - Third-party jar လိုတဲ့ recipe က ဒါ တစ်ခုတည်းပါ။ ဒေတာက တကယ်ပဲ list တစ်ခုသာ ဖြစ်ရင် text file recipe က အဲဒီ setup ကို လုံးဝ မလိုအောင် လုပ်ပေးပါတယ်။
တွဲသုံးလို့ရတဲ့ recipe များ: စာသားကို save လုပ်ပြီး ပြန် load လုပ်ခြင်း၊ ကိုယ့်ဒေတာနဲ့ table ကို ဖြည့်ခြင်း၊ ကြာတဲ့ အလုပ် လုပ်နေစဉ် UI ကို တုံ့ပြန်နေအောင် ထားခြင်း (UI ဆိုတာ user interface၊ user နဲ့ program ကြားက မြင်ကွင်း)။