DAO:数据访问对象(data access object)的缩写。
如何你曾经编写过JDBC代码,你肯定会意识到如果不强制捕获SQLException,你几乎不能使用JDBC做任何事情。SQLException表示在尝试访问数据库时出现了问题,但是这个异常却没有告诉你哪里出错了以及如何进行处理。
可能导致抛出SQLException的常见问题包括:
1、应用程序无法连接数据库。
2、要执行的查询有语法错误。
3、查询中所使用的表和(或)列不存在。
4、试图插入或更新的数据违反了数据库的完整性约束。
它不是对每种可能的问题都会有不通的异常类型。而都是抛出SQLException。
一方面,JDBC的异常体系过于简单了---实际上,它算不上一个体系。另一方面,hibernate的异常体系是其本身所独有的。我们需要的是,数据访问异常要具有描述性而且又与特定的持久化框架无关。
spring几乎为读取和写入数据库的所有错误都提供了异常。Spring的数据访问异常很多。尽管Spring的异常体系比JDBC简单的SQLException丰富的多,但它没有与特定的持久化方式相关联。这意味着我们可以使用Spring抛出一致的异常,而不用关心所选择持久化方案。
Spring的异常大多继承自DataAccessException。这个异常的特殊指出在于它是一个非检查型异常。说白了不用写try catch。这把是否捕获异常的权利留给了开发人员。
Spring提供的数据访问模版,分别适用于不通的持久化机制。
下面分别是模版类和它的用途:
jca.cci.core.CciTemplate : JCA CCI连接
jdbc.core.JdbcTemplate : JDBC连接
jdbc.core.namedparam.NamedParameterJdbcTemplate : 支持命名参数的JDBC连接
jdbc.core.simple.SimpleJdbcTemplate : 通过java5简化后的JDBC连接
orm.hibernate.HibernateTemplate : Hibernate 2.x的Session
orm.hibernate3.HibernateTemplate : Hibernate 3.x的Session
orm.ibatis.SqlMapClientTemplate : iBATIS SqlMap客户端
orm.jdo.JdoTemplate : Java数据对象实现
orm.jpa.JpaTemplate : Java持久化API的实体管理器
配置数据源
1、使用JNDI数据源
Spring应用程序经常部署在Java EE应用服务器中,如WebSphere、JBoss或者像Tomcat这样的Web容器。这些服务器允许你配置通过JNDI获取数据源。这种配置的好处在于数据源完全可以在应用程序之外进行管理,这样应用程序只需在访问数据库的时候查找数据源就可以了。
使用<jee:jndi-lookup>元素装配到Spring中。
2、使用数据源连接池
前4个属性是配置BasicDataSource所必需的。下面列出了另外的最有用的一些属性。
initialSize:池启动时创建的连接数量。
maxActive:同一时间可从池中分配的最多连接数。如果设置为0,表示无限制。
maxIdle:池里不会被释放的最多空闲连接数。如果设置为0,则表示无限制。
maxOpenPreparedStatements:在同一时间能够从语句池中分配的预处理语句的最大数量。如果设置为0,表示无限制。
maxWait:在抛出异常之前,池等待连接回收的最大时间(当没有可用连接时)。如果设置为-1,表示无限等待。
minEvictableIdleTimeMillis:连接在池中保持空闲而不回收的最大时间。
minIdle:在不创建新连接的情况下,池中保持空闲的最小连接数。
poolPreparedStatements:是否对预处理语句进行池处理(布尔值)
3、基于JDBC驱动的数据源
在Spring中,通过JDBC驱动定义数据源是最简单的配置方式。Spring提供了两种数据源对象。
DriverManagerDataSource:在每个连接请求时都会返回一个新建的连接。与DBCP的BasicDataSource不同,由DriverManagerDataSource提供的连接兵没有进行池化管理。
SingleConnectionDataSource:在每个连接请求时都会返回同一个连接。尽管SingleConnectionDataSource不是严格意义上的连接池数据源,但是你可以将其视为只有一个连接的池。
以上两个数据源的配置与BasicDataSource的配置类似:
唯一的区别在于DriverManagerDataSource和SingleConnectionDataSource都没有提供连接池功能,所以没有可配置的池相关的属性。
SingleConnectionDataSource有且只有一个数据库连接,所以不适用于多线程的应用程序。尽管DriverManagerDataSource支持多线程,但是在每次请求连接时都会创建新连接,这是以性能为代价的。鉴于以上的这些限制,我强烈建议应该使用数据源连接池。
使用JDBC模版
Spring为JDBC框架承担了资源管理和异常处理的工作,从而简化了JDBC代码,让我们只需编写从数据库读写数据的必须代码。
Spring将数据访问的样板式代码提取到模版类中。Spring为JDBC提供了3个模版类供使用。
1、JdbcTemplate:最基本的Spring的JDBC模版,这个模版支持最简答你的JDBC数据库访问功能以及简单的索引参数。
2、NamedParameterJdbcTemplate:使用该模版类执行查询时,可以将查询值以命名参数的形式绑定到SQL中,而不是使用简单的索引参数。
3、SImpleJdbcTemplate:该模版类利用Java5的一些特性,例如自动装箱、泛型以及可变参数列表来简化JDBC模版的使用。
使用SImpleJdbcTemplate访问数据
现在,可以将jdbcTemplate装配到DAO中兵使用了 SImpleJdbcTemplate:
你还需要装配JdbcSpitterDAO的jdbcTemplate属性,如下:
下面是基于SimpleJdbcTemplate的addSpitter()方法。
使用JdbcTemplate也简化了数据读取操作。下面是个新方法,在这个方法中使用SimpleJdbcTemplate回调将结果集映射成域对象。
在getSpitterById()方法中使用了SimpleJdbcTemplate的queryForObject()方法来从数据库查询Spitter。queryForObject()方法有3个参数:
1、String,包含了要从数据库中查找数据的SQL。
2、parameterizedRowMapper对象,用来从ResultSet中提取值并构建域对象
3、可变参数列表,列出了要绑定到查询上的索引参数值。
使用命名参数
在上面的addSpitter()方法使用了索引参数。这意味着我们需要留意查询中参数的顺序,而在将值传递给update()方法的时候要保持正确的顺序。如果在修改SQL时更改了参数的顺序,那么我们还需要修改参数值的顺序。
除了这种方法之外,我们还可以使用命名参数。命名参数可以赋予SQL中的每个参数一个明确的名字,在绑定值到查询语句的时候就通过该名字来引用参数。例如:
使用命名参数查询,绑定值的顺序就不重要了,我们可以按照名字来绑定值。如果查询语句发生了变化导致参数的顺序域之前不一致,我们不需要修改绑定的代码。
使用SPring 的JDBC DAO支持类
对于应用程序中的每一个JDBC DAO类,我们都需要添加一个SimpleJdbcTemplate属性以及对应的setter方法,并确保将SimpleJdbcTemplate Bean装配到每个DAO的SimpleJdbcTemplate属性中。如果应用程序中只有一个DAO,这并不算什么问题,但是如果有多个DAO的话,这就会产生大量的重复工作。
一种可行的解决方案就是为所有的DAO创建一个通用的父类,在其中会有SimpleJdbcTemplate属性。然后让所有的DAO类继承这个类兵使用父类的SimpleJdbcTemplate进行访问。
Spring提供了内置的基类。(JdbcDaoSupport、SimpleJdbcDaoSupport和NamedParameterJdbcDaoSupported)
下面是例子:
SimpleJdbcDaoSupport通过getSimpleJdbcTemplate()能够便捷地访问SimpleJdbcTemplate。例如,addSpitter()方法可以这样改写:
在Spring中继承Hibernate
在使用过jdbc后,我们还需要一些复杂的特性:
1、延迟加载:我们可以只抓去需要的数据。
2、预先抓取:这与延迟加载是相对的,借助于预先抓去,我们可以使用一个查询获取完整的关联对象。如果需要PurchaseOrder及其关联的LineItem对象,预先抓去的功能可以在一个操作中将它们全部从数据库中提取出来,这节省了多次查询的成本。
3、级联:有时,更改数据库中的表会同时修改其他表。
SPring对多个持久化框架都提供了支持,包括Hibernate、iBATIS、JAVA数据对象(Java Data Objects,JDO)以及java持久化API。
与Spring对JDBC的支持那样,Spring对ORM框架的支持提供了与这些框架的集成点以及一些附加的服务,如下所示:
1、Spring声明式事务的集成支持
2、透明的异常处理
3、线程安全的、轻量级的模版类
4、DAO支持类
5、资源管理
声明Hibernate的Session工厂
使用Hibernate的主要接口是org.hibernate.Session。Session接口提供了基本的数据访问功能,如保存、更新、删除以及从数据库加载对象的功能。通过Hibernate的Session接口,应用程序的DAO能够满足所有的持久化需求。
获取Hibernate Session对象的标准方式是借助于Hibernate的SessionFactory接口的实现类。除了一些其他的任务,SessionFactory主要负责Hibernate Session的打开、关闭以及管理。
1、XML配置
2、注解配置
就像在LocalSessionFactoryBean中那样,dataSource和hibernateProperties属性声明了从哪里获取数据库连接以及要使用哪一种数据库。
这里不再列出Hibernate配置文件,而是使用packagesToScan属性告诉Spring扫描一个或多个包以查找域类,这些类通过注解方式表明要使用Hibernate进行持久化。使用JPA的@Entity或@MappedSuperclass注解以及Hibernate的@Entity注解进行标注的类都会包含在内。
如果愿意,我们还可以通过使用annotatedClassed属性来将应用程序中所有的持久化类以全限定名的方式明确列出:
annotatedClassed属性对于准确指定少量的域类是不错的选择。如果你有很多的域类且不想将其全部列出。或者你想自由地添加或溢出域类而不想修改Spring配置的话,则使用packagesToScan属性更合适。
构建不依赖于Spring的Hibernate代码
我们在类上使用了@Repository注解,这会为我们做两件事情。首先,@Repository是Spring的另一种构造型注解,它能够像其他注解一样被Spring的<context:component-scan>所扫描到。这样就不必明确声明HibernateSpitterDao Bean了,只需在<context:component-scan>配置即可。
除了帮助简化XML配置以外,@Repository还有另外一个用处。让我们回想一下模版类,它有一项任务就是捕获平台相关的异常,然后以Spring的非检查型异常形式重新抛出。如果我们使用Hibernate上下文Session而不是Hibernate模版,那么异常转换会怎么处理呢?
为了给不使用模版的Hibernate DAO添加异常转换功能,我们只需在Spring应用上下文中添加一个PersistenceExceptionTranslationPostProcessorBean:
PersistenceExceptionTranslationPostProcessor是一个Bean的后置处理程序,它会在所有拥有@Repository注解的类上添加一个通知器(advisor),这样就会捕获任何平台相关的异常并以Spring的非检查型数据访问异常的形式重新抛出。
Spring与Java持久化API
配置实体管理器工厂
简单来说,基于JPA的应用程序使用EntityManagerFactory的实现类来获取EntityManager实例。JPA定义了两种类型的实体管理器:
1、应用程序管理类型:当应用程序向实体管理器工厂直接请求实体管理器时,工厂会创建一个实体管理器。在这种模式下,程序要负责打开或关闭实体管理器并在事务中对其进行控制。这种方式的实体管理器适合于不运行在Java EE容器中的独立应用程序。
2、容器管理类型:实体管理器由Java EE创建和管理。应用程序根本不与实体管理器工厂打交道。相反,实体管理器直接通过注入或JNDI来获取。容器负责配置实体管理器工厂。这种类型的实体管理器最适合用于Java EE容器,在这种情况下会希望在persistence.xml指定的JPA配置之外保持一些自己对JPA的控制。
它们两个的区别在于EntityManager的创建和管理方式。应用程序管理类型的EntityManager是由EntityManagerFactory创建的。而后者是通过PersistenceProvider的createEntityManagerFactory()方法得到的。与此相对,容器管理类型的entityManagerFactory是通过PersistenceProvider的createContainerEntityManagerFactory()方法获得的。
在容器管理的场景下,SPring会担当容器的角色。
这两种实体管理器工厂分别由对应的Spring工厂Bean创建的:
1、LocalEntityManagerFactoryBean生成应用程序管理类型的EntityManagerFactory。
2、LocalContainerEntityManagerFactoryBean生成容器管理类型的EntityManagerFactory。
使用应用程序管理类型的JPA
对于应用程序管理类型的实体管理器工厂来说,它绝大部分配置文件来源于一个名叫persistence.xml的配置文件。这个文件必须位于类路径下的META-INF目录下。
persistence.xml的作用在于定义一个或多个持久化单元。持久化单元是同一个数据源下的一个或多个持久化类。简单来讲,persistence.xml列出了一个或多个的持久化类以及一些其他的配置,如数据源和基于XML的配置文件。以下是一个典型的persistence.xml文件,它用于Spitter应用程序。
因为在persistence.xml文件中包含了大量的配置信息,所以在Spring中需要配置的就很少了。可以通过以下的<bean>元素在Spring中声明LocalEntityManagerFactoryBean:
赋给persistenceUnitName属性的值就是persistence.xml中持久化单元的名称。
使用容器管理类型的JPA
容器管理的JPA采取了一种不同的方式。当在容器中运行时,可以使用容器提供的信息来生成EntityManagerFactory。
你可以将数据源信息配置在Spring应用上下文中,而不是在persistence.xml中了。例如,下面的<bean>声明展现了在Spring中如何使用LocalContainerEntityManagerFactoryBean来配置容器管理类型的JPA。
jpaVendorAdapter属性用于指明所使用的是哪一个厂商的JPA实现。Spring提供了多个JPA厂商适配器:
1、EclipseLinkJpaVendorAdapter
2、HibernateJpaVendorAdapter
3、OpenJpaVendorAdapter
4、TopLinkJpaVendorAdapter