【问题标题】:What is the proper way to indicate which data source to inject into my DAOs in a multi-module multi-datasource project?在多模块多数据源项目中,指示将哪个数据源注入我的 DAO 的正确方法是什么?
【发布时间】:2021-01-19 20:12:32
【问题描述】:

我有一个项目分为 3 个模块(到目前为止) - 核心(模型 1)、用户管理(模型 2)和 Web(视图和控制器)。我的项目结构(为了切入主题而简化为仅相关的类)如下:

Project  
|-- core  
|  |-- src.main.java.com.romco.example  
|  |  |-- config.CoreDataSourceConfiguration  
|  |  |-- persistence.daoimpl.SomeCoreDaoImpl  
|-- user-management  
|  |-- src.main.kotlin.com.romco.example  
|  |  |-- config.UserManagementConfiguration  
|  |  |-- persistence.daoimpl.SomeUserManagementDaoImpl  
|-- web  
| // not important right now

我的类如下(在调试我之前的问题时,我不得不将一些值初始化直接移动到代码中,而不是使用 application.properties,正如 TODO 所指出的,所以为了手头的问题,请忽略它)

  • CoreDataSource 配置:

    @Configuration
    public class CoreDataSourceConfiguration {
    
        @Bean
        @Primary
        public DataSourceProperties coreDataSourceProperties() {
            return new DataSourceProperties();
        }
    
        //TODO values should be retrieved from application.properties
    
        @Bean(name = "coreDataSource")
        @Primary
        public DataSource coreDataSource() {
            DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
            dataSourceBuilder.driverClassName("com.mysql.cj.jdbc.Driver");
            dataSourceBuilder.url("...");
            dataSourceBuilder.username("...");
            dataSourceBuilder.password("...");
            return dataSourceBuilder.build();
        }
    
        @Bean(name = "coreTransactionManager")
        @Autowired
        DataSourceTransactionManager coreTransactionManager(@Qualifier("coreDataSource") DataSource dataSource) {
            return new DataSourceTransactionManager(dataSource);
        }
    }

  • SomeCoreDaoImpl:

    @Repository
    public class SomeCoreDaoImpl implements SomeCoreDao {
        
        // some constants here
    
        private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
        
        @Autowired
        @Override
        public void setDataSource(DataSource dataSource) {
            namedParameterJdbcTemplate = NamedParameterJdbcTemplateHolder.get(dataSource);
        }
    
        // DB code here - create, update, etc.
        
    }

  • 用户管理配置:

    @Configuration
    open class UserManagementDataSourceConfiguration {
    
        @Bean
        open fun userManagementDataSourceProperties(): DataSourceProperties {
            return DataSourceProperties()
        }
    
        @Bean(name = ["userManagementDataSource"])
        open fun userManagementDataSource(): DataSource {
            val dataSourceBuilder = DataSourceBuilder.create()
            dataSourceBuilder
                    .driverClassName("com.mysql.cj.jdbc.Driver")
                    .url("...")
                    .username("...")
                    .password("...")
            return dataSourceBuilder.build()
        }
    
        @Bean(name = ["userManagementTransactionManager"])
        @Autowired
        open fun userManagementTransactionManager(@Qualifier("userManagementDataSource") dataSource: DataSource): DataSourceTransactionManager {
            return DataSourceTransactionManager(dataSource)
        }
      }

  • SomeUserManagementDaoImpl:

    @Repository
    open class SomeUserManagementDaoImpl: SomeUserManagementDao{
    
        // constants are here
    
        private lateinit var namedParameterJdbcTemplate: NamedParameterJdbcTemplate
    
        @Autowired
        fun setDataSource(@Qualifier("userManagementDataSource") dataSource: DataSource) {
            namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
        }
    
        // DB code here
    
    }

如您所见,我实现它的方法是在 SomeUserManagementDaoImpl 类中的自动装配的 setDataSource 方法中指定要使用的 bean。

我显然更愿意避免在每个 daoImpl 类中都这样做,虽然我可以考虑将其提取到单个类中,但这似乎不是“弹簧”预期的解决方案。

现在(再次,显然)- 数据源是特定于模块的,最初,我什至认为 spring 会以某种方式在幕后解决它,而不是使用 @Primary 数据源,而是使用在给定模块(除非该模块没有,在这种情况下我认为它会退回到@Primary )。
但是,情况并非如此,我想知道是否有某种方法可以告诉 spring 在整个模块中使用给定的数据源配置...

我一直在查看许多处理多数据源项目的类似主题和指南,但实际上我从未找到答案。事实上,我在实施多数据源解决方案时参考的指南根本没有提到这一点(除非我错过了),例如。
https://www.baeldung.com/spring-boot-failed-to-configure-data-source
https://www.baeldung.com/spring-data-jpa-multiple-databases

