【Spring】【笔记】《Spring In Action》第5章 数据库处理

5.1 Spring数据访问原理
     DAO 数据访问对象(data access object)。
     DAO提供了数据读取和写入到数据库中的一种方式。他们应该以接口的方式发布功能,而应用程序的其他部分就可以通过接口来进行访问了。
     实现了松耦合代码

5.1.1 Spring数据访问异常体系
     不与特定的持久化方式相关联。这意味着可以使用Spring抛出一致的异常,而不用关心所选择的持久化方案。

Spring的数据访问异常:
  • CannotAcquireLockException
  • CannotSerializeTransactionException
  • CleanupFailureDataAccessException
  • ConcurrencyFailureException
  • DataAccessException
  • DataAccessResourceFailureException
  • DataIntegrityViolationException
  • DataRetrievalFailureException
  • DeadlockLoserDataAccessException
  • EmptyResultDataAccessException
  • IncorrectResultSizeDataAceessException
  • InvalidDataAccessApiUsageException
  • InvalidDataAccessResourceUsageException
  • OptimisticLockingFailureException
  • PermissionDeniedDataAccessException
  • PessimisticLockingFailureException
  • PessimisticLockingFailureException
  • TypeMismatchDataAccessException
  • UncategorizedDataAccessException
     ...
     这些异常都继承自DataAccessException,DataAccessException是一个非检查型异常,没有必要去捕获Spring所抛出的数据访问异常,这是因为Spring认为触发异常的很多问题是不能在catch代码块中修复的。因此开发人员可以选择是否编写catch代码块。

5.1.2 数据访问模板化
     模板方法模式:定义了过程的主要框架,将过程中与特点实现相关的部分委托给接口,而这个接口的不同实现定义了过程中的具体行为。
     Spring在数据访问中使用的就是模板方法模式。
     Spring将数据访问过程中固定的和可变的部分明确划分为两个不同的类:模板 template 、回调 callback,模板管理过程中固定的部分,回调处理自定义的数据访问代码。
     
     Spring的处理逻辑
DAO模块 DAO回调
1.准备资源
2.开始事务
3.在事务中执行
5.提交/回滚事务
6.关闭资源和处理错误
4.返回数据
     Spring的模板类处理数据访问的固定部分——事务控制、管理资源、异常处理。应用程序相关的数据访问(创建语句、绑定参数以及整理结果集)在回调的实现中处理

     针对不同的持久化平台,Spring提供了多个可选的模板
模板类(org.springframework.*) 用途
jca.cci.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.JpaTemplate Java持久化API的实体管理器
     使用数据访问模板只需将其配置为Spring上下文中的Bean并将其织入到应用程序的DAO中。或者使用Spring的DAO支持类进一步简化应用程序的DAO配置。

5.1.3 DAO支持类
     基于模板-回调设计,Spring提供了DAO支持类,而将业务自己的DAO类作为它的子类。当编写应用程序自己的DAO实现时,可以继承自DAO支持类并调用模板获取方法来直接访问底层的数据访问模板。Spring不仅提供了多个数据模板实现类,还为每种模板提供了对应的DAO支持类。
DAO支持类(org.springframework.*) 为谁提供DAO支持
jca.cci.support.CcciDaoSupport JCA CCI连接
jdbc.core.support.JdbcDaoSupport JDBC连接
jdbc.core.namedparam.NamedParameterJdbcDaoSupport 带有命名参数的JDBC连接
jdbc.core.simple.SimpleJdbcDaoSupport 用Java5进行简化的JDBC连接
orm.hibernate.support.HibernateDaoSupport Hibernate2.x的Session
orm.hibernate3.support.HibernateDaoSupport Hibernate3.x的Session
orm.ibatis.support.SqlMapClientDaoSupport iBaTIS SqlMap客户端
orm.jdo.support.JdoDaoSupport Java数据对象(Java Data Object)实现
orm.jpa.support.JpaDaoiSupport Java持久化API的试题管理器

5.2 配置数据源
     无论选择哪一种SpringDAO的支持方式,都需要配置一个数据源的引用。Spring提供了在Spring上下文中配置数据源Bean的多种方式,包括:
  • 通过JDBC驱动程序定义的数据源
  • 通过JNDI查找的数据源
  • 连接池的数据源

