1. 程式人生 > >Java Winform 開發 技術要點 記錄

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