Java 核心技术 卷II (4章)--数据库编程

来源:互联网 发布:小米6怎么设置4g网络 编辑:程序博客网 时间:2024/05/21 02:20

Java 核心技术 卷II  (4章)

---数据库编程

 

1、jdbc设计

1)Jdbc是java能够通过SQL访问数据库的一套javaAPI;

2)Jdbc是一套能与多种关系数据库提供统一访Java接口方法;

 

2、核心API方法介绍

创建执行对象:

Stattement stat = conn.createStatement();

stat.executeUpdate():返回受SQL命令影响的行数,或者对不返回行数的语句(DDL语句)返回0

ResultSet rs = stat.executeQuery(String SQL)

结果集使用:

while(rs.next()){ . . . . . .}

如何取值?不同的参数类型有不同的访问器,可以通过下标和类名获取

rs.getDouble (1);rs.getDouble(“price”)

说明:

1)数据库索引与数组的索引不同,数据库的列序号从1开始计算的。

2)使用get方法的类型与列的数据类型不一致,get方法会尝试合理的类型转换,如rs.getString(“price”),会将price列的浮点值转换为字符串。

3)做通用话数据封装时,可以使用getObject进行通用取。

如何管理连接??

使用完ResultSet、Statement或Connection对象,应立即使用close方法关闭,调用顺序从里到外(ResultSet、Statement、Connection)

如何解析SQL异常

SQLWarning w = stat.getWarning();while(w != null){            do something with w            w = w.nextWarning();}

说明:还有其他的很多方法,见书!!

 

相关其他方法:

Connection :            Statement createStatement()             voidcolse()Statement            ResultSetexecuteQuery(String sql)            intexecuteUpdate(String sql)            booleanexecute(String sql)            ResultSetgetResultSet()            intgetUpdateCount()            voidclose()            booleanisColsed()            void coseOncompleion()ResultSet            booleannext()            XxxgetXxx(int columnNumber)            XxxgetXxx(String columnName)            <T> TgetObject(int columnNumber,Class<T> type)            <T> T getObject(intcolumnName,Class<T> type)            int findColumn(String columnName)            void close()             boolean isClosed();

3、不同的执行对象

3.1预备语句(预编译语句)

如何进行参数化查询??

PreparedStatement stat = conn.prepareStatement(“select *from user where id = ?”);stat.serInt(1,12);ResultSet rs = stat.executeQuery();

        许多数据库通常都会有自动缓存预备语句,如果相同的查询被预备两次,数据库通常会直接重用查询策略。

不使用Statement:

1)使用Statement,若是进行参数化查询,每次查询都会建立新的查询语句,消耗查询性能;

2)使用Statement,如果进行参数化查询,需要拼接SQL,容易造成SQL注入。

使用statement:

如果进行的查询是不带参数的,使用Statement比PrepredStatement更高校,

 

相关其他方法:

Connection            PreparedStatementprepareStatement(String sql)PreparedStatement            voidsetXxx(int n, Xxx x);            voidclearParameters();            ResultSetexecuteQuery()            intexecuteUpdate()                 【如果执行的是数据定义语句(DDL),如CREATE TABLE,则返回0】

3.2 CallableStatement执行存储过程

CALL pro_findById2(5,@NAME);

(1)存储过程

mysql> DELIMITER $mysql> CREATE PROCEDURE pro_findById2(IN eidINT,OUT vname VARCHAR(20))-> BEGIN->      SELECT empname INTO vname FROM employeeWHERE id = eid;-> END $;

(2)测试

String sql = "CALL pro_findById2(?,?)"; //第一个?是输入参数,第二个?是输出参数cstmt =conn.prepareCall(sql);//设置输入参数cstmt.setInt(1, 6);//设置输出参数(注册输出参数)cstmt.registerOutParameter(2,java.sql.Types.VARCHAR);//返回结果到输出参数中cstmt.executeQuery();//从输出参数的索引中,获取结果String result = cstmt.getString(2);            

4、其他

4.1读写Lob

Lob分两种:一种字符型大对象Clob,二进制大对象Blob

Blob:
存入数据库:

String sql = " insert into test(img)values(?)";// 连接con = JdbcUtil.getConnection();// pstmt 对象pstmt = con.prepareStatement(sql);// 获取图片流InputStream in = App_text.class.getResourceAsStream("7.jpg");pstmt.setBinaryStream(1, in);            // 执行保存图片pstmt.execute(); 

从数据库读取:

String sql = "select img from  test where id=2;";// 连接con = JdbcUtil.getConnection();pstmt = con.prepareStatement(sql);rs = pstmt.executeQuery();if (rs.next()) {      // 获取图片流      InputStream in =rs.getBinaryStream("img");      // 图片输出流      FileOutputStream out = new FileOutputStream(new File("c://1.jpg"));      int len = -1;      byte b[] = new byte[1024];      while ((len = in.read(b)) != -1) {            out.write(b, 0,len);      }}

Clob:

存入数据库:

String sql = "insert into test(content)values(?)";con = JdbcUtil.getConnection();pstmt = con.prepareStatement(sql);// 设置参数// 先获取文件路径String path = App_text.class.getResource("tips.txt").getPath();FileReader reader = new FileReader(new File(path));pstmt.setCharacterStream(1,reader);// 执行sqlpstmt.executeUpdate();

从数据库读取:

String sql = "select * from  test;";con = JdbcUtil.getConnection();pstmt = con.prepareStatement(sql);rs = pstmt.executeQuery();if (rs.next()) {      // 获取长文本数据,方式1:      //Reader r = rs.getCharacterStream("content");                             // 获取长文本数据,方式2:      System.out.print(rs.getString("content"));} 

4.2获取主键

String sql = "insert into articlevalues(null,?,now(),?)";//执行语句的同时返回一个主键集合PreparedStatement pst =DB.prepareStmt(conn, sql,Statement.RETURN_GENERATED_KEYS);pst.setInt(1,0);pst.setInt(2, 0);pst.executeUpdate();      //得到主键集合ResultSet rsKey =pst.getGeneratedKeys();rsKey.next();rootId = rsKey.getInt(1);

4.3多结果集

若是执行存储过程或者在单个查询中提交了多个select语句,那么一个数据库能返回多个结果集。

 

关键点:重复调用getMoreResult方法移动到下一项结果集。

CREATE PROCEDURE proc_test()BEGINselect * from person;select * from person;END;

Connection conn = getConn();String sql = "{call proc_test()}";   CallableStatement ctmt = conn.prepareCall(sql);boolean hadResults = ctmt.execute();   int i=0;   ResultSet rs = null; while (hadResults) {       System.out.println("result No:----"+(++i));       rs = ctmt.getResultSet();       while (rs !=null && rs.next()){           int id1 = rs.getInt(1);           String name1 = rs.getString(2);           System.out.println(id1 +":" + name1);       }       hadResults = ctmt.getMoreResults(); //检查是否存在更多结果集   } 

4.4可滚动&可更新结果集

可滚动结果集:

Connection conn = getConn();/* *conn.createStatement(type,concurrency) */// TYPE_SCROLL_SENSITIVE:结果集能滚动,对数据库变化敏感// CONCUR_UPDATABLE:结果集可以用于更新数据库Statement stat = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);ResultSet rs = stat.executeQuery("select * from user");// 移动游标方法rs.next();rs.relative(3);rs.previous();rs.relative(-1);rs.absolute(5); // 特殊位置rs.first();rs.last();rs.beforeFirst();rs.afterLast();

ps:因为在操作过程中,一直连接着数据库,可滚动结果集,消耗数据库资源巨大。

 

可更新结果集:

Connection conn = getConn();// TYPE_SCROLL_SENSITIVE:结果集能滚动,对数据库变化敏感// CONCUR_UPDATABLE:结果集可以用于更新数据库Statement stat = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);ResultSet rs = stat.executeQuery("select * from user");rs.next();rs.updateString("name","u-2");rs.updateInt("age", 22);rs.updateRow();  rs.insertRow();rs.deleteRow();

ps:

①更新数据库行,必须要updateRow(),这样可以将变化同步到数据库;插入数据库一行,使用updateXxx,之后要使用insertRow(),rs.moveToCurrentRow();deleteRow()直接将当前行删除。

②如果查询语句涉及到多个表的连接操作,那么它所产生的结果集将是不可更新的;若是利用主键连接表的,那么它还是可更新结果集。

4.5行集

         RowSet接口扩展自ResultSet接口,却无需始终保持与数据库连接,可以完美解决可滚动结果集的,数据库资源消耗问题。

行集分几种:

CachedRowSet:缓存的行集。

WebRowSet:缓存的行集,可保存为XML文件,该文件可移动到Web应用。

FilteredRowSet和JoinRowSet:支持对行集的轻量级操作。

JdbcRowSet:是ResultSet接口的瘦包装器。

 

行集中的数据来源??

使用JDBC驱动程序从数据库检索的数据

从其他数据源获得的数据,如文件数据

 

行集(Row  Set)的优点??

1)可以断开数据库连接操作数据;

2)可以在分布式系统中的不同组件之间传递;

3)默认可更新,可滚动,可序列化,可以方便的在网络间传输;

4)可以方便的使数据在行集与JavaBean对象之间进行转换。行集中的一行数据可以封装为一个JavaBean对象。

Connection conn = getConn();PreparedStatement pst=conn.prepareStatement("select * from user"); //必须设置非自动提交 conn.setAutoCommit(false); ResultSet rs=pst.executeQuery(); //创建行集实例 CachedRowSetImpl rowset=newCachedRowSetImpl(); //填充 rowset.populate(rs);  //这里已经关闭结果集了!!!rs.close(); pst.close();  // 在关闭连接之前进行更新操作rowset.absolute(5); rowset.updateInt("English", 55); rowset.updateRow();  //更新 rowset.acceptChanges(conn); //提交  //输出结果集之前,关闭连接 conn.close();  //输出行集数据 while(rowset.next()){     System.out.print(rowset.getInt("id")+"\t");     System.out.print(rowset.getInt("Chinese")+"\t");     System.out.print(rowset.getInt("English")+"\t");     System.out.println(rowset.getInt("history")); }    