5.2.1 使用JNDI数据源   
      JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互
     完全可以在应用程序之外对数据源进行管理,应用程序只需在访问数据库的时候查找数据源就可以了。
     利用Spring,可以像使用SpringBean那样配置JNDI中数据源的引用,并将其装配到需要的类中。jee命名空间下的<jee:jndi-lookup>元素可以用于检索JNDI中的任何对象包括数据源,并将其用于SpringBean中。
eg:  <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并没有提供数据源连接池实现

     Spring推荐使用DBCP(Jakarta Commons Database Connection Pooling)
所需jar:commons-dbcp.jar、commons-pool.jar
     DBCP包含了多个提供连接池功能的数据源,其中BasicDataSource是最常用的,因为它易于在Spring中配置,而且类似于Spring自带的DriverManagerDataSource。
     例如:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
     <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
     <property name="url" value="jdbc:hsqldb:hsql://localhost/spitter/spitter"/>
     <property name="username" value="sa"/>
     <property name="password" value=""/>
     <property name="initialSize" value="5"/>
     <property name="maxActive" value="10"/>
</bean>
     前4个属性是配置BasicDataSource所必须的。属性driverClassName制定了JDBC驱动类的全限定类名。此外还有多个属性用来配置数据源连接池。
池配置属性 所指定的内容
initialSize 池启动时创建的连接数量
maxActive 同一时间可从池中分配的最多连接数,设置为0表示无限制
maxIdle 池里不会被释放的最多控线连接,设置为0表示无限制
maxOpenPreparedStatements 在同一时间能够从语句池中分配的预处理语句的最大数量,设置为0表示无限制
maxWait 在抛出 异常之前,池等待连接回收的最大时间(当没有可用连接时)。如果设置为-1,表示无限等待
minEvictableIdleTimeMillis 连接在池中保持空闲而不被回收的最大时间
minIdle 在不创建新连接的情况下,池中保持空闲的最小连接数
poolPreparedStatements 是否对预处理语句进行池管理(布尔值)

5.2.3基于JDBC驱动的数据源
     Spring提供了两种数据源对象,均位于org.springframework.jdbc.datasource
  • DriverManagerDataSource:在每个连接请求时都会返回一个新建的连接。与DBCP的BasicDataSource不同,由DriverManagerDataSource提供的连接并没有进行池化管理
  • SingleConnectionDataSource:在每个连接请求时都会返回同一个连接。可以视为只有一个连接的池,但不是严格意义上的连接池数据源
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
     <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
     <property name="url" value="jdbc:hsqldb:hsql://localhost/spitter/spitter"/>
     <property name="username" value="sa"/>
     <property name="password" value=""/>
</bean>
     SingleConnectionDataSource有且只有一个数据库连接,所以不适用于多线程的应用程序。尽管DriverManagerDataSource支持多线程,但是在每次请求连接时都会创建新连接,这是以性能为代价的。因此推荐使用数据源连接池。

5.3 在Spring中使用JDBC
     
5.3.1 应对失控的JDBC代码
     大致就是JDBC代码太长了、很麻烦、但很重要等等等等……

5.3.2 使用JDBC模板
     Spring为JDBC提供了3个模板类供使用
  • JDBCTemplate:最基本的Spring JDBC模板,这个模板支持最简单的JDBC数据库访问功能以及简单的索引参数查询
  • NamedParameterJdbcTemplate:使用该模板类执行查询时,可以将查询值以命名参数的形式绑定到SQL中,而不是使用简单的索引参数
  • SimpleJdbctemplate:该模板类利用Java 5的一些特性,如自动装箱、泛型以及可变参数列表来简化JDBC模板的使用。
     Spring3.0之后SimpleJdbcTemplate是最好的选择

使用SimpleJdbcTemplate访问数据
      在Spring4.2中,已经没有SimpleJdbcTemplate了。
     只需要设置DataSource就能够让SimpleJdbcTemplate正常工作。
     Spring中配置SimpleJdbcTemplate
< bean id= "jdbcTemplate" class= "org.springframework.jdbc.core.simple.SimpleJdbcTemplate" >
     <constructor-arg ref = "dataSource"/>
</bean >
     属性dataSource所引用的dataSource可以是javax.sql.DataSource的任意实现

使用命名参数
     命名参数可以赋予SQL中的每个参数一个明确的名字,在绑定值到查询语句的时候就通过该名字来引用参数。

