程序员人生 网站导航

JDBC的进化3

栏目:php教程时间:2015-03-12 08:46:05

这次进化完成后,JDBC的进化就需要设置1个savepoint了,然后提交1下,提交到我们的脑袋硬盘中。
上1次说到了利用Statement对象来发送sql语句到数据库。但是这样的话,会暴露出两个问题。
那末问题来了!!!
问题1:
在履行executeUpdate(String sql)和executeQuery(String sql)方法时,我们需要拼写sql字符串,就像下面这样:

String sql = "insert into customers values(21, 'yushen1', 'yushem@123.com', '1998⑶⑵', null)";

String sql = "SELECT FlowId flowId, type, IDCard idCard, ExamCard examCard, StudentName studentName, location, Grade grade FROM examstudent WHERE ExamCard = '" + examCard + "'";
感遭到点甚么了没有???麻烦!!是否是,假设说我要修改20个字段,我是否是得拼好长1个字符串?这就是问题1。

问题2:
看下面的代码:

String user = "' OR 1 = 1--'"; SELECT * FROM user_table WHERE user = '"+user+"';

大家知道产生了甚么?我把整张表的信息都获得了,包括密码(你的银行账户密码被我知道了),太可怕了! 这就是SQL注入问题,也是我要说的第2个问题。
告知你个好消息,有1个1劳永逸的办法可以同时解决这两个问题,同时对批量处理效力会大大的提升,这个办法就是PreparedStatement接口。简直是好处多多啊。来我们来学习这个办法。还是老模样,从具体到通用。

Solution:
PreparedStatement接口:它是Statement接口的子接口。
看看API中对它的描写:

“An object that represents a precompiled SQL statement.”

“A SQL statement is precompiled and stored in a PreparedStatement object. This object can then be used to efficiently execute this statement multiple times. “

先看第1句:代表1个预编译的SQL命令对象
再看第2句:1个SQL命令是预编译的和存储在1个PreparedStatement对象中。这个对象随后能高效的履行这个命令屡次。
触及到1个词:预编译,我在下面结合代码来讲
先来1个具体的例子:

