• 1. 访问数据库主讲人:孙鑫http://www.sunxin.org
  • 2. Spring的DAO支持Spring提供的DAO(数据访问对象)支持主要的目的是便于以标准的方式使用不同的数据访问技术, 如JDBC,Hibernate或者JDO等。它不仅可以让你方便地在这些持久化技术间切换, 而且让你在编码的时候不用考虑处理各种技术中特定的异常。http://www.sunxin.org
  • 3. 一致的异常层次Spring提供了一种简便的方法,把特定于某种技术的异常,如SQLException, 转化为自己的异常,这种异常属于以DataAccessException 为根的异常层次。这些异常封装了原始异常对象,这样就不会有丢失任何错误信息的风险。 除了对JDBC异常的封装外,Spring也对Hibernate异常进行了封装,把它们从一种专有的受查异常 (Hibernate3.0以前的版本),转化为一系列抽象的运行时异常(对JDO也是这样)。 它可以让你轻松处理大多数持久化异常(这些异常大多是不可恢复的,而且只出现在特定 的层次),而不再需要讨厌的样板式catch/throw代码块和异常声明。你仍然可以在需要 的地方捕获并处理这些异常。就像我们上面提到的,JDBC异常(包括特定于某种数据库 方言的异常)也可以被转化为同样的异常层次,这意味着你可以在一致的编程模型下,通 过JDBC来执行某些操作。 上述情况适用于各种使用模板方式访问ORM的版本。如果使用拦截器方式,你在应用中就得自己小心处理HibernateException、 JDOException等,最好是委托给 SessionFactoryUtils的 convertHibernateAccessException、 convertJdoAccessException等方法。这些方法可以把相应的异常转化为与org.springframework.dao中定义的异常层次相兼容的异常。 由于JDOException是unchecked异常,它们也可以被简单地抛出, 尽管这在异常处理方面牺牲了通用的DAO抽象。 Spring的DataAccessException异常层次位于org.springframework.dao包中。http://www.sunxin.org
  • 4. 一致的DAO支持抽象类为了便于以一种一致的方式使用各种数据访问技术,如JDBC、JDO和Hibernate, Spring提供了一套抽象的DAO类供你继承。这些抽象类提供一些方法来设置数据源,以及你正在使用的技术中专有的一些配置设定。 Dao支持类: JdbcDaoSupport - JDBC数据访问对象的基类。需要设置数据源,同时为子类提供JdbcTemplate。 HibernateDaoSupport - Hibernate数据访问对象的基类。需要设置SessionFactory,同时为子类提供HibernateTemplate。也可以选择直接通过HibernateTemplate来初始化, 这样就可以重用后者的设置,例如SessionFactory,flush的方式,异常解释器等等。 JdoDaoSupport - JDO数据访问对象的基类。需要设置PersistenceManagerFactory,同时为子类提供JdoTemplate。http://www.sunxin.org
  • 5. 配置数据源为了对数据库进行JDBC操作,你必须首先得到一个数据库连接。你可以采用传统的数据库访问方式,通过Class.forName()来加载与注册数据库驱动,然后通过DriverManager.getConnection()来得到数据库连接。然而采用这种方式,你需要自己去管理数据库连接,无法得到数据源的好处。在Spring的DAO框架中,Connection对象是通过DataSource得到的。Spring提供了几种选择让你的应用程序获得DataSource。http://www.sunxin.org
  • 6. 使用DriverManagerDataSourceSpring发行版提供一个非常轻量级的DataSource实现:DriverManagerDataSource,这个类对于脱离Web容器的单元测试是十分便利的。在产品阶段我们还是应该使用功能更为强大的第三方的数据源实现。 DriverManagerDataSource和传统的方式一样获取JDBC连接。 下面这个例子说明如何配置DriverManagerDataSource: DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName( "com.mysql.jdbc.Driver"); dataSource.setUrl( "jdbc:mysql://localhost:3306/bookstore"); dataSource.setUsername("root"); dataSource.setPassword("1234"); http://www.sunxin.org
  • 7. com.mysql.jdbc.Driver jdbc:mysql://localhost:3306/bookstore root 1234 http://www.sunxin.org
  • 8. 从JNDI得到数据源 java:comp/env/jdbc/bookstore http://www.sunxin.org
  • 9. 创建一个DataSource连接池Jakarta Commons DBCP是一套开放源代码的数据库连接池项目。你可以从下面网址处下载: http://jakarta.apache.org/commons/dbcp 使用Jakarta Commons DBCP项目中的BasicDataSource类的例子如下: com.mysql.jdbc.Driver root 1234 http://www.sunxin.org
  • 10. 模板方法模式模板方法模式是类的行为模式(GOF95)。准备一个抽象类,将部分逻辑以具体方法以及具体构造方法的形式实现;然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。这就是模板方法模式的用意。(参见《Java与模式》P641) 模板方法中的方法可以分为两大类:模板方法(Template Method)和基本方法(Primitive Method)。 模板方法的实现可以有两种,一种是通过抽象方法来实现,子类继承父类,重写抽象方法;另一种是将具体的实现委托给一个接口,这个接口的不同实现定义了逻辑的具体实现。http://www.sunxin.org
  • 11. 在Spring中使用JDBC--使用JdbcTemplateSpring的JDBC框架承担了资源管理和错误处理的重担,使你的JDBC代码非常干净。它把你从书写statement和查询语句来读写数据库中解放出来。 所有Spring的数据访问框架都结合了模板类,在这里,就是JdbcTemplate,该类需要一个DataSource实例。 所有的Spring DAO模板类都是线程安全的,在我们的应用中,对于每个DataSource我们只需要一个JdbcTemplate实例。要使用JdbcTemplate,你的每一个DAO类都需要配置一个JdbcTemplate的实例。http://www.sunxin.org
  • 12. 使用JdbcTemplateJdbcTemplate类定义了许多重载的execute()方法,如下所示: public Object execute(CallableStatementCreator csc, CallableStatementCallback action) throws DataAccessException public Object execute(ConnectionCallback action) throws DataAccessException public Object execute(StatementCallback action) throws DataAccessException public void execute(String sql) throws DataAccessException public Object execute(String callString, CallableStatementCallback action) throws DataAccessException public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action) throws DataAccessException 还有很多query()和update()方法,参看API文档。http://www.sunxin.org
  • 13. 使用JdbcTemplateStatementCallback PreparedStatementCreator PreparedStatementCallback PreparedStatementSetterhttp://www.sunxin.org
  • 14. SqlProvider实现org.springframework.jdbc.core.SqlProvider接口,以提供SQL字符串。 典型地被PreparedStatementCreator、CallableStatementCreator和StatementCallback所实现,想要暴露它们用于创建Statement的SQL语句,允许在发生异常时提供更好的上下文信息。http://www.sunxin.org
  • 15. 批量更新批量更新可以使用org.springframework.jdbc.core.BatchPreparedStatementSetter接口。该接口有两个方法,如下: int getBatchSize() 返回批量处理语句的数目。 void setValues(PreparedStatement ps, int i) throws SQLException 在给定的PreparedStatement对象ps上设置值。 在JdbcTemplate类中提供了两个用于批量更新的方法: public int[] batchUpdate(String[] sql) throws DataAccessException public int[] batchUpdate(String sql, BatchPreparedStatementSetter pss) throws DataAccessExceptionhttp://www.sunxin.org
  • 16. 读取数据获取数据主要使用JdbcTemplate的query()方法,我们先看下面的两个方法: public Object query(String sql, ResultSetExtractor rse) throws DataAccessException public void query(String sql, RowCallbackHandler rch) throws DataAccessException ResultSetExtractor接口主要在JDBC框架内部使用,它只有一个方法: Object extractData(ResultSet rs) throws SQLException, DataAccessException RowCallbackHandler接口也只有一个方法: void processRow(ResultSet rs) throws SQLException ResultSetExtractor接口和RowCallbackHandler接口的区别是,一个ResultSetExtractor对象是典型的无状态的,可以重复使用,只要它不访问有状态的资源(就象LOB内容输出流)或者在对象中保持结果状态。一个RowCallbackHandler对象是典型的有状态的,它在对象中保持结果状态,对于稍后的检查是可利用的。 这两个接口都只是用于取出单条记录。http://www.sunxin.org
  • 17. 使用RowMapper接口JdbcTempalte使用RowMapper接口来映射返回的结果集。 RowMapper接口典型的或者用于JdbcTemplate的query方法(和RowMapperResultSetExtractor适配器类一起),或者用于存储过程的输出参数。RowMapper对象典型是无状态的,因此可以重复使用;在单独的地方实现行映射(row-mapping),它们是理想的选择。该接口只有一个方法: Object mapRow(ResultSet rs, int rowNum) throws SQLException 在程序中使用RowMapper接口,Spring框架将会使用RowMapperResultSetExtractor适配器类。http://www.sunxin.org
  • 18. RowMapperResultSetExtractor类RowMapperResultSetExtractor类实现了ResultSetExtractor接口,是ResultSetExtractor接口的适配器实现,委派给RowMapper对象,该对象为每一行创建一个对象。每一个对象被添加到ResultSetExtractor的结果列表中。 RowMapperResultSetExtractor类可以获取数据库表中所有的行记录,或者获取指定行的记录。 注意,RowMapper对象是典型无状态的,因此可以重复使用,只是RowMapperResultSetExtractor适配器是有状态的。 RowMapperResultSetExtractor有两个重载的构造方法: public (RowMapper rowMapper) public RowMapperResultSetExtractor(RowMapper rowMapper, int rowsExpected) 第二个构造方法的rowsExpected参数指定预期返回的行数。注意,这个参数并不是用于指定返回的行数,这个参数仅仅是用于集合处理的优化。http://www.sunxin.org
  • 19. 返回简单类型的查询使用JdbcTemplate类的queryForXXX()方法。http://www.sunxin.org
  • 20. 调用存储过程Spring提供了CallableStatementCallback接口来执行存储过程的回调。该接口只有一个方法: Object doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException CallableStatementCallback接口被JdbcTemplate内部使用,但是对应用程序代码也是有用的。注意,传入的CallableStatement可以被框架所创建,也可以被定制的CallableStatementCreator创建。然而,后者几乎从来不需要。 可以用JdbcTemplate类的下列两个方法来执行存储过程。 public Object execute(CallableStatementCreator csc, CallableStatementCallback action) throws DataAccessException public Object execute(String callString,CallableStatementCallback action) throws DataAccessExceptionhttp://www.sunxin.org
  • 21. 把JDBC操作建模成对象org.springframework.jdbc.object包由一些允许你以更面向对象的方式访问数据库的类组成。你可以执行查询并获得一个包含业务对象的List,查询出的关系数据的字段值被映射为业务对象的属性。你也可以执行存储过程,更新,删除和插入操作。 Spring提供了读写数据的类。这些数据库对象是线程安全的,意味着对于每个数据库操作,你只需创建一个实例。 此外,这些数据库操作对象必须在运行前先编译一下,这样就让对象知道什么时候可以预备statement,以便在稍后能执行它们。http://www.sunxin.org
  • 22. SqlUpdateorg.springframework.jdbc.object.SqlUpdate是RdbmsOperation的子类,表示一个SQL更新。 这个类提供了许多update()方法,类似于查询对象的execute()方法。 这个类是具体的。通过SQL设定和参数声明,它可以很容易的参数化,虽然它也可以子类化 (例如增加自定义update方法)。http://www.sunxin.org
  • 23. SqlQuery这是一个表示SQL查询的可重用的而且线程安全的对象。子类必须实现newRowMapper ()方法将每一行映射为一个对象,该对象将作为List中的一个元素被SqlQuery的execute()方法返回。这个类很少被直接使用,而使用它的子类MappingSqlQuery,它提供了更多的方法将数据行映射到Java类。MappingSqlQueryWithParameters 和UpdatableSqlQuery是继承SqlQuery的另外两个实现。http://www.sunxin.org
  • 24. MappingSqlQueryMappingSqlQuery是一个可以重用的查询对象, 它的子类必须实现抽象方法mapRow(ResultSet, int)来把JDBC ResultSet的每一行转换成一个对象。 在所有的SqlQuery实现中,这个类是最常使用并且也是最容易使用的。http://www.sunxin.org
  • 25. StoredProcedure这是RDBMS存储过程的对象抽象的超类。它是一个抽象类,它的执行方法都是protected的, 以避免被直接调用,而只能通过提供更严格形式的子类调用。 继承的sql属性是RDBMS中存储过程的名字。注意JDBC3.0引入了命名的参数,虽然这个类中提供的其他功能在JDBC3.0中也是必要的。 下面是一段例子程序,它调用Oracle数据库提供的函数sysdate()。 要使用存储过程的功能,你必须创建一个继承StoredProcedure的子类。在这个例子中没有任何输入参数,但需要使用SqlOutParameter类声明一个date型的输出参数。 execute()方法返回一个map,对于每一个被声明的输出参数在map中都有一个条目,参数的名字作为key。http://www.sunxin.org
  • 26. private class MyStoredProcedure extends StoredProcedure { public static final String SQL = "sysdate"; public MyStoredProcedure(DataSource ds) { setDataSource(ds); setFunction(true); setSql(SQL); declareParameter(new SqlOutParameter("date", Types.DATE)); compile(); } public Map execute() { Map out = execute(new HashMap()); return out; } }http://www.sunxin.org
  • 27. SqlFunctionSqlFunction封装返回单一结果行的查询。默认的情况返回一个int,当然我们可以覆盖它,通过使用带有额外返回类型参数的方法。这和使用JdbcTemplate的 queryForXxx方法很相似。使用SqlFunction的好处是你不必创建JdbcTemplate,它在后台自动进行。 这个类的目的是用于调用SQL function,使用像"select user()"或者"select sysdate from dual" 得到单一的结果。它不是用来调用复杂的存储功能,也不是用来使用一个CallableStatement 来调用存储过程或者存储函数。对于这种类型的处理应当使用StoredProcedure或者SqlCall。 SqlFunction是一个具体的类,它通常不需要子类化。像所有的RdbmsOperation对象,SqlFunction对象是线程安全的。http://www.sunxin.org
  • 28. 获取生成的主键通常我们会用一个整型值作为数据库的主键,一种是采用自动增长的主键,一种是手动插入主键。对于前者,在插入数据后,我们如何才能方便的得到插入数据的主键呢?对于后一种情况,我们应该如何计算要插入数据的主键呢? 不采用自动增长主键 (1)建立主键值表(2)实现DataFieldMaxValueIncrementer接口 使用KeyHolder(用于自动增长主键)http://www.sunxin.org
  • 29. 使用KeyHolderorg.springframework.jdbc.support.KeyHolder是获取键的接口,典型地用于自动生成的键,作为JDBC insert语句潜在的返回值。 JDBC 3.0,作为J2SE1.4的一部分,规定遵从JDBC 3.0 的驱动必须实现java.sql.Statement.getGeneratedKeys()方法。这个方法会从数据库中获取生成主键(例如,在MySQL中调用select last_insert_id();)。该方法的原型声明如下所示: ResultSet getGeneratedKeys() throws SQLException 为了利用Spring JDBC生成的主键,使用KeyHolder作为SqlUpdate.update()的第2参数。 KeyHolder keys = new GeneratedKeyHolder(); su.update(params, keys); user.setId(new Long(keys.getKey().longValue())); 对于不符规范的驱动可以使用前面介绍过的DataFieldMaxValueIncrementer策略。http://www.sunxin.org