一、数据库驱动的概念、JDBC
数据库厂商提供的用来操作数据库用的jar包就是数据库驱动。各个厂商如果提供各自的数据库驱动的话会导致开发人员学习成本太高,所以sun公司提供了一套数据库驱动应该遵循的接口规范,这套规范就叫做JDBC,本质上是很多的接口。 由于所有的数据库驱动都遵循JDBC规范,我们在学习和使用数据库时只要学习JDBC中的接口就可以了。二、JDBC快速入门 *在数据库中建立好表 *在程序中导入数据库驱动包 1.注册数据库驱动 DriverManager.registerDriver(new Driver());//缺点一:观察mysqlDriver源码发现此方法导致了数据库驱动被注册了两次。缺点二:整个程序域mysql数据库驱动绑定增加了耦合性 Class.forName(“com.mysql.jdbc.Driver”); 2.获取连接 DriverManager.getConnection(url, user, password); ~url的写法: Oracle写法:jdbc:oracle:thin:@localhost:1521:sid SqlServer—jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sid MySql—jdbc:mysql://localhost:3306/sid ~url可以接的参数 user、password useUnicode=true&characterEncoding=UTF-83.获取传输器
createStatement():创建向数据库发送sql的statement对象。 prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。 4.利用传输器执行sql语句获取结果集 executeQuery(String sql) :用于向数据发送查询语句。 executeUpdate(String sql):用于向数据库发送insert、update或delete语句 execute(String sql):用于向数据库发送任意sql语句5.遍历结果集取出结构
ResultSet以表的样式在内存中保存了查询结果,其中还维护了一个游标,最开始的时候游标在第一行之前,每调用一次next()方法就试图下移一行,如果移动成功返回true; ResultSet还提供了很多个Get方法,用来获取查询结果中的不同类型的数据 除了next方法,还有以下方法可以用来遍历结果集: next():移动到下一行 Previous():移动到前一行 absolute(int row):移动到指定行 beforeFirst():移动resultSet的最前面。 afterLast() :移动到resultSet的最后面。 6.释放资源 conn是一个有限的资源,用完立即要释放表 stat占用内存,所以使用完后也要释放 rs占用内存,所以使用完后也要释放 释放时后创建的先释放 if(rs != null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } finally{ rs = null; } } if(stat != null){ try { stat.close(); } catch (SQLException e) { e.printStackTrace(); } finally{ stat = null; } } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } finally{ conn = null; } }
package com.dzq.jdbc;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import com.mysql.jdbc.Driver;public class JDBCDemo1 { public static void main(String []args) throws SQLException{ //1.注册数据驱动 DriverManager.registerDriver(new Driver()); //2.获取数据库 Connection conn= (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/database01", "root", ""); //3.获取传输器对象 Statement stat=conn.createStatement(); //4.利用传输器传输sql语句到数据库中执行,获取结果集对象 ResultSet rs=stat.executeQuery("select * from user"); //5.遍历结果集 while(rs.next()){ String name=rs.getString("name"); System.out.println(name); } //6.关闭资源 rs.close(); stat.close(); conn.close(); } }
改进后前后对比:
package com.dzq.jdbc;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class JDBCDemo1 { public static void main(String []args) throws Exception{ //1.注册数据驱动 //--由于mysql在Driver类的实现中自己注册了一次,而我们又注册了一次,于是会导致MySql驱动被注册两次 //--创建MySql的Driver对象时,导致了程序和具体的Mysql驱动绑死在了一起,在切换数据库时需要改动java代码 // DriverManager.registerDriver(new Driver()); Class.forName("com.mysql.jdbc.Driver"); //2.获取数据库 Connection conn= (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/database01", "root", ""); //3.获取传输器对象 Statement stat=conn.createStatement(); //4.利用传输器传输sql语句到数据库中执行,获取结果集对象 ResultSet rs=stat.executeQuery("select * from user"); //5.遍历结果集 while(rs.next()){ String name=rs.getString("name"); System.out.println(name); } //6.关闭资源 rs.close(); stat.close(); conn.close(); } }
再次改进,改善异常处理机制:
package com.dzq.jdbc;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class JDBCDemo1 { public static void main(String []args) { Connection conn=null; ResultSet rs=null; Statement stat=null; //1.注册数据驱动 //--由于mysql在Driver类的实现中自己注册了一次,而我们又注册了一次,于是会导致MySql驱动被注册两次 //--创建MySql的Driver对象时,导致了程序和具体的Mysql驱动绑死在了一起,在切换数据库时需要改动java代码 // DriverManager.registerDriver(new Driver()); try{ Class.forName("com.mysql.jdbc.Driver"); //2.获取数据库 conn= (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/database01", "root", ""); //3.获取传输器对象 stat=conn.createStatement(); //4.利用传输器传输sql语句到数据库中执行,获取结果集对象 rs=stat.executeQuery("select * from user"); //5.遍历结果集 while(rs.next()){ String name=rs.getString("name"); System.out.println(name); } }catch(Exception e){ e.printStackTrace(); }finally{ //6.关闭资源 try { if(rs!=null){ rs.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ rs=null; } try { if(stat!=null){ stat.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ stat=null; } try { if(conn!=null){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); }finally{ conn=null; } } } }
三、PreparedStatement 1.Sql注入:由于jdbc程序在执行的过程中sql语句在拼装时使用了由页面传入参数,如果用户恶意传入一些sql中的特殊关键字,会导致sql语句意义发生变化,这种攻击方式就叫做sql注入,参考用户注册登录案例。 2.PreparedStatement是Statement的孩子,不同的是,PreparedStatement使用预编译机制,在创建PreparedStatement对象时就需要将sql语句传入,传入的过程中参数要用?替代,这个过程回导致传入的sql被进行预编译,然后再调用PreparedStatement的setXXX将参数设置上去,由于sql语句已经经过了预编译,再传入特殊值也不会起作用了。 3.PreparedStatement使用了预编译机制,sql语句在执行的过程中效率比Statement要高。 四、大数据 1.mysql数据库也可以直至在数据库中保存大文本和大二进制数据, Text TINYTEXT(255)、TEXT(64k)、MEDIUMTEXT(16M)和LONGTEXT(4G) Blob TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB 2.JDBC去操作大文本: ~插入大文本: ps = conn.prepareStatement("insert into Demo2Text values(null,?,?)"); ps.setString(1, "钢铁是怎样练成"); File file = new File("1.txt"); ps.setCharacterStream(2, new FileReader(file), (int) file.length()); //1.Exception in thread "main" java.lang.AbstractMethodError: com.mysql.jdbc.PreparedStatement.setCharacterStream(ILjava/io/Reader;J)V //ps.setCharacterStream(2, new FileReader(file), file.length());第三个参数是long型的是从1.6才开始支持的,驱动里还没有开始支持。 //解决方案:ps.setCharacterStream(2, new FileReader(file), (int)file.length()); //2.Exception in thread "main" java.lang.OutOfMemoryError: Java heap space //文件大小过大,导致PreparedStatement中数据多大占用内存,内存溢出 //-Xms256M-Xmx256M //3.com.mysql.jdbc.PacketTooBigException: Packet for query is too large (10886466 > 1048576). You can change this value on the server by setting the max_allowed_packet' variable. //数据库连接传输用的包不够大,传输大文本时报此错误 //在my.ini中配置max_allowed_packet指定包的大小 ~查询大文本: Reader rd = rs.getCharacterStream("content"); 3.JDBC操作大二进制 ~插入: ps = conn.prepareStatement("insert into Demo3Blob values(null,?,?)"); ps.setString(1, "梦想的力量"); File file = new File("1.mp3"); ps.setBinaryStream(2, new FileInputStream(file), (int) file.length()); ~查询 InputStream in = rs.getBinaryStream("content");