目录搜索
前言简介概览使用场景Spring 2.0和 2.5的新特性简介控制反转(IoC)容器新的bean作用域更简单的XML配置可扩展的XML编写Annotation(注解)驱动配置在classpath中自动搜索组件面向切面编程(AOP)更加简单的AOP XML配置对@AspectJ 切面的支持对bean命名pointcut( bean name pointcut element)的支持对AspectJ装载时织入(AspectJ load-time weaving)的支持中间层在XML里更为简单的声明性事务配置对Websphere 事务管理的完整支持JPA异步的JMSJDBCWeb层Spring MVC合理的默认值Portlet 框架基于Annotation的控制器Spring MVC的表单标签库对Tiles 2 支持对JSF 1.2支持JAX-WS支持其他动态语言支持增强的测试支持JMX 支持将Spring 应用程序上下文部署为JCA adapter计划任务对Java 5 (Tiger) 支持移植到Spring 2.5改变支持的JDK版本Spring 2.5的Jar打包XML配置Deprecated(淘汰)的类和方法Apache OJBiBATISHibernateJDOUrlFilenameViewController更新的样例应用改进的文档核心技术IoC(控制反转)容器简介基本原理 - 容器和bean容器配置元数据实例化容器XML配置元数据的结构多种beanbean的命名bean的别名实例化bean用构造器来实例化使用静态工厂方法实例化使用实例工厂方法实例化使用容器依赖注入依赖构造器注入构造器参数解析构造器参数类型匹配构造参数索引Setter注入一些例子依赖配置详解直接变量(基本类型、Strings类型等。)idref元素引用其它的bean(协作者)内部bean集合集合的合并强类型集合(仅适用于Java5+)NullsXML配置文件的简写及其他XML-based configuration metadata shortcuts使用p名称空间配置属性组合属性名称使用depends-on延迟初始化bean自动装配(autowire)协作者将bean排除在自动装配之外依赖检查方法注入Lookup方法注入自定义方法的替代方案Bean的作用域Singleton作用域Prototype作用域Singleton beans和prototype-bean的依赖其他作用域初始化web配置Request作用域Session作用域global session作用域作用域bean与依赖选择创建代理的类型自定义作用域创建自定义作用域使用自定义作用域定制bean特性生命周期回调初始化回调析构回调缺省的初始化和析构方法组合生命周期机制在非web应用中优雅地关闭Spring IoC容器了解自己BeanFactoryAwareBeanNameAwarebean定义的继承容器扩展点用BeanPostProcessor定制bean使用BeanPostProcessor的Hello World示例RequiredAnnotationBeanPostProcessor示例用BeanFactoryPostProcessor定制配置元数据PropertyPlaceholderConfigurer示例PropertyOverrideConfigurer示例使用FactoryBean定制实例化逻辑The ApplicationContextBeanFactory 还是 ApplicationContext?利用MessageSource实现国际化事件底层资源的访问ApplicationContext在WEB应用中的实例化粘合代码和可怕的singleton以J2EE RAR文件的形式部署Spring ApplicationContext基于注解(Annotation-based)的配置@Autowired基于注解的自动连接微调CustomAutowireConfigurer@Resource@PostConstruct 与 @PreDestroy对受管组件的Classpath扫描@Component和更多典型化注解自动检测组件使用过滤器自定义扫描自动检测组件的命名为自动检测的组件提供一个作用域用注解提供限定符元数据注册一个LoadTimeWeaver资源简介Resource接口内置 Resource 实现UrlResourceClassPathResourceFileSystemResourceServletContextResourceInputStreamResourceByteArrayResourceResourceLoader接口ResourceLoaderAware 接口把Resource作为属性来配置Application context 和Resource 路径构造application context创建 ClassPathXmlApplicationContext 实例 - 简介Application context构造器中资源路径的通配符Ant风格的pattern潜在的可移植性classpath*: 前缀其他关于通配符的说明FileSystemResource 说明校验,数据绑定,BeanWrapper,与属性编辑器简介使用Spring的Validator接口进行校验从错误代码到错误信息Bean处理和BeanWrapper设置和获取属性值以及嵌套属性内建的PropertyEditor实现注册用户自定义的PropertyEditor使用PropertyEditorRegistrars使用Spring进行面向切面编程(AOP)简介AOP概念Spring AOP的功能和目标AOP代理@AspectJ支持启用@AspectJ支持声明一个切面声明一个切入点(pointcut)切入点指示符(PCD)的支持组合切入点表达式共享通用切入点定义示例声明通知前置通知后置通知(After returning advice)异常通知(After throwing advice)最终通知(After (finally) advice)环绕通知通知参数(Advice parameters)访问当前的连接点传递参数给通知确定参数名处理参数通知顺序引入(Introduction)切面实例化模型例子基于Schema的AOP支持声明一个切面声明一个切入点声明通知前置通知后置通知异常通知最终通知环绕通知通知参数通知顺序引入切面实例化模型Advisor例子AOP声明风格的选择Spring AOP还是完全用AspectJ?Spring AOP中使用@AspectJ还是XML?混合切面类型代理机制理解AOP代理以编程方式创建@AspectJ代理在Spring应用中使用AspectJ在Spring中使用AspectJ进行domain object的依赖注入@Configurable对象的单元测试Working with multiple application contextsSpring中其他的AspectJ切面使用Spring IoC来配置AspectJ的切面在Spring应用中使用AspectJ加载时织入(LTW)第一个例子切面'META-INF/aop.xml'相关类库(JARS)Spring配置特定环境的配置通用Java应用TomcatWebLogic更多资源Spring AOP APIs简介Spring中的切入点API概念切入点运算AspectJ切入点表达式便利的切入点实现静态切入点正则表达式切入点属性驱动的切入点动态切入点控制流切入点切入点的超类自定义切入点Spring的通知API通知的生命周期Spring里的通知类型拦截环绕通知前置通知异常通知后置通知引入通知Spring里的Advisor API使用ProxyFactoryBean创建AOP代理基础JavaBean属性基于JDK和CGLIB的代理对接口进行代理对类进行代理使用“全局”通知器简化代理定义使用ProxyFactory通过编程创建AOP代理操作被通知对象使用“自动代理(autoproxy)”功能自动代理bean定义BeanNameAutoProxyCreatorDefaultAdvisorAutoProxyCreatorAbstractAdvisorAutoProxyCreator使用元数据驱动的自动代理使用TargetSource热交换目标源池化目标源原型目标源ThreadLocal目标源定义新的Advice类型更多资源测试简介单元测试Mock对象JNDIServlet APIPortlet API单元测试支持类通用工具类Spring MVC集成测试概览使用哪个支持框架通用目标上下文管理及缓存测试fixtures依赖注入事务管理集成测试支持类JDBC测试支持常用注解JUnit 3.8遗留支持上下文管理及缓存测试fixture依赖注入字段级别(Field Level)注入事务管理JUnit 3.8 遗留支持类Java 5+ 专有支持使用注解的事务相关测试JPA支持类Spring TestContext Framework主要的抽象上下文管理和缓存测试fixture的依赖注入事务管理TestContext支持类JUnit 3.8支持类JUnit 4.4支持类定制JUnit 4.4运行器TestNG支持类TestContext框架注解支持PetClinic示例更多资源中间层数据访问事务管理简介动机关键抽象使用资源同步的事务高层次方案低层次方案TransactionAwareDataSourceProxy声明式事务管理理解Spring的声明式事务管理实现第一个例子回滚为不同的bean配置不同的事务语义<tx:advice/> 有关的设置使用 @Transactional@Transactional 有关的设置事务传播requiredRequiresNewNested通知事务操作结合AspectJ使用 @Transactional编程式事务管理使用TransactionTemplate指定事务设置使用PlatformTransactionManager选择编程式事务管理还是声明式事务管理与特定应用服务器集成IBM WebSphereBEA WebLogicOracle OC4J常见问题的解决方法对一个特定的 DataSource 使用了错误的事务管理器更多的资源DAO支持简介一致的异常层次一致的DAO支持抽象类使用JDBC进行数据访问简介选择一种工作模式Spring JDBC包结构利用JDBC核心类控制JDBC的基本操作和错误处理JdbcTemplate类一些示例查询(SELECT)更新(INSERT/UPDATE/DELETE)其他操作JdbcTemplate 的最佳实践NamedParameterJdbcTemplate类SimpleJdbcTemplate类DataSource接口SQLExceptionTranslator接口执行SQL语句执行查询更新数据库获取自动生成的主键控制数据库连接DataSourceUtils类SmartDataSource接口AbstractDataSource类SingleConnectionDataSource类DriverManagerDataSource类TransactionAwareDataSourceProxy类DataSourceTransactionManager类NativeJdbcExtractorJDBC批量操作使用JdbcTemplate进行批量操作使用SimpleJdbcTemplate进行批量操作通过使用SimpleJdbc类简化JDBC操作使用SimpleJdbcInsert插入数据使用SimpleJdbcInsert来获取自动生成的主键指定SimpleJdbcInsert所使用的字段使用SqlParameterSource提供参数值使用SimpleJdbcCall调用存储过程声明SimpleJdbcCall使用的参数如何定义SqlParameters使用SimpleJdbcCall调用内置函数使用SimpleJdbcCall返回的ResultSet/REF Cursor用Java对象来表达JDBC操作SqlQuery类MappingSqlQuery类SqlUpdate类StoredProcedure类SqlFunction类参数和数据处理的基本原则为参数设置SQL类型信息处理BLOB 和 CLOB对象在IN语句中传入一组参数值处理复杂类型的存储过程调用使用ORM工具进行数据访问简介Hibernate资源管理在Spring容器中创建 SessionFactoryThe HibernateTemplate不使用回调的基于Spring的DAO实现基于Hibernate3的原生API实现DAO编程式的事务划分声明式的事务划分事务管理策略容器资源 vs 本地资源在应用服务器中使用Hibernate的注意事项JDO建立PersistenceManagerFactoryJdoTemplate和JdoDaoSupport基于原生的JDO API实现DAO事务管理JdoDialectOracle TopLinkSessionFactory 抽象层TopLinkTemplate and TopLinkDaoSupport基于原生的TopLink API的DAO实现事务管理iBATIS SQL Maps创建SqlMapClient使用 SqlMapClientTemplate 和 SqlMapClientDaoSupport基于原生的iBATIS API的DAO实现JPA在Spring环境中建立JPALocalEntityManagerFactoryBean从JNDI中获取 EntityManagerFactoryLocalContainerEntityManagerFactoryBeanTomcat(5.0以上)加载时的织入配置使用VM代理的全局加载时织入上下文范围内的加载时织入配置处理多持久化单元JpaTemplate 和 JpaDaoSupport基于原生的JPA实现DAO异常转化事务管理JpaDialectThe WebWeb MVC framework Web框架概述与其他MVC实现框架的集成Spring Web MVC框架的特点DispatcherServlet控制器AbstractController 和 WebContentGenerator其它的简单控制器MultiActionController命令控制器处理器映射(handler mapping)BeanNameUrlHandlerMappingSimpleUrlHandlerMapping拦截器(HandlerInterceptor)视图与视图解析视图解析器(ViewResolver)视图解析链重定向(Rediret)到另一个视图RedirectViewredirect:前缀forward:前缀本地化解析器AcceptHeaderLocaleResolverCookieLocaleResolverSessionLocaleResolverLocaleChangeInterceptor使用主题简介如何定义主题主题解析器Spring对分段文件上传(multipart file upload)的支持介绍使用MultipartResolver在表单中处理分段文件上传使用Spring的表单标签库配置form标签input标签checkbox标签checkboxes标签radiobutton标签radiobuttons标签password标签select标签option标签options标签textarea标签hidden标签errors标签处理异常惯例优先原则(convention over configuration)对控制器的支持:ControllerClassNameHandlerMapping对模型的支持:ModelMap(ModelAndView)对视图的支持:RequestToViewNameTranslator基于注解的控制器配置建立dispatcher实现注解支持使用@Controller定义一个控制器使用@RequestMapping映射请求使用@RequestParam绑定请求参数到方法参数使用@ModelAttribute提供一个从模型到数据的链接使用@SessionAttributes指定存储在会话中的属性自定义WebDataBinder初始化使用@InitBinder自定义数据绑定配置一个定制的WebBindingInitializer更多资源集成视图技术简介JSP和JSTL视图解析器'Plain-old' JSPs versus JSTL 'Plain-old' JSP与JSTL帮助简化开发的额外的标签Tiles需要的资源如何集成TilesUrlBasedViewResolver类ResourceBundleViewResolver类SimpleSpringPreparerFactory 和 SpringBeanPreparerFactoryVelocity和FreeMarker需要的资源Context 配置创建模板高级配置velocity.propertiesFreeMarker绑定支持和表单处理用于绑定的宏简单绑定表单输入生成宏输入域选择输入域重载HTML转码行为并使你的标签符合XHTMLXSLT写在段首Bean 定义标准MVC控制器代码把模型数据转化为XML定义视图属性文档转换小结文档视图(PDF/Excel)简介配置和安装文档视图定义Controller 代码Excel视图子类PDF视图子类JasperReports依赖的资源配置配置ViewResolver配置View关于报表文件使用 JasperReportsMultiFormatView构造ModelAndView使用子报表配置子报表文件配置子报表数据源配置Exporter的参数集成其它Web框架简介通用配置JavaServer FacesDelegatingVariableResolverFacesContextUtilsStrutsContextLoaderPluginDelegatingRequestProcessorDelegatingActionProxyActionSupport ClassesTapestry注入 Spring 托管的 beans将 Spring Beans 注入到 Tapestry 页面中组件定义文件添加抽象访问方法将 Spring Beans 注入到 Tapestry 页面中 - Tapestry 4.0+ 风格WebWork更多资源Portlet MVC框架介绍控制器 - MVC中的C视图 - MVC中的VWeb作用范围的BeanDispatcherPortletViewRendererServlet控制器AbstractController 和 PortletContentGenerator其它简单的控制器Command控制器PortletWrappingController处理器映射PortletModeHandlerMappingParameterHandlerMappingPortletModeParameterHandlerMapping增加 HandlerInterceptorsHandlerInterceptorAdapterParameterMappingInterceptor视图和它们的解析Multipart文件上传支持使用 PortletMultipartResolver处理表单里的文件上传异常处理Portlet应用的部署整合使用Spring进行远程访问与Web服务简介使用RMI暴露服务使用RmiServiceExporter暴露服务在客户端链接服务使用Hessian或者Burlap通过HTTP远程调用服务为Hessian和co.配置DispatcherServlet使用HessianServiceExporter暴露你的bean在客户端连接服务使用Burlap对通过Hessian或Burlap暴露的服务使用HTTP Basic认证使用HTTP调用器暴露服务Exposing the service object在客户端连接服务Web Services使用JAX-RPC暴露基于servlet的web服务使用JAX-RPC访问web服务注册JAX-RPC Bean映射注册自己的JAX-RPC 处理器使用JAX-WS暴露基于servlet的web服务使用JAX-WS暴露单独web服务使用Spring支持的JAX-WS RI来暴露服务使用JAX-WS访问web服务使用XFire来暴露Web服务JMS服务端配置客户端配置对远程接口不提供自动探测实现在选择这些技术时的一些考虑Enterprise Java Beans (EJB) 集成简介访问EJB概念访问本地的无状态Session Bean(SLSB)访问远程SLSBAccessing EJB 2.x SLSBs versus EJB 3 SLSBs使用Spring提供的辅助类实现EJB组件EJB 2.x base classesEJB 3 注入拦截JMS (Java Message Service)简介使用Spring JMSJmsTemplate连接工厂目的地管理消息侦听容器SimpleMessageListenerContainerDefaultMessageListenerContainerServerSessionMessageListenerContainer事务管理发送消息使用消息转换器SessionCallback 和 ProducerCallback接收消息同步接收异步接收 - 消息驱动的POJOSessionAwareMessageListener接口MessageListenerAdapter事务中的消息处理JCA消息端点的支持JMS命名空间支持JMX介绍将Bean暴露为JMX创建MBeanServer重用原有的MBeanServer延迟初始化的MBeanMBean的自动注册控制注册行为控制Bean的管理接口MBeanInfoAssembler接口使用源码级元数据使用JDK 5.0的注解源代码级的元数据类型AutodetectCapableMBeanInfoAssembler接口用Java接口定义管理接口使用MethodNameBasedMBeanInfoAssembler控制Bean的ObjectName从Properties读取Properties使用MetadataNamingStrategy<context:mbean-export/>元素JSR-160连接器服务器端连接器客户端连接器基于Burlap/Hessian/SOAP的JMX通过代理访问MBean通知为通知注册监听器发布通知更多资源JCA CCI简介配置CCI连接器配置在Spring中配置ConnectionFactory配置CCI连接使用一个 CCI 单连接使用Spring的 CCI访问支持记录转换CciTemplate类DAO支持自动输出记录生成总结直接使用一个CCI Connection接口和Interaction接口CciTemplate 使用示例建模CCI访问为操作对象MappingRecordOperationMappingCommAreaOperation自动生成输出记录总结MappingRecordOperation 使用示例MappingCommAreaOperation 使用示例事务Spring邮件抽象层简介使用Spring邮件抽象MailSender 和 SimpleMailMessage 的基本用法使用 JavaMailSender 和 MimeMessagePreparator使用MimeMessageHelper发送附件和嵌入式资源(inline resources)附件内嵌资源使用模板来创建邮件内容一个基于Velocity的示例Spring中的定时调度(Scheduling)和线程池(Thread Pooling)简介使用OpenSymphony Quartz 调度器使用JobDetailBean使用 MethodInvokingJobDetailFactoryBean使用triggers和SchedulerFactoryBean来包装任务使用JDK Timer支持类创建定制的timers使用 MethodInvokingTimerTaskFactoryBean类最后:使用TimerFactoryBean来设置任务SpringTaskExecutor抽象TaskExecutor接口TaskExecutor类型使用TaskExecutor动态语言支持介绍第一个示例定义动态语言支持的bean公共概念<lang:language/> 元素Refreshable bean内置动态语言源文件理解dynamic-language-backed bean上下文中的构造器注入JRuby beansGroovy beans通过回调定制Groovy对象BeanShell beans场景Spring MVC控制器的脚本化Validator的脚本化Bits and bobsAOP - 通知脚本化bean作用域更多的资源注解和源代码级的元数据支持简介Spring的元数据支持注解@RequiredSpring中的其它@AnnotationsJakarta Commons Attributes集成元数据和Spring AOP自动代理基本原理声明式事务管理示例程序演示案例介绍使用动态语言实现的Spring MVC控制器构建与部署使用SimpleJdbcTemplate和@Repository实现DAO域对象Data Access Object构建XML Schema-based configurationIntroductionXML Schema-based configurationReferencing the schemasThe util schema<util:constant/>Setting a bean property or constructor arg from a field value<util:property-path/>Using <util:property-path/> to set a bean property or constructor-argument<util:properties/><util:list/><util:map/><util:set/>The jee schema<jee:jndi-lookup/> (simple)<jee:jndi-lookup/> (with single JNDI environment setting)<jee:jndi-lookup/> (with multiple JNDI environment settings)<jee:jndi-lookup/> (complex)<jee:local-slsb/> (simple)<jee:local-slsb/> (complex)<jee:remote-slsb/>The lang schemaThe jms schemaThe tx (transaction) schemaThe aop schemaThe context schema<property-placeholder/><annotation-config/><component-scan/><load-time-weaver/><spring-configured/><mbean-export/>The tool schemaThe beans schemaSetting up your IDESetting up EclipseSetting up IntelliJ IDEAIntegration issuesXML parsing errors in the Resin v.3 application serverExtensible XML authoringIntroductionAuthoring the schemaCoding a NamespaceHandlerCoding a BeanDefinitionParserRegistering the handler and the schema'META-INF/spring.handlers''META-INF/spring.schemas'Using a custom extension in your Spring XML configurationMeatier examplesNesting custom tags within custom tagsCustom attributes on 'normal' elementsFurther Resourcesspring-beans-2.0.dtdspring.tldIntroductionThe bind tagThe escapeBody tagThe hasBindErrors tagThe htmlEscape tagThe message tagThe nestedPath tagThe theme tagThe transform tagspring-form.tldIntroductionThe checkbox tagThe checkboxes tagThe errors tagThe form tagThe hidden tagThe input tagThe label tagThe option tagThe options tagThe password tagThe radiobutton tagThe radiobuttons tagThe select tagThe textarea tagSpring 2.5开发手册中文化项目声明致谢参与人员项目历程
文字