使用Spring的JDBC DAO支持类
      在Spring4.2中,已经没有SimpleJdbcTemplateDaoSupport
     DaoSupport类就是把每一个Dao类需要完成的相同工作抽离出来,形成一个父类,让所有的dao类去继承它,减少重复代码。

5.4 在Spring中集成Hibernate
  • 延迟加载(Lazy loading):借助于延迟加载,可以只抓取需要的数据。
  • 预先抓取(Eager fetching):与延迟加载相对。借助预先抓取,可以使用一个查询获取完整的关联对象。
  • 级联(Cascading):更改数据库中的表会同时修改其他表
     ORM框架(对象/关系映射 object-relational mapping,ORM)提供了这些服务。在持久层使用ORM工具可以节省代码数量和开发时间。
     Spring对对多个持久化框架都提供了支持,包括Hibernate、iBATIS、JDO、Java持久化API。
     与Spring对JDBC的支持那样,Spring对ORM框架的支持提供了与这些框架的集成店以及一些附加的服务:
  • Spring声明式事务的继承支持
  • 透明的异常处理
  • 线程安全的、轻量级的模板类
  • DAO支持类
  • 资源管理

5.4.1 Hibernate概览
     以前,在Spring应用程序中使用Hibernate是通过HibernateTemplate进行的,其职责之一是管理Hibernate的Session。HibernateTemplate的不足之处在于存在一定程度的侵入性。在DAO中使用HibernateTemplate(无论是直接使用还是通过HibernateDaoSupport)时,DAO类就会与SpringAPI产生了耦合。
     Hibernate3中引入了上下文Session(Contextual session),这是Hibernate本身所提供的保证每个事务使用同一Session的方案,因此没有必要再使用HibernateTemplate来保证这一行为。这种方式可以让自定义的DAO类不包含特定的Spring代码。
     
5.4.2 声明Hibernate的Session工厂
     使用Hibernate的主要接口是org.hibernate.Session,Session接口提供了基本的数据访问功能,通过Hibernate的Session接口,应用程序的DAO能够满足所有的持久化需求。
     获取Hibernate Session对象的标准方式是借助于Hibernate的SessionFactory接口的实现类。除了一些其他的任务,SessionFactory主要负责HibernateSession的打开、关闭以及管理。
     在Spring中,通过Spring的某一个Hibernate Session 工厂Bean来获取Hibernate的SessionFactory。可以在Spring上下文中,像配置其他Bean那样来配置Hibernate Session工厂。配置是要确定持久化域对象时通过XML文件还是通过注解来进行配置的。如果选择在XML中定义对象与数据库之间的映射,那么需要在Spring中配置LocalSessionFactoryBean

<bean id="sessionFactory"
     class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
     <property name="dataSource" ref="dataSource"/>
     <property name="mappingResources">
          <list>
               <value>Spitter.hbm.xml</value>
          </list>
     </property>
     <property name="hibernateProperties">
          <props>
               <prop key="dialect">org.hibernate.dialect.HSQLDialect</prop>
          </props>
     </property>
</bean>
     dataSource装配了一个DataSource Bean的引用。
     mappingResources装配了一个或多个的Hibernate映射文件,在这些文件中定义了应用程序的持久化策略。    
     hibernateProperties属性配置了Hibernate如何进行操作的细节。

     如果使用注解的方式来定义持久化,那需要使用AnnotationSessionFactoryBean来代替LocalSessionFactoryBean
     < bean id= "sessionFactory"
          class= "org.springframework.orm.hibernate3.annotaion.AnnotationSessionFactoryBean" >
            <property name = "dataSource"      ref= "dataSource" />
            <property name = "packagesToScan" value= "com.habuma.spitter.domain" />
            <property name = "hibernateProperties">
                 <props >
                      <prop key= "dialect" >org.hibernate.dialect.HSQLDialect </prop >
                 </props >
            </property >
      </bean >
     dataSource和hibernateProperties属性生命了从哪里获取数据库连接以及要使用哪一种数据库。package说ToScan告诉Spring扫描一个或多个包以查找域类,这些类通过注解方式表明要使用Hibernate进行持久化。使用JPA的@Entity或@MappedSuperclass注解以及Hibernate的@Entity注解进行标注的类都会包含在内。
     AnnotationSessionFactoryBean的package说ToScan属性使用一个String所组成的数组来表明要在那些包中查找域类。一般情况下会使用列表(<property name="pcakagesToScan"> <list> <value>).还可以通过使用annotatedClasses属性来将应用程序中所有的持久化类以权限定名的方式明确列出。