@Test public void testPrepareSelect(){ String sql = "SELECT * FROM users WHERE id = ?;"; // get connection Connection conn = null; // get PreparedStatement's object PreparedStatement ps = null; // execute the sql ResultSet rs = null; try { conn = JDBCUtils.getConnection(); ps = conn.prepareStatement(sql); // set the ? ps.setInt(1, 1); // execute the sql rs = ps.executeQuery(); // get the rs if(rs.next()){ int id = rs.getInt("id"); String name = rs.getString("name"); String address = rs.getString("address"); String phone = rs.getString("phone"); User user = new User(id, name, address, phone); System.out.println(user); } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.close(rs, ps, conn); } }

是否是发现个问号,然后给这个问号设置值?问:这个问号是甚么东东啊?答:这个问号相当于1个占位符,我就把这个位置占住了。要不为何叫预编译。
再来1个通用的:

/** * PreparedStatement: through the reflect and generic and PreparedStatement * @param sql * @param clazz * @param args * @return */ public <T> T getPrepareSelect(String sql,Class<T> clazz, Object ... args){ T t = null; // get the connection Connection conn = null; // get the PreparedStatement's object PreparedStatement ps = null; // execute the ps ResultSet rs = null; try { conn = JDBCUtils.getConnection(); ps = conn.prepareStatement(sql); // set values for ps for(int i = 0; i < args.length; i++){ ps.setObject(i+1, args[i]); } rs = ps.executeQuery(); // get the ResultSetMetaData's object ResultSetMetaData rsmd = rs.getMetaData(); // get the columnNum int columnNum = rsmd.getColumnCount(); // read the data of rs, and packaging an object if(rs.next()){ t = clazz.newInstance(); for(int i = 1; i <= columnNum; i++){ // get the columnName and columnValue of the special row special column String columnName = rsmd.getColumnLabel(i); Object columnValue = rs.getObject(columnName); // through the generic put the columnValue to the Class' field Field userField = clazz.getDeclaredField(columnName); userField.setAccessible(true); userField.set(t, columnValue); } } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.close(rs, ps, conn); } return t; }

我靠,你这写的甚么东西啊? 我就问你你还记得反射和泛型吗?
反射:
我们在编译时不能肯定创建甚么对象,只能在运行的时候创建,而运行时怎样创建呢?昨天还是前天的博客,我提到过1个思惟分散:描写数据的数据叫元数据,描写注解的注解叫元注解,描写类的类叫??? 汗,我也不知道该叫甚么了,但是的确存在1个类来描写1个已编译(已加载?我不肯定,回头看看)的类。而通过这个类我们可以取得它描写类的任何信息,包括创建对象和给属性设置值。我后面会总结1篇反射。现在该知道了吧?

由于要写成通用的,
1.我们不能肯定返回值是甚么类型的对象,我们使用了泛型。
2.对象的属性个数,甚么类型你一样不知道,我们使用反射,和多态参数来解决这个问题。

这里出现了个这东西:ResultSetMetaData 它是用来描写ResultSet的,我们知道ResultSet存的是1张数据表,而ResultSetMetaData就是用来描写这张表的,包括他有几列,每列是甚么。

现在读我这个程序是否是感觉好多了? 也不过如此么!!!

从具体到1般,我们上面写的仅仅是查询1条记录的。
来,再来1个查询多条记录的通用的方法:

/** * PreparedStatement : getAll * @param sql * @param clazz * @param args * @return */ public <T> List<T> getAll(String sql, Class<T> clazz, Object ... args){ List<T> list = new ArrayList<T>(); // get connection Connection conn = null; // get PreparedStatement PreparedStatement ps = null; // execute the sql ResultSet rs = null; try { conn = JDBCUtils.getConnection(); ps = conn.prepareStatement(sql); // set the ps for(int i = 0; i < args.length; i++){ ps.setObject(i+1, args[i]); } rs = ps.executeQuery(); // get the columnNum ResultSetMetaData rsmd = rs.getMetaData(); int columnNum = rsmd.getColumnCount(); // read the rs and write to an object while(rs.next()){ T t = clazz.newInstance(); for(int i = 1; i <= columnNum; i++){ // read String columnName = rsmd.getColumnLabel(i); Object columnVal = rs.getObject(columnName); // write // through the field(reflect) //Field field = clazz.getDeclaredField(columnName); //field.setAccessible(true); //field.set(t, columnVal); // through the method(reflect) PropertyUtils.setProperty(t, columnName, columnVal); } list.add(t); } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.close(rs, ps, conn); } return list; }

来了,相比于上1个来讲,这个其实很简单了,只不过是增加了1个List<T>集合来寄存对象,获得连接和关闭的话,你看看我的进化1就明白了。还有就是使用了Apache提供的工具类,但是我个人还是希望你能自己写写,以后实际项目使用的时候再用工具类会好1点。

这里来讲预编译
预编译指令唆使了在程序正式编译前就由编译器进行的操作,我认为这个编译固然不是Java中的编译了,而是将它放到了数据库中,在数据库中进行预编译,你仔细想一想是否是这样?这个该说说内存了,数据库在内存中会有库池,->数据缓冲区,->日志缓冲区,1条简单的Select语句发送到数据库要经历1个硬解析的进程,硬解析->检查->履行计划,然后到库池,再到数据库缓冲池。对普通的Statement语句发送的Sql语句,它每次都要履行这个进程。而PreparedStatement则不同,它被编译过的语句会被缓存下来,下次调用有相同的预编译语句就不会重新进行编译(即上面那个进程),将参数传入就会履行。

通过上面1段话:有产生了1个新的东西:
批量处理:
3个方法:addBatch()1个装载的进程,executeBatch()履行的进程,clearBatch()清空缓冲的数据。
具体的履行进程和上面的类似,我会在效力的比较中给出具体的例子。

这些说完了,我们来测试测试他和Statement的效力,不然你们还以为我骗你们,说PreparedStatement效力高。
我同时向数据库中插入100000条记录为例
statement:

@Test public void testStatement() {// 260111 long start = System.currentTimeMillis(); Connection conn = null; Statement statm = null; try { conn = JDBCUtils.getConnection(); statm = conn.createStatement(); for (int i = 0; i < 100000; i++) { String sql = "insert into emp1 values(" + i + ", 'emp" + i + "')"; statm.executeUpdate(sql); } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.close(statm, conn); } long end = System.currentTimeMillis(); System.out.println("the time is :" + (end - start)); }

履行时间:
260111ms
PreparedStatement:

@Test public void testPreparedStatment() {// 141991 long start = System.currentTimeMillis(); // get connection Connection conn = null; // get PreparedStatement's object PreparedStatement ps = null; try { conn = JDBCUtils.getConnection(); String sql = "insert into emp1 values(?, ?)"; ps = conn.prepareStatement(sql); for (int i = 0; i < 100000; i++) { ps.setInt(1, i + 1); ps.setString(2, "emp" + i); ps.executeUpdate(); } } catch (SQLException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.close(null, ps, conn); } long end = System.currentTimeMillis(); System.out.println("the time is :" + (end - start)); }

141991ms 快了接近1倍
再看看批量处理:
Statement:

@Test public void testStatement2() {// 271924 long start = System.currentTimeMillis(); Connection conn = null; Statement statm = null; try { conn = JDBCUtils.getConnection(); statm = conn.createStatement(); for (int i = 0; i < 100000; i++) { String sql = "insert into emp1 values(" + i + ", 'emp" + i + "')"; statm.addBatch(sql); if ((i % 250) == 0) { statm.executeBatch(); statm.clearBatch(); } } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.close(statm, conn); } long end = System.currentTimeMillis(); System.out.println("the time is :" + (end - start)); }

271924ms 好慢

PreparedStatement:
接下来就是见证奇迹的时刻!!!

@Test public void testPreparedStatement2() {// 3230 long start = System.currentTimeMillis(); // get connection Connection conn = null; // get PreparedStatement's object PreparedStatement ps = null; try { conn = JDBCUtils.getConnection(); String sql = "insert into emp1 values(?, ?)"; ps = conn.prepareStatement(sql); for (int i = 0; i < 100000; i++) { ps.setInt(1, i + 1); ps.setString(2, "emp" + i); ps.addBatch(); if ((i % 250) == 0) { ps.executeBatch(); ps.clearBatch(); } } } catch (SQLException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.close(null, ps, conn); } long end = System.currentTimeMillis(); System.out.println("the time is :" + (end - start)); }

3230ms 甚么???这么快!!!
我就甚么也不多说了,继续吧!!

我们接下来讲事务,我还是分开写吧,这个太长了,不好浏览。没耐心的观众已坐不住了!!

补充1点:大数据处理,这里只提供代码示例,可以参考着去研究研究
大数据的读取:

@Test public void get(){ String sql = "select * from customers where id = ?"; Connection conn = null; PreparedStatement ps = null; InputStream is = null; ResultSet rs = null; OutputStream os = null; try { // get connection conn = JDBCUtils.getConnection(); // get PreparedStatement's object ps = conn.prepareStatement(sql); ps.setInt(1, 22); // execute the ps rs = ps.executeQuery(); while(rs.next()){ int id = rs.getInt(1); String name = rs.getString(2); String email = rs.getString(3); Date birth = rs.getDate(4); Customer customer = new Customer(id, name, email, birth); System.out.println(customer); Blob blob = rs.getBlob(5); is = blob.getBinaryStream(); os = new FileOutputStream("1.jpg"); byte[] b = new byte[1024]; int len = 0; while((len = is.read(b)) != -1){ os.write(b, 0, len); } } } catch (Exception e) { e.printStackTrace(); } finally { if(os != null){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if(is != null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } JDBCUtils.close(rs, ps, conn); } }

大数据的写入:

public int insertBlob() { String sql = "insert into customers values(?, ?, ?, ?, ?)"; // get connection Connection conn = null; // get PreparedStatement's object PreparedStatement ps = null; // execute ps int rows = 0; try { conn = JDBCUtils.getConnection(); ps = conn.prepareStatement(sql); ps.setInt(1, 22); ps.setString(2, "lisi"); ps.setString(3, "lisi@abc.com"); ps.setDate(4,new Date(new java.util.Date().getTime())); ps.setBlob(5, new FileInputStream("089.jpg")); rows = ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtils.close(ps, conn); } return rows; }
------分隔线----------------------------
------分隔线----------------------------

最新技术推荐