11.2. 利用JDBC核心类控制JDBC的基本操作和错误处理

11.2.1. JdbcTemplate

JdbcTemplate是core包的核心类。它替我们完成了资源的创建以及释放工作,从而简化了我们对JDBC的使用。 它还可以帮助我们避免一些常见的错误,比如忘记关闭数据库连接。 JdbcTemplate将完成JDBC核心处理流程,比如SQL语句的创建、执行,而把SQL语句的生成以及查询结果的提取工作留给我们的应用代码。 它可以完成SQL查询、更新以及调用存储过程,可以对ResultSet进行遍历并加以提取。 它还可以捕获JDBC异常并将其转换成org.springframework.dao包中定义的,通用的,信息更丰富的异常。

使用JdbcTemplate进行编码只需要根据明确定义的一组契约来实现回调接口。 PreparedStatementCreator回调接口通过给定的Connection创建一个PreparedStatement,包含SQL和任何相关的参数。 CallableStatementCreateor实现同样的处理,只不过它创建的是CallableStatement。 RowCallbackHandler接口则从数据集的每一行中提取值。

我们可以在DAO实现类中通过传递一个DataSource引用来完成JdbcTemplate的实例化,也可以在Spring的IoC容器中配置一个JdbcTemplate的bean并赋予DAO实现类作为一个实例。 需要注意的是DataSource在Spring的IoC容器中总是配制成一个bean,第一种情况下,DataSource bean将传递给service,第二种情况下DataSource bean传递给JdbcTemplate bean。

