zoukankan      html  css  js  c++  java
  • 持久层封装 JDBC

    Spring对JDBC

    进行了良好的封装,通过提供相应的模板和辅助类,在相当程度上降低
    了JDBC操作的复杂性。并且得益于Spring良好的隔离设计,JDBC封装类库可以脱离
    Spring Context独立使用,也就是说,即使系统并没有采用Spring作为结构性框架,我们
    也可以单独使用Spring的JDBC部分(spring-dao.jar)来改善我们的代码。
     
    作为对比,首先让我们来看一段传统的JDBC代码:
    Connection conn =null;
    Statement stmt = null;
    try {
    conn = dataSource.getConnection();
     stmt = con.createStatement();
     stmt.executeUpdate("UPDATE user SET age = 18 WHERE id = 'erica'");
    } finally {
     if (stmt != null) {
       try {
         stmt.close();
       } catch (SQLException ex) {
         logger.warn("Exception in closing JDBC Statement", ex);
       }
     }
    if (conn != null) {
     try {
       conn.close();
     } catch (SQLException ex) {
       logger.warn("Exception in closing JDBC Connection", ex);
     }
    }
    }
    类似上面的代码非常常见。

    为了执行一个SQL语句,我们必须编写22行代码,而其中
    21行与应用逻辑并无关联,并且,这样的代码还会在系统其他地方(也许是每个需要数据
    库访问的地方)重复出现。 于是,大家开始寻找一些设计模式以改进如此的设计,Template模式的应用是其中一
    种典型的改进方案。
    Spring的JDBC封装,很大一部分就是借助Template模式实现,它提供了一个优秀的
    JDBC模板库,借助这个工具,我们可以简单有效的对传统的JDBC编码方式加以改进。
    下面是借助Spring JDBC Template修改过的代码,这段代码完成了与上面代码相同
    的功能。
     
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    jdbcTemplate.update("UPDATE user SET age = 10 WHERE id = 'erica'");

     
    可以看到,两行代码完成了上面需要19行代码实现的功能。所有冗余的代码都通过合理
    的抽象汇集到了JdbcTemplate中。
    无需感叹,借助Template模式,我们大致也能实现这样一个模板,不过,Spring的设计
    者已经提前完成了这一步骤。org.springframework.jdbc.core.JdbcTemplate中包含了
    这个模板实现的代码,经过Spring设计小组精心设计,这个实现可以算的上是模板应用的
    典范。特别是回调(CallBack)的使用,使得整个模板结构清晰高效。值得一读。
     
    Tips:实际开发中,可以将代码中硬编码的SQL语句作为Bean的一个String类型属性,借
    助DI机制在配置文件中定义,从而实现SQL的参数化配置。

    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    jdbcTemplate
      .update(
      "UPDATE user SET age = ? WHERE id = ?",
      new PreparedStatementSetter() {
        public void setValues(PreparedStatementSetter ps)
          throws SQLException {
            ps.setInt(1, 18);
            ps.setString(2, "erica");
          }
        }
    );
     
    可以看到,上面引用了update方法的另一个版本,传入的参数有两个,第一个用于创建
    PreparedStatement的SQL。第二个参数是为PreparedStatement设定参数的
    PreparedStatementSetter。
    第二个参数的使用方法比较独到,我们动态新建了一个PreparedStatementSetter类,
    并实现了这个抽象类的setValues方法。之后将这个类的引用作为参数传递给update。
    update接受参数之后,即可调用第二个参数提供的方法完成PreparedStatement的初始
    化。
    Spring JDBC Template中大量使用了这样的Callback机制,这带来了极强的灵活性和
    扩展性。
     
    上面演示了update方法的使用(同样的操作适用于update、insert、delete)。下面是
    一个查询的示例。

    final List userList = new ArrayList();
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    jdbcTemplate
      .query(   "SELECT name, sex, address FROM user WHERE age > 18",
      new RowCallbackHandler() {
        public void processRow(ResultSet rs) throws SQLException {
            User user = new User();
            user.setId(rs.getString("name"));
            user.setSex(rs.getString("sex"));
            user.setAddress(rs.getString("address"));
            userList.add(product);
          }
       }
    );
     
    这里传入query方法的有两个参数,第一个是Select查询语句,第二个是一个
    RowCallbackHandler实例,我们通过RowCallbackHandler对Select语句得到的每行记
    录进行解析,并为其创建一个User数据对象。实现了手动的OR映射。
     
     
    此外,我们还可以通过JdbcTemplate.call方法调用存储过程。
     
    query、update方法还有其他很多不同参数版本的实现,具体调用方法请参见Spring
    JavaDoc。

     
    JdbcTemplate与事务
    上例中的JdbcTemplate操作采用的是JDBC默认的AutoCommit模式,也就是说我们还
    无法保证数据操作的原子性(要么全部生效,要么全部无效),如:
     
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    jdbcTemplate.update("UPDATE user SET age = 10 WHERE id = 'erica'");
    jdbcTemplate.update("UPDATE user SET age = age+1 WHERE id = 'erica'");
     
    由于采用了AutoCommit模式,第一个update操作完成之后被自动提交,数据库
    中”erica”对应的记录已经被更新,如果第二个操作失败,我们无法使得整个事务回滚到最
    初状态。对于这个例子也许无关紧要,但是对于一个金融帐务系统而言,这样的问题将导致
    致命错误。
     
    为了实现数据操作的原子性,我们需要在程序中引入事务逻辑,在JdbcTemplate中引入
    事务机制,在Spring中有两种方式:
    1.  代码控制的事务管理
    2.  参数化配置的事务管理
    下面就这两种方式进行介绍。
     
    u  代码控制的事务管理
    首先,进行以下配置,假设配置文件为(Application-Context.xml):
     
    再对上面的例子进行一些改进,通过PrepareStatement执行update操作以避免SQL
    Injection 漏洞
    <beans>

    <bean id="dataSource"
      class="org.apache.commons.dbcp.BasicDataSource"
      destroy-method="close">
      <property name="driverClassName">
      <value>net.sourceforge.jtds.jdbc.Driver</value>
      </property>
      <property name="url">
      <value>jdbc:jtds:sqlserver://127.0.0.1:1433/Sample</value>
      </property>
      <property name="username">
      <value>test</value>
      </property>
      <property name="password">
      <value>changeit</value>
      </property>
    </bean>
    <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransac
    onManager">
      <property name="dataSource">
      <ref local="dataSource" />
      </property>
    </bean>  
    <bean id="userDAO" class="net.xiaxin.dao.UserDAO">
      <property name="dataSource">
        <ref local="dataSource" />
      </property>
      <property name="transactionManager">
        <ref local="transactionManager" />
      </property>
      </bean>
    </beans>

    配置中包含了三个节点:  
    Ø  dataSource
    这里我们采用了apache dhcp组件提供的DataSource实现,并为其配置了
    JDBC驱动、数据库URL、用户名和密码等参数。
    Ø  transactionManager
    针对JDBC DataSource类型的数据源,我们选用了
    DataSourceTransactionManager
    作为事务管理组件。

    如果需要使用基于容器的数据源(JNDI),我们可以采用如下配置:
    <bean id="dataSource"
     class="org.springframework.jndi.JndiObjectFactoryBean">
     <property name="jndiName">
       <value>jdbc/sample</value>
     </property>
    </bean>
    <bean id="transactionManager"
    class="org.springframework.transaction.jta.JtaTrans
    actionManager"  
    />
    userDAO
    申明了一个UserDAO Bean,并为其指定了dataSource和
    transactionManger资源。

     
      UserDAO对应的代码如下:
    public class UserDAO {
     
    private DataSource dataSource; private PlatformTransactionManager transactionManager;
     
    public PlatformTransactionManager getTransactionManager() {
      return transactionManager;
      }
     
    public void setTransactionManager(PlatformTransactionManager
    transactionManager) {
      this.transactionManager = transactionManager;
      }
     
    public DataSource getDataSource() {
      return dataSource;
      }
     
    public void setDataSource(DataSource dataSource) {
      this.dataSource = dataSource;
      }
     
    public void insertUser() {
        TransactionTemplate tt =
        new TransactionTemplate(getTransactionManager());
     
      tt.execute(new TransactionCallback() {
     
      public Object doInTransaction(TransactionStatus status) {
            JdbcTemplate jt = new JdbcTemplate(getDataSource());
            jt.update(
            "insert into users (username) values ('xiaxin');");
            jt.update(
            "insert into users (id,username) values(2,
    'erica');");
          return null;       }
        });
      }
    } 可以看到,在insertUser方法中,我们引入了一个新的模板类:
    org.springframework.transaction.support.TransactionTemplate。
    TransactionTemplate封装了事务管理的功能,包括异常时的事务回滚,以及操作成
    功后的事务提交。和JdbcTemplate一样,它使得我们无需在琐碎的try/catch/finally代码
    中徘徊。 在doInTransaction中进行的操作,如果抛出未捕获异常将被自动回滚,如果成功执行,
    则将被自动提交。
     
    这里我们故意制造了一些异常来观察数据库操作是否回滚(通过在第二条语句中更新自
    增ID字段故意触发一个异常):
     
    编写一个简单的TestCase来观察实际效果:
      InputStream is = new FileInputStream("Application-Context.xml");
      XmlBeanFactory factory = new XmlBeanFactory(is);
      UserDAO userDAO = (UserDAO) factory.getBean("userDAO");
      userDAO.insertUser();
     
      相信大家多少觉得上面的代码有点凌乱,Callback类的编写似乎也有悖于日常的编程
    习惯(虽然笔者觉得这一方法比较有趣,因为它巧妙的解决了笔者在早期自行开发数据访问
    模板中曾经遇到的问题)。
     
      如何进一步避免上面这些问题?Spring 的容器事务管理机制在这里即体现出其强大
    的能量。

    作者:少帅

    出处:少帅的博客--http://www.cnblogs.com/wang3680

    您的支持是对博主最大的鼓励,感谢您的认真阅读。

    本文版权归作者所有,欢迎转载,但请保留该声明。

    支付宝 微信
  • 相关阅读:
    mtk camera 移植步骤
    Camera 涉及的文件70
    android camera(四):camera 驱动 GT2005
    android camera(二):摄像头工作原理、s5PV310 摄像头接口(CAMIF)
    android camera(三):camera V4L2 FIMC
    android camera(一):camera模组CMM介绍
    【译】第23节---Fluent API
    【译】第22节---Fluent API
    【译】第21节---Fluent API
    【译】第20节---数据注解-InverseProperty
  • 原文地址:https://www.cnblogs.com/wang3680/p/2988839.html
Copyright © 2011-2022 走看看