Java Winform 開發 技術要點 記錄
很早之前寫了一個批量匯入excel資料到Oracle的程式,成批匯入,每批都有一個不同的上級編碼,每匯入一批,都要動手修改程式程式碼...
批量匯入過程中,這些excel的名字是需要和資料庫中存的名字保持一致,否則就找不到資料。
在這個程式裡面,這個名字就是村名,但是客戶給的資料名字都是口語中的,很早以前匯入資料前修改400以上的excel檔案,修改的頭疼。
為了解決上述的不方便之處,決定開發一個Winform介面來進行這些操作,儘可能簡化這些操作。
開發工具:Eclipse 4.2
使用外掛:WindowBuilder
資料庫連線:JDBC
Excel讀取:POI
經過慢慢修改最終形成的介面:
介面開發使用WindowBuilder開發,視覺化開發。
開發過程中的技術要點:
1.選擇檔案,點選選擇後開啟選擇檔案框,這裡限制可選檔案為資料夾。
選擇檔案使用:JFileChooser,上面三個按鈕使用了同一個事件,使用方法如下:
if (e.getSource().equals(button)&&!textField.getText().equals("")){ chooser = new JFileChooser(textField.getText()); } else { chooser = new JFileChooser(); } chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); FileNameExtensionFilter filter = new FileNameExtensionFilter( "Excel", "xls"); chooser.setFileFilter(filter); int returnVal = chooser.showOpenDialog(ImportExcel.this); if (returnVal == JFileChooser.APPROVE_OPTION) { if (e.getSource().equals(button)) { textField.setText(chooser.getSelectedFile().getAbsolutePath()); //讀入到table ReadInTable_1(null); } else if (e.getSource().equals(button_1)) { textField_1.setText(chooser.getSelectedFile().getAbsolutePath()); } if (e.getSource().equals(button_2)) { textField_2.setText(chooser.getSelectedFile().getAbsolutePath()); } }
chooser = new JFileChooser(textField.getText());
帶引數的是直接開啟預設目錄。
chooser = new JFileChooser();
不帶引數的開啟的可能是我的文件
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
這裡設定只能選擇資料夾型別,還有另外兩個引數,大家可以參考API
int returnVal = chooser.showOpenDialog(ImportExcel.this);
JFileChooserd的返回型別有三種:
/** * Return value if cancel is chosen. */ public static final int CANCEL_OPTION = 1; /** * Return value if approve (yes, ok) is chosen. */ public static final int APPROVE_OPTION = 0; /** * Return value if an error occured. */ public static final int ERROR_OPTION = -1;
chooser.getSelectedFile()通過這個方法獲取選擇的檔案。關於這個物件的更多說明,可以參考API。
2.JComboBox動態載入,使用K,V型別的資料。
動態載入如下面程式碼,獲取LIst物件,然後通過DefaultComboBoxModel進行動態新增,新增前使用comboBoxModel.removeAllElements();清空原有資料。
關於K,V型別的資料,接著看下面。
List<Af08> af08s = af08Dao.getAf08List(aaa027TextField.getText());
if(af08s.size()>0){
DefaultComboBoxModel comboBoxModel = (DefaultComboBoxModel)comboBox.getModel();
comboBoxModel.removeAllElements();
for(Af08 af08 :af08s){
comboBoxModel.addElement(af08);
}
}
else {
JOptionPane.showMessageDialog(ImportExcel.this, "該統籌區下沒有鄉鎮!");
return;
}
K,V型別的下拉框實現很簡單,上面的Af08定義如下:
public class Af08 {
private String aaf015;
private String aaf031;
public Af08(String aaf015,String aaf031) {
this.aaf015 = aaf015;
this.aaf031 = aaf031;
}
public String getAaf015() {
return aaf015;
}
public void setAaf015(String aaf015) {
this.aaf015 = aaf015;
}
public String getAaf031() {
return aaf031;
}
public void setAaf031(String aaf031) {
this.aaf031 = aaf031;
}
@Override
public String toString() {
return aaf031;
}
}
這裡面最關鍵的是重新了toString的方法,其實使用這個,不只是KV對應,你可以有更多的欄位,jcombobox顯示的是toString返回的物件,很簡單吧。
獲取這個選項的時候使用:
af08 = (Af08)comboBox.getSelectedItem();
是不是發現太簡單方便了。
3.JTable動態載入。
建立帶滾動條的JTable,下面這些程式碼是WindowBuilder自動生成的,根據這個樣式手寫也沒問題。
JScrollPane scrollPane_1 = new JScrollPane();
scrollPane_1.setBounds(173, 35, 300, 280);
panel.add(scrollPane_1);
table_1 = new JTable();
scrollPane_1.setViewportView(table_1);
table_1.setFillsViewportHeight(true);
table_1.setModel(new DefaultTableModel(
new Object[][] {
},
new String[] {
"\u5E8F\u53F7", "\u6587\u4EF6\u540D", "\u8DEF\u5F84", "\u8001\u6587\u4EF6\u540D"
}
) {
boolean[] columnEditables = new boolean[] {
false, true, false, false
};
public boolean isCellEditable(int row, int column) {
return columnEditables[column];
}
});
table_1.getColumnModel().getColumn(0).setPreferredWidth(29);
table_1.getColumnModel().getColumn(0).setMinWidth(20);
table_1.getColumnModel().getColumn(0).setMaxWidth(40);
table_1.getColumnModel().getColumn(1).setPreferredWidth(253);
table_1.getColumnModel().getColumn(2).setResizable(false);
table_1.getColumnModel().getColumn(2).setPreferredWidth(0);
table_1.getColumnModel().getColumn(2).setMinWidth(0);
table_1.getColumnModel().getColumn(2).setMaxWidth(0);
table_1.getColumnModel().getColumn(3).setPreferredWidth(0);
table_1.getColumnModel().getColumn(3).setMinWidth(0);
table_1.getColumnModel().getColumn(3).setMaxWidth(0);
這是建立了一個4列的table,其中隱藏列通過設定width都為0實現
下面是動態載入或修改資料的方法:
DefaultTableModel tableModel = (DefaultTableModel) table_1.getModel();
while (tableModel.getRowCount()>0) {
tableModel.removeRow(0);
}
for (int i = 0; i < files.length; i++) {
tableModel.addRow(
new Object[] {
i,
files[i].getName(),
files[i].getName(),
files[i].getAbsolutePath()
});
}
使用DefaultTableModel對JTable進行操作,新增資料addRow,刪除用removeRow(),修改用setValueAt很容易實現。
4.自動匹配檔名。
我使用一個很沒效率的方法來進行匹配,從右邊tabel去一個名字A,迴圈遍歷左邊的B,看A是否包含B,如果包含就存入map,如果不包含就將B去掉後面一個字串,在用A遍歷B看是否包含,依次類推。
使用map儲存,使用合適的Key可以保證不會出現重複的名字。
5.批量修改檔名。
使用流建立新檔案,將原始檔在寫到一個備份的資料夾,然後刪除原始檔。
程式碼如下:
for(int i=0;i<tableModel.getRowCount();i++){
if(!tableModel.getValueAt(i, 1).equals(tableModel.getValueAt(i, 2))){
count++;
filePath = tableModel.getValueAt(i, 3).toString();
filePath = filePath.substring(0, filePath.indexOf(tableModel.getValueAt(i, 2).toString()));
File file = new File(filePath+"/"+tableModel.getValueAt(i, 1));
try {
int byteread = 0;
File oldfile = new File(tableModel.getValueAt(i, 3).toString());
if (oldfile.exists()) {
InputStream inStream = new FileInputStream(oldfile);
FileOutputStream fs = new FileOutputStream(file);
byte[] buffer = new byte[1444];
while ((byteread = inStream.read(buffer)) != -1) {
fs.write(buffer, 0, byteread);
}
fs.close();
//inStream.close();
//將oldfile移動到bak資料夾
file = new File(filePath+"/bak/");
if(!file.exists()){
file.mkdirs();
}
file = new File(filePath+"/bak/"+tableModel.getValueAt(i, 2));
fs = new FileOutputStream(file);
while ((byteread = inStream.read(buffer)) != -1) {
fs.write(buffer, 0, byteread);
}
fs.close();
inStream.close();
//刪除oldfile
oldfile.delete();
btn7();
txtrexcelliuzh.append("\n");
txtrexcelliuzh.append(tableModel.getValueAt(i, 2)+" 修改為:"+tableModel.getValueAt(i, 1));
}
} catch (Exception e1) {
JOptionPane.showMessageDialog(ImportExcel.this, "修改檔案出錯:"+e1.getMessage());
}
}
}
6.查詢資料庫。
public static List<HashMap<String, Object>> execSql(String sql) throws Exception {
ResultSet resultSet = null;
ResultSetMetaData resultSetMetaData = null;
Connection conn = oracleJDBC.openCon();
PreparedStatement ps = conn.prepareStatement(sql);
resultSet = ps.executeQuery();
resultSetMetaData = resultSet.getMetaData();
int j = resultSetMetaData.getColumnCount();
List<HashMap<String, Object>> list = new ArrayList<HashMap<String, Object>>();
HashMap<String, Object> map = null;
String[] header = null;
while (resultSet.next()) {
map = new HashMap<String, Object>();
// 將標題行存入header陣列
if (header == null) {
header = new String[j];
for (int l = 0; l < j; ++l) {
header[l] = resultSetMetaData.getColumnName(l + 1)
.toLowerCase();
}
}
// 內容
for (int l = 1; l <= j; l++) {
map.put(header[l-1], resultSet.getString(l));
}
list.add(map);
}
return list;
}
這個方法用於簡單的讀取。
7.屬性檔案。
屬性檔案config.properties和匯出的JAR檔案放在同一個目錄下,在工程中也就是SRC目錄下,使用下面的方法獲取路徑在工程目錄執行或者單獨執行都可以。
public static String LOCATION;
static{
try {
String temp = URLDecoder.decode(PropUtil.class.getProtectionDomain().getCodeSource().getLocation().getFile(), "UTF-8");
LOCATION = temp.substring(1, temp.lastIndexOf('/'));
} catch (UnsupportedEncodingException e) {
LOCATION = "";
}
}
4個操作屬性的方法
/**
* @param args
* @throws Exception
*/
public static Properties getProperties(String filepath) throws Exception {
Properties prop = new Properties();
FileInputStream fis = new FileInputStream(LOCATION+"/"+filepath);
prop.load(fis);
return prop;
}
public static void SaveProperties(Properties prop,String filepath) throws Exception {
FileOutputStream fos = new FileOutputStream(LOCATION+"/"+filepath);
prop.store(fos, "@author Isea");
fos.close();
}
public static String getConfigValue(String key) {
try {
Properties properties = getProperties(Info.CONFIG);
} catch (Exception e) {
System.out.println(e.getMessage());
}
return "";
}
public static void setConfigValue(String key,String value){
try {
Properties properties = getProperties(Info.CONFIG);
properties.setProperty(key, value);
SaveProperties(properties, Info.CONFIG);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
前兩個對屬性檔案進行操作。後兩個對單個屬性進行操作。
8.其他。
用POI讀取excel在以前記錄過,如果有人需要了解,可以看我很早之前的部落格,或者留下郵箱,我發一個30多頁的POI基礎教程,非常好的,文字版的PDF。
讀取Excel後存資料庫使用的是儲存過程,
Connection conn = oracleJDBC.openCon();
String sql = "{ call PRC_XXXX(?,?,?,?,?,?,?,?,?,?,?,?,?) }";
CallableStatement proc = conn.prepareCall(sql);
Integer rows = 0, row = 0;
textArea.append("\n");
textArea.append("一共讀取" + pslist.size() + "條資料");
Iterator it = pslist.iterator();
int oldvalue = progressBar_1.getValue();
while (it.hasNext()) {
OldPersonDTO_dengji op = (OldPersonDTO_dengji) it.next();
proc.setString(1, op.getName()); // 姓名
proc.setString(2, op.getSfzh()); // 身份證號
//省略部分程式碼
proc.registerOutParameter(12, Types.INTEGER);
proc.registerOutParameter(13, Types.VARCHAR);
proc.execute();
int return_ret = proc.getInt(12);
if (return_ret < 0) {
textArea.append("\n"+"第" + row + "行資料儲存失敗!原因:"+ proc.getString(13));
}
row++;
if (return_ret >= 0) {
rows++;
}
ProcessBar.processbarshow(row, pslist.size(),progressBar);
progressBar_1.setValue(oldvalue+(int)Math.ceil((percent*row*100/pslist.size())));
}
用了大約1天半的工作時間(12小時左右)完成了上面的工作,然後用了大概40分鐘左右的時間,把幾百個Excel檔案匯入完成。
使用過程中的截圖(從截圖可以看出,自動匹配的成功率很高):
因為有錯誤資料,所以仍然會很方便,終於不用在手動操作Excel檔案了。
424385e6ab538f85bcbcd112a722c6ac