最后,JdbcTemplate中使用的所有SQL将会以“DEBUG”级别记入日志(一般情况下日志的category是JdbcTemplate相应的全限定类名,不过如果需要对JdbcTemplate进行定制的话,可能是它的子类名)。

11.2.1.1. 一些示例

下面是一些使用JdbcTemplate类的示例。(这些示例并不是完整展示所有的JdbcTemplate所暴露出来的功能。请查看与之相关的Javadoc)。

11.2.1.1.1. 查询(SELECT)

一个简单的例子用于展示如何获取一个表中的所有行数。

int rowCount = this.jdbcTemplate.queryForInt("select count(0) from t_accrual");

一个简单的例子展示如何进行参数绑定。

int countOfActorsNamedJoe = this.jdbcTemplate.queryForInt(
        "select count(0) from t_actors where first_name = ?", new Object[]{"Joe"});

查询一个String

String surname = (String) this.jdbcTemplate.queryForObject(
        "select surname from t_actor where id = ?", 
        new Object[]{new Long(1212)}, String.class);

查询并将结果记录为一个简单的数据模型。

Actor actor = (Actor) this.jdbcTemplate.queryForObject(
    "select first_name, surname from t_actor where id = ?",
    new Object[]{new Long(1212)},
    new RowMapper() {

        public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
            Actor actor = new Actor();
            actor.setFirstName(rs.getString("first_name"));
            actor.setSurname(rs.getString("surname"));
            return actor;
        }
    });

