最新公告
  • 欢迎您光临波比源码,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入我们
  • 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. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!

    波比源码 » JDBC的进化3

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    波比源码
    一个高级程序员模板开发平台
    升级波友尊享更多特权立即升级