《Spring3实战》摘要(5-1)征服数据库

来源:互联网 发布:歌词有傻瓜的网络歌曲 编辑:程序博客网 时间:2024/04/20 08:15

第5章 征服数据库

5.1 Spring 的数据访问哲学

DAO是数据访问对象(data access object)的缩写。DAO提供了数据读取和写入DAO数据库中的一种方式。它们应该以接口的方式发布功能,而应用程序的其他部分就可以通过接口来进行访问了。

5.1.1 了解 Spring 的数据访问异常体系

JDBC 的异常体系过于简单,实际上,它算不上一个体系。而 Hibernate 的异常体系是其本身所独有的。我们需要的是,数据访问异常要具有描述性而且又与特定的持久化框架无关。

Spring 的平台无关持久化异常

Spring JDBC 提供了多个数据访问异常,分别描述了它们抛出时对应的问题。而且 Spring 的异常体系并没有与特定的持久化方式相关联。这意味着我们可以使用 Spring 抛出一致的异常,而不用关心所选择的持久化方案。这有助于我们将所选择持久化机制与数据访问层隔离开来。

看!不用写 catch 代码块

Spring 的数据访问异常都继承自 DataAccessException 。DataAccessException 的特殊之处在于它是一个非检查型异常。换句话说,没有必要捕获 Spring 所抛出的数据访问异常。

Spring 认为触发异常的很多问题是不能再 catch 代码块中修复。Spring 使用了费检查型异常,而不是强制开发人员编写 catch 代码块。这把是否要捕获异常的权利留给了开发人员。

5.1.2 数据访问模板化

模板方法模式:模板方法定义了过程的主要框架。从软件术语来讲,模板方法将过程中与特定实现相关的部分委托给接口,而这个接口的不同实现定义了过程中的具体行为。这也是 Spring 再数据访问中所使用的模式。

Spring 将数据访问过程中固定的和可变的部分明确划分为两个不同的类:模板(template)和回调(callback)模板管理过程中固定的部分,而回调处理自定义的数据访问代码。

针对不同的持久化平台,Spring 提供了多个可选的模板。如果直接使用 JDBC ,则可以选择 JdbcTemplate 。如果希望使用对象关系映射框架,则 HibernateTemplate 或 JpaTemplate 可能更适合你。

Spring 提供的数据访问模板,分别使用与不同的持久化机制:

模板类(org.springframework.*) 用途 jca.cii.core.CciTemplate JCA CCI 连接 jdbc.core.JdbcTemplate JDBC 连接 jdbc.core.namedparam.NamedParameterJdbcTemplate 支持命名参数的 JDBC 连接 jdbc.core.simple.SimpleJdbcTemplate 通过 Java 5 简化后的 JDBC 连接 orm.hibernate.HibernateTemplate Hibernate 2.x 的 Session orm.hibernate3.HibernateTemplate Hibernate 3.x 的 Session orm.ibatis.SqlMapClientTemplate iBATIS SqlMap 客户端 orm.jdo.JdoTemplate Java 数据对象(Java Data Object)实现 orm.jpa.JpaTempalte Java 持久化 API 的实体管理器

5.1.3 使用 DAO 支持类

基于模板-回调设计,Spring 提供了 DAO 支持类,而将业务自己的 DAO 类作为它的子类。

模板类、DAO支持类以及自定义 DAO 实现之间的关系,如下图所示:
这里写图片描述

当编写应用程序自己的 DAO 实现时,可以继承自 DAO 支持类并调用模板获取方法 getXXXXTemplate() 来直接访问底层的数据访问模板。
另外,如果需要访问底层的持久化平台,则每个 DAO 支持类都能够访问其与数据库进行通信的类。例如,JdbcDaoSupport 包含了一个 getConnection() 方法,可以用于直接处理 JDBC 连接。

5.2 配置数据源

Spring 提供了在 Spring 上下文中配置数据源 Bean 的多种方式,包括:

  • 通过 JDBC 驱动程序定义的数据源
  • 通过 JNDI 查找的数据源
  • 连接池的数据源

对于即将发布到生产环境中的应用程序,我建议使用从连接池获取连接的数据源。如果可能的话,我倾向于通过应用服务器的 JNDI 来获取池中的数据源。

5.2.1 使用 JNDI 数据源

JNDI是 Java 命名与目录接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之一。

JNDI 的用处:程序员应该不需要关心“具体的数据库后台是什么? JDBC 驱动程序是什么?JDBC URL格式是什么?访问数据库的用户名和口令是什么?”等等这些问题,程序员编写的程序应该没有对 JDBC 驱动程序的引用,没有服务器名称,没有用户名称或口令 —— 甚至没有数据库池或连接管理。而是把这些问题交给 J2EE 容器来配置和管理,程序员只需要对这些配置和管理进行引用即可。

