【问题标题】:Multiple JdbcTemplate instances or not?是否有多个 JdbcTemplate 实例?
【发布时间】:2012-03-16 15:49:54
【问题描述】:

据我了解,DataSourceJdbcTemplates 都是threadsafe,因此您可以配置JdbcTemplate 的单个实例,然后安全地将这个共享引用注入多个DAO(或存储库) 。还有DataSource应该是一个Spring单例,因为它管理着连接池。

官方Spring Documentation JdbcTemplate best practices解释了替代方案(手册摘录为斜体,方括号内是我的注释:

  • 在你的 Spring 配置文件中配置一个 DataSource,然后将共享的 DataSource bean 依赖注入到你的 DAO 类中; JdbcTemplate 是在 DataSource 的设置器中创建的。 [使用 XML 配置,这会导致多个 JdbcTemplate 实例,因为在数据源设置器中有new JdbcTemplate(dataSource)]
  • 为依赖注入使用组件扫描和注释支持。在这种情况下,您使用 @Repository 注释该类(这使其成为组件扫描的候选对象)并使用 @Autowired 注释 DataSource 设置方法。 [这种情况也会导致多个 JdbcTemplate 实例]
  • 如果您使用 Spring 的 JdbcDaoSupport 类,并且您的各种 JDBC 支持的 DAO 类从它扩展,那么您的子类从 JdbcDaoSupport 类继承 setDataSource(..) 方法。你可以选择是否继承这个类。提供 JdbcDaoSupport 类只是为了方便。 [由于每个扩展它的类都有一个 JdbcDaoSupport 实例,因此派生类的每个实例也有一个 JdbcTemplate 实例(请参阅source code for JdbcDaoSupport)]

但是,稍后的说明不鼓励刚才提出的所有选项:

配置后,JdbcTemplate 实例是线程安全的。如果您的应用程序访问多个数据库,这需要多个 DataSource,并且随后需要多个不同配置的 JdbcTemplate,您可能需要多个 JdbcTemplate 实例。

换句话说,刚刚提出的所有选项都将导致拥有多个 JdbcTemplate 实例(每个 DAO 一个),并且就在文档说使用单个数据库时没有必要之后。

我要做的是直接将JdbcTemplate 注入到需要它的各种 DAO,所以我的问题是,这样做可以吗?而且,您是否也认为 Spring 参考文档是自相矛盾的?还是我的误会?

【问题讨论】:

  • 每个数据库方案只使用一个数据源/jdbcTemplate
  • Spring 文档并不自相矛盾。您应该阅读前面的声明 - 无论您选择使用(或不使用)上述哪种模板初始化样式,每次执行 SQL 时都很少需要创建 JdbcTemplate 类的新实例。最佳实践是每个 dao 一个 JdbcTemplate。如果您只需要多个数据源,则需要多个 JdbcTemplates。
  • @stivlo 你还记得你最终做了什么吗?我处于类似的设计选择中,我认为每个 DAO 有 1 个 JdbcTemplate 实例就足够了。但是你会看到类似这样的教程,他们在同一个 DAO vogella.com/tutorials/SpringJDBC/… 中使用多个 JdbcTemplate 实例
  • 可能是因为JdbcTemplate 有几个setter 方法。 Spring 似乎有几个可变的 bean,可能会影响其他线程。当可以修改这些 bean 时,我认为共享 JdbcTemplateObjectMapper 之类的 bean 不是最佳做法。

标签: java spring jdbctemplate


【解决方案1】:

IMO,将 JdbcTemplate 注入您的(多个)DAO 没有问题。当您需要运行数据库查询时,该模板用于将您的 DAO“连接”到物理资源(数据库连接)。因此,如果 SessionFactory 和 TransactionManager 配置正确,您将不会遇到并发问题 - Spring 管理您使用持久层所需的 bean 的生命周期。使用模板的优点是:

  1. JDBC 模板管理自动与数据库交互所需的物理资源,例如创建和释放数据库连接。
  2. Spring JDBC 模板将标准 JDBC SQLExceptions 转换为 RuntimeExceptions。这使您可以更灵活地对错误做出反应。 Spring JDBC 模板还将供应商特定的错误消息转换为更易于理解的错误消息

【讨论】:

    【解决方案2】:

    所以应该分两种情况:

    我们不改变DAO中的JdbcTemplate属性,我们可以定义如下:

    <bean id="tlmJDBCTemplate" class="org.springframework.jdbc.core.JdbcTemplate"             <property name="dataSource" ref="plmTlmDataSource"/>        
        </bean>
    

    注意:大多数时候我们不会更改 JdbcTemplate 属性,因为没有必要。

    我们更改 DAO 中的 JdbcTemplate 属性,我们应该扩展 JdbcDaoSupport。

    State:
    •   fetchSize: If this variable is set to a non-zero value, it will be used for setting the fetchSize property on statements used for query processing(JDBC Driver default)
    •   maxRows: If this variable is set to a non-zero value, it will be used for setting the maxRows property on statements used for query processing(JDBC Driver default)
    •   queryTimeout: If this variable is set to a non-zero value, it will be used for setting the queryTimeout property on statements used for query processing.(JDBC Driver default)
    •   skipResultsProcessing: If this variable is set to true then all results checking will be bypassed for any callable statement processing.  This can be used to avoid a bug in some older Oracle JDBC drivers like 10.1.0.2.(false)
    •   skipUndeclaredResults: If this variable is set to true then all results from a stored procedure call that don't have a corresponding SqlOutParameter declaration will be bypassed. All other results processing will be take place unless the variable {@code skipResultsProcessing} is set to {@code true}(false)
    •   resultsMapCaseInsensitive: If this variable is set to true then execution of a CallableStatement will return the results in a Map that uses case insensitive names for the parameters if Commons Collections is available on the classpath.(false)
    
    1. JdbcDao支持

      公共抽象类 JdbcDaoSupport 扩展 DaoSupport {

      private JdbcTemplate jdbcTemplate;
      
      
      /**
       * Set the JDBC DataSource to be used by this DAO.
       */
      public final void setDataSource(DataSource dataSource) {
          if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) {
              this.jdbcTemplate = createJdbcTemplate(dataSource);
              initTemplateConfig();
          }
      }
      

    总结:我认为spring给在guide中的练习并不是最好的。

    【讨论】:

      【解决方案3】:

      Spring 本质上对最佳实践非常微妙。

      JdbcTemplate 是线程安全的,尤其是无锁 (v4.2.4)。 这意味着在并发线程之间共享时不应导致性能下降*。 因此,没有令人信服的理由为每个数据源提供多个实例。

      推测性说明:这部分确实令人困惑。 可能是由于历史(进化)的原因。 由于非线程安全性或一次对域的了解不足,也许 Spring 过去有 per dao 策略。 类似于基于 xml 的配置“灾难”。 如今,春天放弃了自以为是的观点,而是努力变得灵活。 不幸的是,这导致错误的设计选择被隐蔽地承认。

      * 衡量不要猜测

      【讨论】:

        【解决方案4】:

        时隔多年,再次看到这个问题,我想我们可以先用单例创建“Jdbc模板”,然后注入到DAO,所以它只是模板的一个实例。

        <bean id="template" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource" />
        </bean>
        

        然后您可以将模板注入 DAO 或 DAO 扩展 JdbcDaoSupport。

        public final void setJdbcTemplate(JdbcTemplate jdbcTemplate)
        {
           this.jdbcTemplate = jdbcTemplate;
           initTemplateConfig();
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-03-07
          • 1970-01-01
          • 2021-12-25
          • 2017-12-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多