JDBC的进化3

这次进化完成后,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;
}

波比源码 – 精品源码模版分享 | www.bobi11.com
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
7. 本站源码并不保证全部能正常使用,仅供有技术基础的人学习研究,请谨慎下载
8. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!

波比源码 » JDBC的进化3

发表评论

Hi, 如果你对这款模板有疑问,可以跟我联系哦!

联系站长
赞助VIP 享更多特权,建议使用 QQ 登录
喜欢我嘛?喜欢就按“ctrl+D”收藏我吧!♡