Spring 应用程序经常部署在 Java EE 应用服务器中,如 WebSphere,JBoss 或者像 Tomcat 这样的 Web 容器。这些服务器运行你配置通过 JNDI 获取数据源。这种配置的好处在于数据源完全可以在应用程序之外进行管理,这样应用程序只需在访问数据库的时候查找数据源就可以了。

利用 Spring ,我们可以像使用 Spring Bean 那样配置 JNDI 中数据源的引用并将其装配到需要的类中。位于 jee 命名空间下的 <jee:jndi-lookup> 元素可以用于检索 JNDI 中的任何对象(包括数据源)并将其用于 Spring Bean 中。

示例如下:

<jee:jndi-lookup id="dataSource" jndi-name="/jdbc/SpitterDS" resource-ref="true" />

jndi-name 属性用于指定 JNDI 中资源的名称。如果只设置了 jndi-name 属性,那么就会根据指定的名称查找数据源。但是,如果应用程序运行在 Java 应用程序服务器中,则需要将 resource-ref 属性设置为 true,这样给定的 jndi-name 将会自动添加 java:comp/env/ 前缀。

5.2.2 使用数据源连接池

Spring 并没有提供数据源连接池实现,但 Jakarta Commons Database Connection pooling (DBCP) 项目(http://jakarta.apache.org/commons/dbcp)是一个不错的选择。

DBCP 包含了多个提供连接池功能的数据源,其中 BasicDateSource 是最常用的,因为它易于在 Spring 中配置,而且类似于 Spring 自带的 DriveManagerSource (将在下一小节介绍)。

注:使用 dbcp 连接池需要添加 dbcp 的jar包。

<!-- 创建数据源,这里使用 H2 数据库--><!-- 前4个属性是配置 BasicDataSource 所必需的。 --><bean id="dataSource"     class="org.apache.commons.dbcp.BasicDataSource">    <property name="driverClassName" value="org.h2.Driver" />    <property name="url" value="jdbc:h2:tcp://localhost/mem:spitter" />    <property name="username" value="sa" />    <property name="password" value="" />    <!-- 连接池启动时会创建5个连接,当需要更多连接时,允许最大的连接数为10 -->    <property name="initialSize" value="5" />    <property name="maxActive" value="10" /></bean>

配置 BasicDataSource 的其他常用属性:

池配置属性 所指定的内容 initalSize 池启动时创建的连接数量 maxActive 同一时间可从池中分配的最多连接数,如果设置为0,表示无限制 maxIdle 池里不会被释放的最多空间连接数,如果设置为0,表示无限制 maxopenPreparedStatements 在同一时间能够从语句池中分配的预处理语句的最大数量,如果设置为0,表示无限制 maxWait 在抛出异常之前,池等待连接回收的最大时间(当没有可用连接时),如果设置为-1.表示无限等待 minEvictableTimeMillis 连接在池中保持空闲而不被回收的最大时间 minIdle 在不创建新连接的情况下,池中保持空闲的最小连接数 poolPreparedStatements 是否对预处理语句进行池管理(布尔值)

5.2.3 基于 JDBC 驱动的数据源

在 Spring 中,通过 JDBC 驱动定义数据源是最简单的配置方式。Spring 提供了两种数据源对象(均位于 org.springframework.jdbc.datasource 包中)供选择。

  • DriverManagerDataSource:在每个连接请求时都会返回一个新建的连接。与 DBCP 的 BasicDataSource 不同,由 DriverManagerDataSource 提供的连接并没有进行池化管理。
  • SingleConnectionSource:在每个连接请求时都会返回同一个连接。尽管 SingleConnectionDataSource 不是严格意义上的连接池数据源,但是你可以将其视为只有一个连接的池。

以上两个数据源的配置与 BasicDataSource 的配置类似,唯一的区别在于它们都没有提供连接池功能,所以没有可配置的池相关的属性。

5.3 在 Spring 中使用 JDBC

5.3.1 应对失控的 JDBC 代码

JDBC 的优点

  • 建立在标准 SQL 之上,不需要掌握其他框架的查询语句;
  • JDBC 允许用户使用数据库的所有属性,这是其他框架不鼓励甚至禁止的;
  • JDBC 能够更好地对数据访问的性能进行调优;
  • 相对于持久层框架,JDBC 能在更低的层次上处理数据,能够访问和管理数据库中单独的列。

JDBC 的缺点

  • 为了完成一项基本的数据库操作,JDBC 需要实现大量用于创建连接和语句以及异常处理的样板代码。

5.3.2 使用 JDBC 模板

注:需要添加spring-jdbc.jar

Spring 的 JDBC 框架承担了资源管理和异常处理的工作,从而简化了 JDBC 代码,让我们只需要编写从数据库读写数据的必需代码。

Spring 将数据访问的样板式代码提取到模板类中。Spring为 JDBC 提供了 3个模板类供使用。

  • org.springframework.jdbc.core.JdbcTemplate:最基本的 Spring JDBC 模板,这个模板支持最简单的 JDBC 数据库访问功能以及简单的索引参数查询。
  • org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate:使用该模板类执行查询时,可以将查询值以命名参数的形式绑定到 SQL 中,而不是使用简单的索引参数。
  • org.springframework.jdbc.core.simple.SimpleJdbcTemplate:该模板类利用 Java 5 的一些特性,如自动装箱、泛型以及可变参数列表来简化 JDBC 模板的使用。

注:在 eclipse 中实现书中例子时发现 SimpleJdbcTemplate 已经被标注为@deprecated(声明不赞成, 反对),查找网上说法是
“2.5的时候simplejdbctemplate把JdbcTemplate替代了,到了3.1 JdbcTemplate和NamedParameterJdbcTemplate又把simplejdbctemplate替代了

3者的区别可查看:http://www.cnblogs.com/xuyuanjia/p/5706372.html

5.3.2.1 JdbcTemplate

最基本的 Spring JDBC 模板,这个模板支持最简单的 JDBC 数据库访问功能以及简单的索引参数查询。

public class JdbcSpitterDAO {    @Autowired    private JdbcTemplate jdbcTemplate;    private static final String SQL_INSERT_SPITTER = "insert into spitter(username, password, email)"            +" values(?, ?, ?)";    public void addSpitter(Spitter spitter) {        jdbcTemplate.update(SQL_INSERT_SPITTER,                spitter.getUsername(),                spitter.getPassword(),                spitter.getEmail());    }}

5.3.2.2 NamedParameterJdbcTemplate

使用该模板类执行查询时,可以将查询值以命名参数的形式绑定到 SQL 中,而不是使用简单的索引参数。

public class JdbcSpitterDAO {    @Autowired    private NamedParameterJdbcTemplate jdbcTemplate;    private static final String SQL_INSERT_SPITTER = "insert into spitter(username, password, email)"            +" values(:username, :password, :email)";    public void addSpitter(Spitter spitter) {        Map<String,Object> params = new HashMap<String,Object>();        params.put("username", spitter.getUsername());        params.put("password", spitter.getPassword());        params.put("email", spitter.getEmail());        jdbcTemplate.update(SQL_INSERT_SPITTER,params);    }}

5.3.2.3 SimpleJdbcTemplate

该模板类利用 Java 5 的一些特性,如自动装箱、泛型以及可变参数列表来简化 JDBC 模板的使用。

SimpleJdbcTemplate 的 queryForObject() 方法来从数据库查询指定类型的Object。queryForObject() 方法有3个参数:

  • String,查找数据的 SQL
  • ParameterizedRowMapper 对象,用来从 ResultSet 中提取值并构建域对象;
  • 可变参数列表,列出了要绑定到查询上的索引参数值。
public class JdbcSpitterDAO {    @Autowired    private SimpleJdbcTemplate jdbcTemplate;    private static final String SQL_SELECT_SPITTER = "select id, username, password, email from spitter where id = ?";    public Spitter getSpitterById(long id) {        return jdbcTemplate.queryForObject(SQL_SELECT_SPITTER,                new ParameterizedRowMapper<Spitter>(){                    @Override                    public Spitter mapRow(ResultSet rs, int rowNum) throws SQLException {                        Spitter spitter = new Spitter();                        spitter.setId(rs.getLong(1));                        spitter.setUsername(rs.getString(2));                        spitter.setPassword(rs.getString(3));                        spitter.setEmail(rs.getString(4));                        return spitter;                    }                },                id);    }}

使用 JDBC 模板访问数据

只需要设置 DataSource 就能够让 NamedParameterJdbcTemplate 正常工作:

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">    <!-- 属性 dataSource 可以是 javax.sql.DataSource 的任意实现 -->    <constructor-arg ref="dataSource" /></bean>

将 jdbcTemplate 装配到 DAO 中并使用它来访问数据库。

public class JdbcSpitterDAO implements SpitterDAO{    //....    @Autowired    private NamedParameterJdbcTemplate jdbcTemplate;    //....}

使用 Spring 的 JDBC DAO 支持类
对于应用程序中的多个 JDBC DAO 类,如果我们都需要添加 jdbcTemplate 属性以及对应的 setter 方法,这就会产生大量的重复性工作。

一种可行的解决方案是为所有的 DAO 创建一个通用的父类,在区中会有 jdbcTemplate属性。

Spring 提供了内置的基类来设置 JDBC 模板(JdbcDaoSupport、SimpleJdbcDaoSupport 和 NamedParameterJdbcDaoSupport)。子类 DAO 类 继承相应基类后使用 getXxxTemplate() 方法获得 JDBC Template 对象。

另一种方法是,直接将数据源配置给自定义的 DAO 类,该自定义类需继承 Spring 提供的 JDBC DAO 支持类。这样就不需要在 Spring 配置文件中明确声明 XxxTemplate Bean 了。

阅读全文
0 0
原创粉丝点击