本文共 5966 字,大约阅读时间需要 19 分钟。
简单的jdbc编程示例:
public class DBTest { public static void runTest(){ Connection conn = null; Statement stat = null; try{ conn = getConn(); stat = conn.createStatement(); ResultSet rs = stat.executeQuery("select 1 from dual"); if(rs.next()){ System.out.println("数据库连接成功!"); } }catch(Exception e){ e.printStackTrace(); }finally{ //关闭连接 try { if(stat!= null && !stat.isClosed()){ stat.close(); }if(conn!= null && !conn.isClosed()){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } public static Connection getConn() throws IOException,SQLException, ClassNotFoundException { Properties pro = new Properties(); //获取db连接文件输入流 InputStream in = Files.newInputStream(Paths.get("properties", "db.properties")); pro.load(in); String driverName = pro.getProperty("driverName"); //手动加载数据库驱动类 Class.forName(driverName); String url = pro.getProperty("url"); String username = pro.getProperty("username"); String password = pro.getProperty("password"); //获得数据库连接 return DriverManager.getConnection(url, username, password); } public static void main(String[] args) throws IOException, SQLException { runTest(); }}
除了数字、字符串和日期以外,许多数据库还可以存储大对象,例如图片或其他数据。在SQL中,二进制大对象称为BLOB,字符型大对象称为CLOB。
从数据库中获取一张图片:
ResultSet rs = stat.executeQuery();if(rs.next()){Blob coverBlob = rs.getBlob(1);Image coverImage = ImageIO.read(coverBlob.getBinaryStream()); }
存储一张图片:
//创建Blob对象Blob coverBlob = connection.createBlob();int offset = 0;//获取输出流OutputStream out = coverBlob.setBinaryStream(offset);将Image写入到输出流中ImageIO.write( (RenderedImage) coverImage, "PNG", out);stat = connection.prepareStatement("INSERT INTO Cover VALUES(?)");tat.setBlob(1, coverBlob);
“转义”语法是各种数据库普遍支持的特性,但是数据库使用的是与数据库相关的语法变体,因此,将转义语法转译为特定数据库的语法是JDBC驱动程序的任务之一。
转译日期:{d 'yyyy-mm-dd'}
、{t 'hh:mm:ss'}
、{ts 'yyyy-mm-dd hh:mm:ss'}
String sql="INSERT INTO STUDENTS VALUES" + "(1,'tjy', {d '2001-12-16'})";
按照数据库的不同,将日期转换为数据库的日期格式。
大多数数据库都支持某种在数据库中对行自动编号的机制。这些编号通常作用于主键,尽管JDBC没有提供生成主键的机制,但是,当一条数据插入以后,它可以获取该条数据自动生成的主键。
stat.executeUpdate(insertStatement,Statement.RETURN_GENERATED_KEYS);ResultSet rs = stat.getGeneratedKeys();
前面已经介绍,使用ResultSet接口中的next方法可以迭代遍历结果集中的所有行,对于一个只需要分析数据的程序来说,这已经够了,但是,如果用于展示一张表或查询结果的可视化数据显示,我们通常会希望用户可以在结果集上前后移动。对于可滚动结果集而言,我们可以在其中向前或向后移动,甚至可以跳到任意位置。
默认情况下,结果集是不可滚动和不可更新的。为了从查询中获取可滚动的结果集,必须使用下面的方法得到一个不同的Statement对象:
Statement stat = conn.createStatement(type,concurrency);
如果要获得预备语句,请调用下面的方法:
PrepareStatement stat = conn.prepareStatement(command,type,concurrency);
下表展示了type、concurrency的所有可能取值
ResultSet类的type值:
值 | 解释 |
---|---|
TYPE_FORWARD_ONLY | 结果集不能滚动(默认值) |
TYPE_SCROLL_INSENSITIVE | 结果集可以滚动,但对数据库变化不敏感 |
TYPE_SCROLL_SENSITIVE | 结果集可以滚动,且对数据库变化敏感 |
ResultSet类Concurrency值:
值 | 解释 |
---|---|
CONCUR_READ_ONLY | 结果集不能用于更新数据库(默认) |
CONCUR_UPDATABLE | 结果集可以用于更新数据库 |
例如:如果只想滚动遍历结果集,而不想编辑它的数据,那么可以使用以下语句
Statement stat = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);//通过调用以下方法获得的所有结果集都将是可滚动的ResultSet rs = stat.executeQuery(sql);
注意:即使这样获得的Statement对象得到的ResultSet对象也有可能并不支持滚动,例如一个复杂的查询,可以通过使用ResultSet接口中的getType和getConcurrency方法来查看结果集实际支持的模式。
可滚动的结果集有一个游标,用以指示当前位置。
向前滚动:rs.previous();
向前或向后移动多行(正数:前,负数:后移):rs.relative(n);
获得当前行号:rs.getRow();
first、last、beforeFirst、afterLast这些简便方法用于将游标移动到第一行、最后一行、第一行之前、最后一行之后。
isFirst、isLast、isBeforeFirst、isAfterLast用于判断游标是否在特殊位置上。
假设想提高某些图书的价格,但是在执行update语句时,没有一个统一的提价标准,此时,可以根据任意设定的条件,迭代遍历所有的图书并更新他们的价格。
String query = "select * from Books";ResultSet rs = stat.excuteQuery(query);while(rs.next()){ if(...){ double increase = ... double price = rs.getDouble("price"); //更新结果集中当前行的数据,为同步到数据库中,一旦换行,更新数据无效 rs.updateDouble("Price",price+increase); //同步当前行数据到数据库中 rs.updateRow(); }}
新插入一行:
//新建一个空白行rs.moveToInsertRow();//插入数据rs.updateString("Title",title);rs.updateDouble("Price",price);//将当前行同步到数据库中rs.insertRow();//移动到插入行之前的行rs.moveToCurrentRow();
可滚动的结果集虽然功能强大,却有一个重要的缺陷:在与用户的整个交互过程中,必须始终与数据库保持连接。在这种情况下,我们可以使用行集。RowSet接口扩展自ResultSet接口,无需始终保持与数据库的连接。
以下为javax.sql.rowset包提供的接口,它们都扩展了RowSet接口:
ResultSet result = ...;RowSetFactory factory = RowSetProvider.newFactory();CachedRowSet crs = factory.createCachedRowSet();crs.populate(result);conn.close();
或者也可以让CachedRowSet对象自动建立一个数据库连接。首先,设置数据库参数:
//设置数据库相关参数crs.setURL(url);crs.setUsername(username);crs.setPassword(pwd);//设置查询语句和所有参数crs.setCommand("SELECT * FROM Books WHERE publisher_ID = ?");crs.setString(1,publisherId);//将查询结果填充到行集中//这个方法将建立数据库连接,执行查询操作,填充行集,断开数据库连接crs.execute();
如果查询结果非常大,不想把所有结果都放入行集中,用户可能只是想浏览其中几行而言,在这种情况下,可以指定每一页的尺寸。
CachedRowSet crs = ...;crs.setCommand(command);//每一页20条数据crs.setPageSize(20);crs.execute();//获得下一页数据crs.nextPage();
如果修改了行集中的数据,可以调用下列方法同步到数据库中。
crs.acceptChanges(conn);//或者 行集中保存了连接数据库的参数crs.acceptChanges();
注意:在同步数据库时,同步时首先检查行集中的原始值是否与数据库中的当前值一致,一致,将修改后的值覆盖数据库的当前值,否则,将抛出异常。
我们可以将一组语句构成一个事务,当所有语句执行之后,事务可以被提交。否则,如果其中某个语句遇到错误,那么事务将被回滚,就像任何语句都没被执行过一样。
默认情况下,数据库连接处于自动提交模式:每个sql语句一旦被执行,就会立即提交给数据库,一旦提交,将无法回滚。关闭自动提交:
conn.setAutoCommit(false);
多次调用executeUpdate方法:
stat.executeUpdate(command1);stat.executeUpdate(command2);stat.executeUpdate(command3);//没有问题conn.commit();//有问题,可以用try ...catch捕获conn.rollback();
保存点:
Statement stat = conn.createStatement();stat.executeUpdate(command1);Savepoint svpt = conn.setSavepoint();stat.executeUpdate(command2);//没有问题conn.commit();//有问题,可以回滚到保存点 保存点之后的语句就像没有执行过一样conn.rollback(svpt);//需要释放保存点conn.releaseSavepoint(svpt);
转载地址:http://dfwqi.baihongyu.com/