5.4.3 构建不依赖于Spring的Hibernate代码
     没有上下文Session之时,Spring的Hibernate模板用于保证每个事务使用同一个Session。既然Hibernate自己能够对其进行管理,那就没有必要使用模板类了。这意味着,可以直接将HibernateSession装配到DAO类中。
     
import java.util.List;
import org.hibernate.SessionFactory;
import org.hibernate.classic.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.habuma.spitter.domain.Spitter;
import com.habuma.spitter.domain.Spittle;

@Repository
public class HibernateSpitterDao implements SpitterDao {
    private SessionFactory sessionFacotory;

    @Autowired
    public HibernateSpitterDao(SessionFacoty sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
    
    private Session currentSession() {
        return sessionFactory.getCurrentSession();
    }

    public void addSpitter(Spitter spitter) {
        currentSession().save(spitter);
    }

    public Spitter getSpitterById(long id) {
        return (Spitter) currentSession().get(Spitter.class,id);
    }

    public void saveSpitter(Spitter spitter) {
        currentSession().update(spitter);
    }
    ...
}
     使用@Autowire的可以让Spring自动将一个SessionFactory注入到HibernateSpi忐忑日Dao的sessionFactory属性。在currentSession()方法中,使用这个SessionF擦头人员来获取当前事务的Session。
    @Repository注解:首先,@Repository是Spring的一种构造型注解,可以被Spring的<context:component-scan>扫描到,因此可不用去声明这个Bean,只需配置<context:component-scan>的base-package属性为相应的包就可以了。
     为了给不适用模板的HibernateDAO添加异常转换功能,需要在Spring应用上下文添加一个PersistenceExceptionTranslationPostProcessor  Bean。

<bean class="org.springframework.dao.annotaion.PersistenceExceptionTranslationPostProcessor"/>
     它是一个Bean的后置处理程序,会在所有拥有@Repository注解的类上添加一个通知器(advisor),这样就会补货任何平台相关的异常,并以Spring的非检查型数据访问异常的形式重新抛出。
     
 
5.5 Spring与Java持久化API
     JPA是基于POJO的持久化机制,从Hibernate和Java数据对象上借鉴了很多理念并加入了Java5注解的特性。

5.5.1 配置实体管理器工厂
     基于JPA的应用程序使用EntityManagerfactory的实现类来获取EntityManager实例。JPA定义了两种类型的实体管理器:
  • 应用程序管理类型(Application-managed):当应用程序向实体管理器工厂直接请求实体管理器时,工厂会创建一个实体管理器。在这种模式下,程序要负责打开或关闭试题管理器并在事务中对其进行控制。这种方式的试题管理器适合于不运行在Java EE容器中的独立应用程序。
  • 容器管理类型(Container-managed):试题管理器由JavaEE创建和管理。应用程序根本不予试题管理器工厂打交道。相反试题管理器直接通过注入或JNDI来获取。容器负责配置实体管理器工厂。这种类型的实体管理器最适合用于JavaEE容器,在这种情况下会希望在persistence.xml指定的JPA配置之外保持一些自己对JPA的控制。
     两者都实现了EntityManager接口。区别在于EntityManager的创建和管理方式。无论使用哪一种EntityManagerFactory,Spring都会负责管理Entity-Manager。如果使用应用程序管理类型的实体管理器,Spring承担了应用程序的角色并以透明的方式处理EntityManager,在容器管理的场景下,Spring会担当容器的角色。
     创建这两种试题管理器工厂的Spring工厂Bean:
  • LocalEntityManagerFactoryBean生成应用程序管理类型的EntityManagerFactory;
  • LocalContainerEntityManagerFactoryBean生成容器管理类型的EntityManagerFactory

使用应用程序管理类型的JPA
     绝大部分配置文件来源于一个名为persistence.xml的配置文件。 这个文件必须位于类路径下的META-INF目录下。persistence.xml的作用在于定义一个或多个持久化单元。持久化单元是同一个数据源下的一个或多个持久化类
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version=1.0>
    <persistence-unit name="spitterPU">
        <class>com.habuma.spitter.domain.Spitter</class>
        <class>com.habuma.spitter.domain.Spittle</class>
        <properties>
            <property name="toplink.jdbc.driver" value="org.hsqldb.jdbcDriver"/>
            <property name="toplink.jdbc.url" value="jdbc:hsqldb:hsql://localhost/spitter/spitter"/>
            <property name="toplink.jdbc.user" value="sa"/>
            <property name="toplink.jdbc.password" value=""/>
        </properties>
    </persistence-unit>
</persistence>
     Spring中要配置的内容很少
<bean id="emf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="spitterPU"/>
</bean>
     persistenceUnitName的值就是在Persistence.xml中配置的持久化单元的名字

使用容器管理类型的JPA(相对较好)
     当在容器中运行时,使用容器提供的信息来生成EntityManagerFactory。当我们使用Spring时,就可以在Spring的应用上下文中配置JPA了。
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
</bean>
     datasource可以是任何javax.sql.DataSource的实现
     jpaVendorAdapter属性用于致命所使用的是哪一个厂商的JPA实现。Spring提供了多个JPA厂商的适配器:
  • EclipseLinkJpaVendorAdapter
  • HibernateJpaVendorAdapter
  • OpenJpaVendorAdapter
  • TopLinkJpaVendorAdapter
     使用Hibernate的例子:
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
     <property name="database" value="HSQL"/>
     <property name="showSql" value="true"/>
     <property name="generateDdl" value="false"/>
     <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect"/>
</bean>

Hibernate的JPA适配器的数据库
数据库平台 属性database的值
IBM DB2 DB2
Apache Derby DERBY
H2 H2
Hypersonic HSQL
Informix INFORMIX
MySQL MYSQL
Oracle ORACLE
PostgresQL POSTGRESQL
Microsoft SQL Server SQLSERVER
Sybase SYBASE

从JNDI获取实体管理器工厂
     如果将Spring应用程序部署在应用服务器中,Spring可能已经创建了EntityManagerFactory并将其置于JNDI中等待查询使用。这种情况下,可以使用Spring的jee命名空间下的<jee:jndi-lookup>元素来获取对EntityManagerFactory 的引用:
<jee:jndi-lookup id="emf" jndi-name="persistence/spitterPU"/>

5.5.2 编写基于JPA的DAO
     Spring对JPA继承也提供了JpaTemplate模板以及对应的支持类JpaDaoSupport。但是基于模板的JPA已经被弃用了(类似Hibernate)。
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Persistencecontext;

import org.springframework.dao.DataaccessException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotaion.Transactional;

@Repository("spitterDao")
@Transactional
public class JpaspitterDao implements SpitterDao {
     private static final String RECENT_SPITTLES = "SELECT s FROM Spittle s";
     private static final String ALL_SPITTERS = "SELECT s FROM Spitter s";
     private static final String SPITTER_FOR_USERNAME = "SELECT s FROM Spitter s WHERE s.username = :username";
     private static final String SPITTLES_BY_USERNAME = "SELECT S FROM Spittle s WHERE s.spitter.username = :username";

