1、JDBC简介
SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC。JDBC规范的作用如下:
2、第一个JDBC程序:编写一个程序,这个程序从users表中读取数据,并打印在命令行窗口中。Users表下载
注意程序注释中写的步骤:
public static void main(String[] args) throws SQLException { // 1、注册驱动(向 DriverManager注册给定驱动程序) DriverManager.registerDriver(new com.mysql.jdbc.Driver()); // 2、建立连接 Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/mydb1", "root", ""); // 3、创建Statement对象(用于向数据库发送SQL) Statement stmt = conn.createStatement(); // 4、执行SQL语句(查询) ResultSet rs = stmt.executeQuery("select * from users"); // 5、遍历结果集 while (rs.next()) { System.out.println(rs.getString("id") + "\t" + rs.getString("name") + "\t" + rs.getString("password") + "\t" + rs.getString("email") + "\t" + rs.getDate("birthday").toLocaleString()); } // 6、释放占用的资源 rs.close(); stmt.close(); conn.close(); }
3、相关API(☆)
在JDBC规范中定义了如下类和接口:
1)DriverManager类,该类作用如下:
① 注册驱动
方式一:DriverManager.registerDriver(new com.mysql.jdbc.Driver());
不建议使用,原因:
a、依赖具体驱动。在注册驱动的时候必须要传入Driver的实例,否则无法编译。(注:Driver接口在JDBC规范中定义,由各个数据库厂商提供实现。)
b、导致驱动注册2遍。在com.mysql.jdbc.Driver类的静态初始化块中已经调用了registerDriver()方法进行了注册。
方式二(建议):Class.forName(“com.mysql.jdbc.Driver”);
附:依赖(dependencies)分类:
编译时依赖(Compile Dependency):编译的时候必须要导入相应jar包,否则编译不通过,此种情况应尽量避免,做到与具体Jar包无关。
运行时依赖(Runtime Dependency):只有到了项目实际运行时才需要具体jar包。
测试时依赖(Test Dependency):测试时需要。
② 连接数据库,并返回Connection对象:public static Connection getConnection(String url,String user,String password)
数据库URL的组成(☆):
2)Connection
Connection对象代表与数据库的连接,所有的数据库操作都是基于连接之上的。常用方法:
createStatement():创建向数据库发送sql的statement对象。
prepareStatement():创建向数据库发送预编译sql的PrepareSatement对象。(占位符)
3)Statement
Jdbc程序中的Statement对象用于向数据库发送SQL语句,完成对数据库的CRUD操作。
常用方法:
ResultSet executeQuery(String sql):向数据库发送select查询语句。
int executeUpdate(String sql):发送insert、update、delete语句,返回值表示有几行受影响。
boolean execute(String sql):sql可以是任意的语句。返回值不是代表成功与否:如果是查询语句,就有结果集,返回true;没有返回结果集的,返回false。(一般用executeQuery和executeUpdate就够了)
4)ResultSet(☆)
Resultset封装了executeQuery()的结果,采用的类似于表格的方式。ResultSet 对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next() 方法,使游标指向具体的数据行,再调用getXxx()方法即可获取该行具体字段的数据。
ResultSet中的getXxx()方法和数据表中的字段类型有个对应关系,具体如下:
除了获取每行指定字段的getXxx方法,ResultSet中还提供如下移动游标的方法:
- next():移动到下一行
- previous():移动到前一行
- absolute(int row):移动到指定行
- beforeFirst():移动resultSet的最前面
- afterLast():移动到resultSet的最后面
5)PreparedStatement
PreperedStatement是Statement的子接口,它的实例对象可以通过调用Connection.preparedStatement()方法获得,相对于Statement对象而言:
① SQL 语句被预编译并存储在 PreparedStatement 对象中,然后可以使用此对象多次、高效地执行该语句(支持?占位符,参数化SQL语句)。
② PreperedStatement可以避免SQL注入。
提示:关于SQL预编译和注入的概念可以上网搜下。
4、释放资源(☆)
JDBC程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, Statement和Connection对象。特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。
为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中。
以下程序可作为一般的JDBC程序的模板(☆):
Connection conn = null; Statement stmt = null; ResultSet rs = null; try { conn = DriverManager.getConnection(……); stmt = conn.createStatement(); rs = stmt.executeQuery("……"); } catch (Exception e) { e.printStackTrace(); } finally{ if(rs!=null){//关闭rs try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } rs = null; } if(stmt!=null){//关闭stmt try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; } if(conn!=null){//关闭conn try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } }
根据上述模板可将JDBC程序共同的部分抽取成JDBC工具类(JdbcUtil.java),代码如下:
public class JDBCUtil { private static String driver; private static String url; private static String user; private static String password; static{ //driver、url、user、password从dbconn.properties中读取 ResourceBundle rb = ResourceBundle.getBundle("dbconn"); driver = rb.getString("driver"); url = rb.getString("url"); user = rb.getString("user"); password = rb.getString("password"); try { Class.forName(driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } //获取连接 public static Connection getConnection() throws ClassNotFoundException, SQLException{ Connection conn = DriverManager.getConnection(url,user,password); return conn; } //释放资源 public static void release(ResultSet rs,Statement stmt,Connection conn){ if (rs != null) {// 关闭rs try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } rs = null; } if (stmt != null) {// 关闭stmt try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; } if (conn != null) {// 关闭conn try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } conn = null; } } }
5、批处理
何时需要:当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。
1)多条不同的SQL语句(Statement.addBatch())
下面的代码模拟了向数据库发送两条添加和一条删除SQL语句:
@Test public void test1() throws Exception{ Connection conn = JDBCUtil.getConnection(); Statement stmt = conn.createStatement(); //添加三条SQL到 Statement对象的命令列表中 stmt.addBatch("insert into t1(id,name) values(1,'wang')"); stmt.addBatch("insert into t1(id,name) values(2,'xiang')"); stmt.addBatch("delete from t1 where id =1"); //执行批处理并打印返回结果 int[] ii = stmt.executeBatch(); for(int i : ii ){ System.out.println(i); } //释放资源 JDBCUtil.release(null, stmt, conn); }
在Statement实例内部有一个List(命令列表),执行addBatch(sql) 时就将sql语句加到List中了。
2)多条相同的SQL语句(PreparedStatement.addBatch())
下面的代码模拟了向数据库发送10条相同的sql语句(只是参数不同),此种情况最适合用PreparedStatement:
@Test public void test2()throws Exception{ Connection conn = JDBCUtil.getConnection(); PreparedStatement stmt = conn.prepareStatement("insert into t1(id,name) values(?,?)"); for(int i =1 ;i <=10;i ++){ stmt.setInt(1, i); stmt.setString(2, "haha"+i); stmt.addBatch(); } stmt.executeBatch(); //释放资源 JDBCUtil.release(null, stmt, conn); }
当需要发送大量SQL语句时一定要注意内存溢出问题,如下面的代码模拟了向数据库发送100,000条插入语句:
@Test public void test3()throws Exception{ Connection conn = JDBCUtil.getConnection(); PreparedStatement stmt = conn.prepareStatement("insert into t1(id,name) values(?,?)"); ResultSet rs = null; for(int i =1 ;i <=100000;i ++){ stmt.setInt(1, i); stmt.setString(2, "haha"+i); stmt.addBatch(); if(i%1000 ==0){ stmt.executeBatch(); stmt.clearBatch();//清理缓存 } } stmt.executeBatch(); JDBCUtil.release(rs, stmt, conn); }
- 本文固定链接: http://www.flyne.org/article/583
- 转载请注明: 东风化宇 2014年08月23日 于 Flyne 发表