也完全有可能是我在做其他非常错误的事情,这是根本原因,在这种情况下,也请帮助我。

【问题讨论】:

    标签: java spring spring-boot multi-module multi-database


    【解决方案1】:

    所以,如果有人偶然发现同样的问题,这就是我目前解决的方法。我将来可能会想出一个更优雅的解决方案,或者更有可能发现这个问题,但现在它似乎正在工作(虽然还没有做太多测试):

    在用户管理模块(不使用@Primary 数据源的模块)中,我创建了以下抽象类,将数据源注入(使用限定符指定数据源)提取到一个位置:

        abstract class WithDataSource {
        
            protected lateinit var namedParameterJdbcTemplate: NamedParameterJdbcTemplate
        
            @Autowired
            fun setDataSource(@Qualifier(USER_MANAGEMENT_DATA_SOURCE_BEAN_NAME) dataSource: DataSource) {
                namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
            }
        
        }
    

    我的每个用户管理 DaoImpl 类都扩展了这个类,因此隐式实现了我的 GenericDao 接口的 setDataSource() 方法。

    为了完整起见,用户管理模块现在看起来像这样(我包括了一些以前省略的接口,但仍然保留了“示例”命名并省略了一些特定的实用程序代码):

        Project  
        |-- core  
        |  |-- src.main.java.com.romco.example  
        |  |  |-- config.CoreDataSourceConfiguration  
        |  |  |-- persistence.daoimpl.SomeCoreDaoImpl  
        |-- user-management  
        |  |-- src.main.kotlin.com.romco.example  
        |  |  |-- config.UserManagementConfiguration  
        |  |  |-- persistence.dao.GenericDao  
        |  |  |-- persistence.daoimpl.SomeUserManagementDaoImpl  
        |  |  |-- persistence.util.DaoUtil.kt  
        |-- web  
        | // not important right now
    
    
    • UserManagementConfiguration(将 bean 名称添加为常量 USER_MANAGEMENT_DATA_SOURCE_BEAN_NAME):
    
        @Configuration
        open class UserManagementDataSourceConfiguration {
    
            companion object {
                const val USER_MANAGEMENT_DATA_SOURCE_BEAN_NAME = "userManagementDataSource"
            }
    
            @Bean
            open fun userManagementDataSourceProperties(): DataSourceProperties {
                return DataSourceProperties()
            }
        
            @Bean(name = ["userManagementDataSource"])
            open fun userManagementDataSource(): DataSource {
                val dataSourceBuilder = DataSourceBuilder.create()
                dataSourceBuilder
                        .driverClassName("com.mysql.cj.jdbc.Driver")
                        .url("...")
                        .username("...")
                        .password("...")
                return dataSourceBuilder.build()
            }
        
            @Bean(name = ["userManagementTransactionManager"])
            @Autowired
            open fun userManagementTransactionManager(@Qualifier("userManagementDataSource") dataSource: DataSource): DataSourceTransactionManager {
                return DataSourceTransactionManager(dataSource)
            }
          }
    
    
    • GenericDao(原始问题中未提及,因为它不太相关,包括解决方案的完整性):
        interface GenericDao<T> {
            fun setDataSource(dataSource: DataSource)
            // retrieves all
            fun retrieveAll(): Collection<T>
            // creates and returns id of the newly created record. In case of failure, returns -1.
            fun create(t: T): Long
            // updates by id, returns true if success.
            fun update(t: T): Boolean
            // deletes by id, returns true if success.
            fun delete(t: T): Boolean
            // performs cleanup, for example, might delete all test records (id < 0)
            fun cleanup()
        }
    
    
    • SomeUserManagementDao(原始问题中未提及,因为它不太相关,包括解决方案的完整性):
        interface SomeUserManagementDao: GenericDao<SomeUserManagementClass> {
            fun retrieveBySpecificValue(specificValue: String): SomeUserManagementClass?
        }
    
    • SomeUserManagementDaoImpl(如评论中所述已更新):
    
        @Repository
        open class SomeUserManagementDaoImpl: SomeUserManagementDao, WithDataSource() {
        
            // constants are here
        
            // namedParameterJdbcTemplate and setDataSource() are now inherited from the parent class - WithDataSource
        
            // DB code here
        
        }
    
    
    • DaoUtil.kt(包含最初提到的抽象类以及其他一些实用程序,在本例中省略):
        abstract class WithDataSource {
        
            protected lateinit var namedParameterJdbcTemplate: NamedParameterJdbcTemplate
        
            @Autowired
            fun setDataSource(@Qualifier(USER_MANAGEMENT_DATA_SOURCE_BEAN_NAME) dataSource: DataSource) {
                namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
            }
        
        }
    

    【讨论】:

      猜你喜欢
      • 2019-11-21
      • 2019-03-03
      • 1970-01-01
      • 2019-11-24
      • 1970-01-01
      • 2020-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多