java 核心技术Ⅱ--章五:JDBC数据库编程

java数据库编程JDBC

简单的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();
    }
}

1、读写LOB

除了数字、字符串和日期以外,许多数据库还可以存储大对象,例如图片或其他数据。在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);

2、SQL转义

“转义”语法是各种数据库普遍支持的特性,但是数据库使用的是与数据库相关的语法变体,因此,将转义语法转译为特定数据库的语法是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'})";

按照数据库的不同,将日期转换为数据库的日期格式。

3、获取数据库自动生成的键值

大多数数据库都支持某种在数据库中对行自动编号的机制。这些编号通常作用于主键,尽管JDBC没有提供生成主键的机制,但是,当一条数据插入以后,它可以获取该条数据自动生成的主键。

stat.executeUpdate(insertStatement,Statement.RETURN_GENERATED_KEYS);
ResultSet rs = stat.getGeneratedKeys();

4、可滚动和可更新的结果集

前面已经介绍,使用ResultSet接口中的next方法可以迭代遍历结果集中的所有行,对于一个只需要分析数据的程序来说,这已经够了,但是,如果用于展示一张表或查询结果的可视化数据显示,我们通常会希望用户可以在结果集上前后移动。对于可滚动结果集而言,我们可以在其中向前或向后移动,甚至可以跳到任意位置。

默认情况下,结果集是不可滚动和不可更新的。为了从查询中获取可滚动的结果集,必须使用下面的方法得到一个不同的Statement对象:

Statement stat = conn.createStatement(type,concurrency);

如果要获得预备语句,请调用下面的方法:

扫描二维码关注公众号,回复: 1467509 查看本文章
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();

5、行集

可滚动的结果集虽然功能强大,却有一个重要的缺陷:在与用户的整个交互过程中,必须始终与数据库保持连接。在这种情况下,我们可以使用行集。RowSet接口扩展自ResultSet接口,无需始终保持与数据库的连接。

5.1、构建行集

以下为javax.sql.rowset包提供的接口,它们都扩展了RowSet接口:

  • cachedRowSet允许在断开连接的状态下执行相关操作。
  • WebRowSet对象代表了一个被缓存的行集。该行集可以保存为XML文件。该文件可以移动到Web应用的其他层中,只要在该层中使用另一个WebRowSet对象重新打开该文件即可。
  • FilteredRowSet和JoinRowSet接口支持对行集的轻量级操作,它们等同于sql中的SELECT和HOIN操作。这两个接口的操作对象时存储在行集中的数据,因此运行时无需连接数据库。
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();

注意:在同步数据库时,同步时首先检查行集中的原始值是否与数据库中的当前值一致,一致,将修改后的值覆盖数据库的当前值,否则,将抛出异常。

6、事务

我们可以将一组语句构成一个事务,当所有语句执行之后,事务可以被提交。否则,如果其中某个语句遇到错误,那么事务将被回滚,就像任何语句都没被执行过一样。

默认情况下,数据库连接处于自动提交模式:每个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);

猜你喜欢

转载自blog.csdn.net/tjy_521/article/details/80535647