     @PersistenceContext
     private EntityManager em;
     
     public void addSpitter(Spitter spitter) {
          em.persist(spitter);
     }

     public Spitter getSpitterById(long id) {
          return em.find(Spitter.class,id);
     }

     public void saveSpitter(Spitter spitter) {
          em.merge(spitter);
     }
}

     @PersistenceContext注解表明需要将一个EntityManager注入到所标注的成员上。为了在Spring中实现EntityManager注入,需要在Spring应用上下文中配置一个PersistenceAnotationBeanPostProcessor
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
     @Repository与开发Hibernate上下文Session的DAO是一只的。由于没有模板类来处理异常,所以需要为DAO添加@Repository注解,这样PersistenceExceptionTranslationPostProcessor就会知道要将这个Bean产生的异常转换成Spring的同一数据访问。要使用PersistenceExceptionTranslationPostProcessor,需要将其作为一个Bean装配到spring中
<bean class="org.springframework.dao.annotaion.PersistenceExceptionTranslationPostProcessor"/>



第五章小结
     Spring对JDBC和ORM的框架的支持简化了各种持久化机制都存在的样板代码,这是我们只需关注与应用程序相关的数据访问即可。
     Spring简化数据访问方式之一就是管理数据库连接的生命周期和ORM框架的Session。通过这种方式,持久化机制的管理对应用程序代码时完全透明的。
     Spring能够捕获框架的特定异常并将其转换成异常体系中的非检查型异常。包括将JDBC抛出的SQLException转换为含义更丰富的异常






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值