查询并组装多个数据模型。

Collection actors = this.jdbcTemplate.query(
    "select first_name, surname from t_actor",
    new RowMapper() {

        public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
            Actor actor = new Actor();
            actor.setFirstName(rs.getString("first_name"));
            actor.setSurname(rs.getString("surname"));
            return actor;
        }
    });

如果最后2个示例中的代码出现在同一段程序中,我们有必要去掉这些重复的RowMapper匿名类代码,将这些代码抽取到一个单独的类中(通常是一个静态的内部类)。 这样,这个内部类就可以在DAO的方法中被共享。因而,最后2个示例写成如下的形式将更加好:

public Collection findAllActors() {
    return this.jdbcTemplate.query( "select first_name, surname from t_actor", new ActorMapper());
}

private static final class ActorMapper implements RowMapper {

    public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
        Actor actor = new Actor();
        actor.setFirstName(rs.getString("first_name"));
        actor.setSurname(rs.getString("surname"));
        return actor;
    }
}
11.2.1.1.2. 更新(INSERT/UPDATE/DELETE)
this.jdbcTemplate.update(
        "insert into t_actor (first_name, surname) values (?, ?)", 
        new Object[] {"Leonor", "Watling"});
this.jdbcTemplate.update(
        "update t_actor set weapon = ? where id = ?", 
        new Object[] {"Banjo", new Long(5276)});