5、元数据

元数据:描述数据库或者其组成部分的数据。

 

元数据分为:

关于数据库的元数据(DatabaseMetaData)

关于结果集的元数据(ResultSetMetaData)

关于预备语句参数的元数据(ParameterMetaData)

 

数据库元数据

// 获取连接Connection conn = JdbcUtil.getConnection();// 获取数据库元数据DatabaseMetaData metaData = conn.getMetaData();//alt + shift + L 快速获取方法返回值     System.out.println(metaData.getUserName());System.out.println(metaData.getURL());System.out.println(metaData.getDatabaseProductName());

参数元数据

// 获取连接Connection conn = JdbcUtil.getConnection();// SQLString sql = "select * from dept wheredeptid=? and deptName=?";// Object[] values = {"tom","888"};           PreparedStatement pstmt = conn.prepareStatement(sql);// 参数元数据ParameterMetaData p_metaDate = pstmt.getParameterMetaData();// 获取参数的个数int count = p_metaDate.getParameterCount();// 测试System.out.println(count); 

结果集元数据

Connection conn = JdbcUtil.getConnection();PreparedStatement pstmt = conn.prepareStatement("select * from dept ");ResultSet rs = pstmt.executeQuery();// 得到结果集元数据(目标:通过结果集元数据,得到列的名称)ResultSetMetaData rs_metaData = rs.getMetaData();          // 迭代每一行结果while (rs.next()) {      // 1. 获取列的个数      int count = rs_metaData.getColumnCount();      // 2. 遍历,获取每一列的列的名称      for (int i=0; i<count; i++) {            // 得到列的名称            String columnName= rs_metaData.getColumnName(i + 1);            // 获取每一行的每一列的值            ObjectcolumnValue = rs.getObject(columnName);            // 测试            System.out.print(columnName +"=" + columnValue + ",");      }      System.out.println();}

说明:getObject可以拿到任意类型行的值

6、事务

            事务:将一组语句构建成一个事务,当所有语句都顺利执行,事务可以被提交。否则,如果其中某个语句遇到错误,那么事务将被回滚。

            事务:确保数据库万整性。

 

默认情况下,数据库连接处于自动提交模式。

Connection con;PreparedStatement pstmt;String sql_zs = "UPDATE account SETmoney=money-1000 WHERE accountName='张三';";String sql_ls = "UPDATE1 account SETmoney=money+1000 WHERE accountName='李四';";try {      con = JdbcUtil.getConnection();// 默认开启的隐士事务      // 一、设置事务为手动提交      con.setAutoCommit(false);       /*** 第一次执行SQL ***/      pstmt = con.prepareStatement(sql_zs);      pstmt.executeUpdate();       /*** 第二次执行SQL ***/      pstmt = con.prepareStatement(sql_ls);      pstmt.executeUpdate();} catch(Exception e) {      try {            // 二、出现异常,需要回滚事务            con.rollback();      } catch (SQLException e1) {            e.printStackTrace();}} finally {      try {            // 三、所有的操作执行成功,提交事务            con.commit();            JdbcUtil.closeAll(con,pstmt, null);      } catch (SQLException e) {      }}


保存点:使用保存点可以更细粒度的控制回滚操作

. . .try {      con = JdbcUtil.getConnection();// 默认开启的隐士事务      // 一、设置事务为手动提交      con.setAutoCommit(false);       /*** 第一次执行SQL ***/      pstmt = con.prepareStatement(sql_zs);      pstmt.executeUpdate();       Savepoint svpt = pstmt.setSavepoint();       /*** 第二次执行SQL ***/      pstmt = con.prepareStatement(sql_ls);      pstmt.executeUpdate();} catch(Exception e) {      // 二、出现异常,需要回滚到回滚点      con.rollback(svpt);      . . .} finally {      . . .}

Ps:当不需要回滚点时,可以释放回滚点:使用conn.releaseSavepoint(svpt)。

7、批量操作

         当程序需要执行多条语句时,可以使用批量操作,如执行上万条insert语句,可以使用批量更新来提高程序性能。

// SQLString sql = "INSERT INTOadmin(userName,pwd) values(?,?)";try {      // 获取连接      con = JdbcUtil.getConnection();      // 创建stmt      pstmt = con.prepareStatement(sql);             // 【预编译SQL语句】      for (int i=0; i<list.size(); i++) {            Admin admin =list.get(i);            // 设置参数            pstmt.setString(1,admin.getUserName());            pstmt.setString(2, admin.getPwd());                        // 添加批处理            pstmt.addBatch();                              // 【不需要传入SQL】                                   // 测试:每5条执行一次批处理            if (i % 5 == 0) {                  // 批量执行                  pstmt.executeBatch();                  // 清空批处理                  pstmt.clearBatch();            }      }                       // 批量执行      pstmt.executeBatch();      // 清空批处理      pstmt.clearBatch();} catch(Exception e) {      e.printStackTrace();} finally {      JdbcUtil.closeAll(con, pstmt, rs);}

原创粉丝点击