传智播客 JDBC基础之二进制对象的存储实现

 

   上一篇说到大的文本对象,现实世界里面的数据大多是二进制的对象,因此要在数据库中保存这些数据就需要有相应的数据类型。其实二进制对象和文本对象类似,对于JDBC来说,插入和读取这些对象是很容易的。只是演示代码中的IO操作比较复杂。数据库中二进制对象用BLOB表示,每种数据库都有这种数据类型,只是表示的名称不同而已。在mysql中该类型用的名称就是BLOB。下面的代码说明了二进制对象的存储于文本对象是一样的:
      static void create() throws SQLException, IOException {
          Connection conn = null;
          PreparedStatement ps = null;
          ResultSet rs = null;
          try {
              conn = JdbcUtils.getConnection();
              String sql = "insert into blob_test(big_bit) values (?) ";
              ps = conn.prepareStatement(sql);
              File file = new File("img.jpg");
              InputStream in = new BufferedInputStream(new FileInputStream(file));
              ps.setBinaryStream(1, in, (int) file.length());
              int i = ps.executeUpdate();
              in.close();
 
              System.out.println("i=" + i);
          } finally {
              JdbcUtils.free(rs, ps, conn);
          }
      }
    除了改变了读入的文件输入流和PreparedStatement调用的方法,其它几乎没有改变。因为读入的文件时二进制的文件,所以就不能用字符流了,改用相应的字节流就可以了。PreparedStatement调用的是setBinaryStream,参数与前面的方法也一致,只需将其中的字符流改为字节流。同样的,读出操作也做相应更改就可以了:
              rs = st.executeQuery("select big_bit  from blob_test");
              while (rs.next()) {
                  Blob blob = rs.getBlob(1);
                  InputStream in = blob.getBinaryStream();
                  File file = new File("IMG_0002_bak.jpg");
                  OutputStream out = new BufferedOutputStream(
                          new FileOutputStream(file));
                  byte[] buff = new byte[1024];
                  for (int i = 0; (i = in.read(buff)) > 0;) {
                      out.write(buff, 0, i);
                  }
                  out.close();
                  in.close();
    需要改动的代码如上所示,和文本对象一样,首先从结果集获取对应的Blob对象,然后调用Blob对象的getBinaryStream来获取相应的字节流,最后建立IO输出流,将获取的字节流通过输出流输出到文件上。到这里为止,JDBC中的数据类型应该就有了大概了解。在前面的例子中,结果处理都是直接打印显示一下查到的信息,正常情况下,一般不会这样做。在真正的J2EE设计中,往往采用三层架构来组织程序。分别是表示层 、业务逻辑层、数据访问层,三层之间用接口隔离,用Domain或者DTO来传递对象数据。而数据访问层是属于最底层的,JDBC只是数据访问层的一种最基本的实现方式。数据访问层是为业务逻辑层服务的。但是业务逻辑层操作的都是对象,而数据访问层操作的基本上时关系型数据库。数据访问层不能直接把关系数据库中的表、列、字段等内容直接返回给业务逻辑层。所以就需要进行类型转换。将关系型数据转换成对象返回给业务逻辑层。下面是演示用接口分离,用Domain对象来传递数据:
      public class User {
          private int id;
          private String name;
          private Date birthday;
          private float money;
          ...
      }
    首先建立Domain对象,有四个属性与数据库中的user表对应,其中的省略号表示接下来是四个字段的getter和setter方法。有了Domain对象,就有了数据库和业务逻辑层的数据传递对象。接下来就是创建DAO接口,提供给业务逻辑层使用:
      public interface UserDao {
          public void addUser(User user);
 
          public User getUser(int userId);
 
          public User findUser(String loginName, String password);
 
          public void update(User user);
 
          public void delete(User user);
 
      }
    有了DAO这个接口以后,就可以供业务逻辑层调用了。这样设计以后,DAO的具体实现可以随时改变而不用修改业务层代码。也就是说,业务层根本不知道数据访问层是怎么实现的。接下来是UserDao的JDBC实现方式,下面只是实现类一部分(包含一个实现方法):
      public class UserDaoJdbcImpl implements UserDao {
          public void addUser(User user) {
              Connection conn = null;
              PreparedStatement ps = null;
              ResultSet rs = null;
              try {
                  conn = JdbcUtils.getConnection();
                  String sql = "insert into user(name,birthday, money) values (?,?,?) ";
                  ps = conn.prepareStatement(sql);
                  ps.setString(1, user.getName());
                  ps.setDate(2, new java.sql.Date(user.getBirthday().getTime()));
                  ps.setFloat(3, user.getMoney());
                  ps.executeUpdate();
              } catch (SQLException e) {
                  e.printStackTrace();
              } finally {
                  JdbcUtils.free(rs, ps, conn);
              }
          }
        ...
    }
    在addUser方法中,基本全是模板代码,唯一区别是PreparedStatement的参数设置是从传入的User对象中取得的,而不是直接通过多个参数传入。上面的代码看上去很规范,但是在异常处理的地方是不正确的。因为,如果业务层代码调用addUser方法,在try语句块中出现异常,则程序直接打印了堆栈信息,然后业务层代码继续向下执行。这样就会导致业务层忽视了底层代码出现的异常,本来出错了,还在继续往下执行。但是又不能直接抛出异常,因为如果抛出SQLException,则将会导致UserDao接口发生改变,里面的addUser方法也要抛出SQLException异常。这样做的话,虽然用JDBC实现的数据访问层能够正常运行了,但是数据访问层也因此不能被替换成其它的实现了。因为其它的实现不一定会抛SQLException异常。所以这就破坏了三层结构的初衷,因此不能直接抛出SQLException异常。解决方法是自定义一个运行时异常,在catch语句块中抛出该异常,然后在业务逻辑代码中捕获该异常并进行处理。这样就保证了错误不会被隐藏,同时数据访问层的实现也能被随时替换掉。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值