this.jdbcTemplate.update(
        "delete from actor where id = ?",
        new Object[] {new Long.valueOf(actorId)});
11.2.1.1.3. 其他操作

execute(..)方法可以被用作执行任何类型的SQL,甚至是DDL语句。 这个方法的实现需要传入一个回调接口、需要绑定的参数数组等作为参数。

this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");

调用一个简单的存储过程(更多复杂的存储过程支持请参见存储过程支持)。

this.jdbcTemplate.update(
        "call SUPPORT.REFRESH_ACTORS_SUMMARY(?)", 
        new Object[]{Long.valueOf(unionId)});

11.2.1.2. JdbcTemplate 的最佳实践

JdbcTemplate类的实例是线程安全的实例。这一点非常重要,正因为如此,你可以配置一个简单的JdbcTemplate实例,并将这个“共享的”、“安全的”实例注入到不同的DAO类中去。 另外, JdbcTemplate 是有状态的,因为他所维护的DataSource 实例是有状态的,但是这种状态是无法变化的。

使用JdbcTemplate的一个常见的最佳实践(同时也是SimpleJdbcTemplateNamedParameterJdbcTemplate 类的最佳实践)就是在Spring配置文件中配置一个DataSource实例,然后将这个共享的DataSource实例助于到你的DAO中去。 而JdbcTemplate的实例将在DataSource的setter方法中被创建。这样的话,DAO可能看上去像这样:

