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

isea533 CSDN认证博客专家 运维开发 系统架构
《MyBatis从入门到精通》作者,MyBatis分页插件PageHelper作者,通用Mapper作者,个人网站:https://mybatis.io
©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页