Java Winform 开发 技术要点 记录

来源:互联网 发布:java生成csv文件 编辑:程序博客网 时间:2024/06/05 09:07

很早之前写了一个批量导入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());//读入到tableReadInTable_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;}@Overridepublic 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();//删除oldfileoldfile.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

原创粉丝点击