public class JdbcCorporateEventDao implements CorporateEventDao {

    private JdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    // JDBC-backed implementations of the methods on the CorporateEventDao follow...
}

相关的配置看上去就像这样。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- the DataSource (parameterized for configuration via a PropertyPlaceHolderConfigurer) -->
    <bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

</beans>

如果你使用Spring提供的JdbcDaoSupport类,并且你的那些基于JDBC的DAO都继承自这个类,那么你会自动地从JdbcDaoSupport类中继承了setDataSource(..)方法。 是否将你的DAO类继承自这些类完全取决于你自己的决定,事实上这并不是必须的,如果你看一下JdbcDaoSupport类你会发现,这里只是提供了一个简便的方式而已。

无论你是否使用上述这种初始化方式,都无需在执行某些SQL操作时多次创建一个JdbcTemplate实例。记住,一旦JdbcTemplate被创建,他是一个线程安全的对象。 一个你需要创建多次JdbcTemplate实例的理由可能在于,你的应用需要访问多个不同的数据库,从而需要不同的DataSources来创建不同的JdbcTemplates实例。

11.2.2. NamedParameterJdbcTemplate

NamedParameterJdbcTemplate类为JDBC操作增加了命名参数的特性支持,而不是传统的使用('?')作为参数的占位符。NamedParameterJdbcTemplate类对JdbcTemplate类进行了封装, 在底层,JdbcTemplate完成了多数的工作。这一个章节将主要描述NamedParameterJdbcTemplate类与JdbcTemplate类的一些区别,也就是使用命名参数进行JDBC操作。

// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

public void setDataSource(DataSource dataSource) {
    this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}

public int countOfActorsByFirstName(String firstName) {

    String sql = "select count(0) from T_ACTOR where first_name = :first_name";

    SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);

    return namedParameterJdbcTemplate.queryForInt(sql, namedParameters);
}

注意这里在'sql'中使用了命名参数作为变量,而这个名称所对应的值被定义在传入的'namedParameters' 中作为参数(也可以传入到MapSqlParameterSource中作为参数)。

你也可以传入许多命名参数以及他们所对应的值,以Map的方式,作为键值对传入到NamedParameterJdbcTemplate中。 (其余的被NamedParameterJdbcOperations所暴露的接口以及NamedParameterJdbcTemplate实现类遵循了类似的方式,此处不包含相关内容)。

// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

public void setDataSource(DataSource dataSource) {
    this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}

public int countOfActorsByFirstName(String firstName) {

    String sql = "select count(0) from T_ACTOR where first_name = :first_name";

    Map namedParameters = Collections.singletonMap("first_name", firstName);

    return this.namedParameterJdbcTemplate.queryForInt(sql, namedParameters);
}

NamedParameterJdbcTemplate类所具备的另外一个比较好的特性就是可以接收SqlParameterSource作为传入参数 (这个类位于相同的包定义中)。 你已经在先前的一个例子中看到了这个接口的一个具体实现类。( MapSqlParameterSource类)。而SqlParameterSource 这个接口对于NamedParameterJdbcTemplate类的操作而言是一个传入的参数。MapSqlParameterSource只是一个非常简单的实现,使用了java.util.Map作为转接器, 其中,Map中的Key表示参数名称,而Map中的Value表示参数值。

另外一个SqlParameterSource 的实现类是BeanPropertySqlParameterSource。这个类对传统的Java进行了封装(也就是那些符合JavaBean标准的类), 并且使用了JavaBean的属性作为参数的名称和值。

public class Actor {

    private Long id;
    private String firstName;
    private String lastName;
    
    public String getFirstName() {
        return this.firstName;
    }
    
    public String getLastName() {
        return this.lastName;
    }
    
    public Long getId() {
        return this.id;
    }
    
    // setters omitted...

}
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

public void setDataSource(DataSource dataSource) {
    this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}

public int countOfActors(Actor exampleActor) {

    // notice how the named parameters match the properties of the above 'Actor' class
    String sql = "select count(0) from T_ACTOR where first_name = :firstName and last_name = :lastName";

    SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);

    return this.namedParameterJdbcTemplate.queryForInt(sql, namedParameters);
}

注意,NamedParameterJdbcTemplate类只是封装JdbcTemplate模板; 因而如果你需要访问相应被封装的JdbcTemplate类,并访问一些只有在JdbcTemplate中拥有的功能,你需要使用getJdbcOperations()方法来进行访问。

请参照第 11.2.1.2 节 “JdbcTemplate 的最佳实践”来获取一些使用NamedParameterJdbcTemplate的最佳实践。

11.2.3. SimpleJdbcTemplate

注意

SimpleJdbcTemplate所提供的一些特性必须工作在Java 5及以上版本。

SimpleJdbcTemplate类是对JdbcTemplate类进行的封装,从而可以充分利用Java 5所带来的varargs和autoboxing等特性。 SimpleJdbcTemplate类完全利用了Java 5语法所带来的蜜糖效应。凡是使用过Java 5的程序员们如果要从Java 5迁移回之前的JDK版本,无疑会发现这些特性所带来的蜜糖效应。

“before and after”示例可以成为SimpleJdbcTemplate类所带来的蜜糖效应的最佳诠释。 下面的代码示例首先展示了使用传统的JdbcTemplate进行JDBC访问的代码,接着是使用SimpleJdbcTemplate类做同样的事情。

// classic JdbcTemplate-style...
private JdbcTemplate jdbcTemplate;

public void setDataSource(DataSource dataSource) {
    this.jdbcTemplate = new JdbcTemplate(dataSource);
}

public Actor findActor(long id) {
    String sql = "select id, first_name, last_name from T_ACTOR where id = ?";
    
    RowMapper mapper = new RowMapper() {
    
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
            Actor actor = new Actor();
            actor.setId(rs.getLong("id"));
            actor.setFirstName(rs.getString("first_name"));
            actor.setLastName(rs.getString("last_name"));
            return actor;
        }
    };
    
    // notice the cast, the wrapping up of the 'id' argument
    // in an array, and the boxing of the 'id' argument as a reference type
    return (Actor) jdbcTemplate.queryForObject(sql, mapper, new Object[] {Long.valueOf(id)});
}

下面是同样的逻辑,使用了SimpleJdbcTemplate;可以看到代码“干净”多了:

// SimpleJdbcTemplate-style...
private SimpleJdbcTemplate simpleJdbcTemplate;

public void setDataSource(DataSource dataSource) {
    this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
}

public Actor findActor(long id) {
    String sql = "select id, first_name, last_name from T_ACTOR where id = ?";

    ParameterizedRowMapper<Actor> mapper = new ParameterizedRowMapper<Actor>() {
    
        // notice the return type with respect to Java 5 covariant return types
        public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
            Actor actor = new Actor();
            actor.setId(rs.getLong("id"));
            actor.setFirstName(rs.getString("first_name"));
            actor.setLastName(rs.getString("last_name"));
            return actor;
        }
    };

    return this.simpleJdbcTemplate.queryForObject(sql, mapper, id);
}

请同样参照第 11.2.1.2 节 “JdbcTemplate 的最佳实践”来获取一些SimpleJdbcTemplate的最佳实践

注意

SimpleJdbcTemplate只是提供了JdbcTemplate所提供的功能的子类。 如果你需要使用JdbcTemplate的方法,而这些方法又没有在SimpleJdbcTemplate中定义,你需要调用getJdbcOperations()方法 获取相应的方法调用。JdbcOperations接口中定义的方法需要在这边做强制转化才能使用。

11.2.4. DataSource接口

为了从数据库中取得数据,我们首先需要获取一个数据库连接。Spring通过DataSource对象来完成这个工作。 DataSource是JDBC规范的一部分,它被视为一个通用的数据库连接工厂。通过使用DataSource, Container或Framework可以将连接池以及事务管理的细节从应用代码中分离出来。 作为一个开发人员,在开发和测试产品的过程中,你可能需要知道连接数据库的细节。但在产品实施时,你不需要知道这些细节。通常数据库管理员会帮你设置好数据源。

在使用Spring JDBC时,你既可以通过JNDI获得数据源,也可以自行配置数据源(使用Spring提供的DataSource实现类)。使用后者可以更方便的脱离Web容器来进行单元测试。 这里我们将使用DriverManagerDataSource,不过DataSource有多种实现, 后面我们会讲到。使用DriverManagerDataSource和你以前获取一个JDBC连接 的做法没什么两样。你首先必须指定JDBC驱动程序的全限定名,这样DriverManager 才能加载JDBC驱动类,接着你必须提供一个url(因JDBC驱动而异,为了保证设置正确请参考相关JDBC驱动的文档), 最后你必须提供一个用户连接数据库的用户名和密码。下面我们将通过一个例子来说明如何配置一个DriverManagerDataSource

DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");

11.2.5. SQLExceptionTranslator接口

SQLExceptionTranslator是一个接口,如果你需要在 SQLExceptionorg.springframework.dao.DataAccessException之间作转换,那么必须实现该接口。 转换器类的实现可以采用一般通用的做法(比如使用JDBC的SQLState code),如果为了使转换更准确,也可以进行定制(比如使用Oracle的error code)。

SQLErrorCodeSQLExceptionTranslator是SQLExceptionTranslator的默认实现。 该实现使用指定数据库厂商的error code,比采用SQLState更精确。转换过程基于一个JavaBean(类型为SQLErrorCodes)中的error code。 这个JavaBean由SQLErrorCodesFactory工厂类创建,其中的内容来自于 “sql-error-codes.xml”配置文件。该文件中的数据库厂商代码基于 Database MetaData 信息中的DatabaseProductName,从而配合当前数据库的使用。

SQLErrorCodeSQLExceptionTranslator使用以下的匹配规则:

  • 首先检查是否存在完成定制转换的子类实现。通常SQLErrorCodeSQLExceptionTranslator 这个类可以作为一个具体类使用,不需要进行定制,那么这个规则将不适用。

  • 接着将SQLException的error code与错误代码集中的error code进行匹配。 默认情况下错误代码集将从SQLErrorCodesFactory取得。 错误代码集来自classpath下的sql-error-codes.xml文件,它们将与数据库metadata信息中的database name进行映射。

  • 使用fallback翻译器。SQLStateSQLExceptionTranslator类是缺省的fallback翻译器。

SQLErrorCodeSQLExceptionTranslator可以采用下面的方式进行扩展:

public class MySQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {

    protected DataAccessException customTranslate(String task, String sql, SQLException sqlex) {
        if (sqlex.getErrorCode() == -12345) {
            return new DeadlockLoserDataAccessException(task, sqlex);
        }
        return null;
    }
}

在上面的这个例子中,error code为'-12345'的SQLException将采用该转换器进行转换,而其他的error code将由默认的转换器进行转换。 为了使用该转换器,必须将其作为参数传递给JdbcTemplate类的setExceptionTranslator方法,并在需要使用这个转换器器的数据 存取操作中使用该JdbcTemplate。下面的例子演示了如何使用该定制转换器:

// create a JdbcTemplate and set data source 
JdbcTemplate jt = new JdbcTemplate(); 
jt.setDataSource(dataSource); 
// create a custom translator and set the DataSource for the default translation lookup 
MySQLErrorCodesTransalator tr = new MySQLErrorCodesTransalator(); 
tr.setDataSource(dataSource); 
jt.setExceptionTranslator(tr); 
// use the JdbcTemplate for this SqlUpdate
SqlUpdate su = new SqlUpdate(); 
su.setJdbcTemplate(jt); 
su.setSql("update orders set shipping_charge = shipping_charge * 1.05"); 
su.compile(); 
su.update();

在上面的定制转换器中,我们给它注入了一个数据源,因为我们仍然需要 使用默认的转换器从sql-error-codes.xml中获取错误代码集。

11.2.6. 执行SQL语句

我们仅需要非常少的代码就可以达到执行SQL语句的目的,一旦获得一个 DataSource和一个JdbcTemplate, 我们就可以使用JdbcTemplate提供的丰富功能实现我们的操作。下面的例子使用了极少的代码完成创建一张表的工作。

import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;

public class ExecuteAStatement {

    private JdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public void doExecute() {
        this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
    }
}

11.2.7. 执行查询

除了execute方法之外,JdbcTemplate还提供了大量的查询方法。 在这些查询方法中,有很大一部分是用来查询单值的。比如返回一个汇总(count)结果 或者从返回行结果中取得指定列的值。这时我们可以使用queryForInt(..)queryForLong(..)或者queryForObject(..)方法。 queryForObject方法用来将返回的JDBC类型对象转换成指定的Java对象,如果类型转换失败将抛出 InvalidDataAccessApiUsageException异常。 下面的例子演示了两个查询的用法,一个返回int值,另一个返回String

import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;

public class RunAQuery {

    private JdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
  
    public int getCount() {
        return this.jdbcTemplate.queryForInt("select count(*) from mytable");
    }

    public String getName() {
        return (String) this.jdbcTemplate.queryForObject("select name from mytable", String.class);
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}

除了返回单值的查询方法,JdbcTemplate还提供了一组返回List结果 的方法。List中的每一项对应查询返回结果中的一行。其中最简单的是queryForList方法, 该方法将返回一个List,该List中的每一条 记录是一个Map对象,对应应数据库中某一行;而该Map 中的每一项对应该数据库行中的某一列值。下面的代码片断接着上面的例子演示了如何用该方法返回表中所有记录:

private JdbcTemplate jdbcTemplate;

public void setDataSource(DataSource dataSource) {
    this.jdbcTemplate = new JdbcTemplate(dataSource);
}

public List getList() {
    return this.jdbcTemplate.queryForList("select * from mytable");
}

返回的结果集类似下面这种形式:

[{name=Bob, id=1}, {name=Mary, id=2}]

11.2.8. 更新数据库

JdbcTemplate还提供了一些更新数据库的方法。 在下面的例子中,我们根据给定的主键值对指定的列进行更新。 例子中的SQL语句中使用了“?”占位符来接受参数(这种做法在更新和查询SQL语句中很常见)。 传递的参数值位于一个对象数组中(基本类型需要被包装成其对应的对象类型)。

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

public class ExecuteAnUpdate {

    private JdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public void setName(int id, String name) {
        this.jdbcTemplate.update(
                "update mytable set name = ? where id = ?", 
                new Object[] {name, new Integer(id)});
    }
}

11.2.9. 获取自动生成的主键

JdbcTemplate中有一个update方法,可以方便地从数据库中获取数据库自动创建的主键。(这是JDBC 3.0的标准 - 可以参见13.6节获取详细信息)。 这个方法使用了PreparedStatementCreator接口作为第一个参数, 可以通过这个接口的实现类来定义相应的Insert语句。另外一个参数是KeyHolder, 一旦update方法成功,这个参数将包含生成的主键。这里对于创建合适的PreparedStatement并没有一个统一的标准。(这也解释了函数签名如此定义的原因)。下面是一个在Oracle上运行良好的示例,它可能在其他平台上无法工作:

final String INSERT_SQL = "insert into my_test (name) values(?)";
final String name = "Rob";

KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(
    new PreparedStatementCreator() {
        public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
            PreparedStatement ps =
                connection.prepareStatement(INSERT_SQL, new String[] {"id"});
            ps.setString(1, name);
            return ps;
        }
    },
    keyHolder);

// keyHolder.getKey() now contains the generated key
上一篇:下一篇: