Spring+Kotlin环境下如何用配置动态值替换硬编码表名?
场景与问题
使用Spring + Kotlin开发Web应用,现有MyBatis Mapper及表定义类如下:
MyEntityMapper代码
@Mapper @Repository interface MyEntityMapper { /** * Поиск MyEntity. * * @param selectStatement Параметры запроса. * @return List MyEntity. */ @SelectProvider(type = SqlProviderAdapter::class, method = "select") @Results( id = "myEntityMap", value = [ Result(property = "name", column = "NAME"), Result(property = "price", column = "PRICE"), Result(property = "age", column = "AGE") ] ) fun findListBy(selectStatement: SelectStatementProvider): List<MyEntity> /** * Обновление MyEntity. * * @param updateStatement MyEntity * @return Количество обновленных записей. */ @UpdateProvider(type = SqlProviderAdapter::class, method = "update") fun update(updateStatement: UpdateStatementProvider): Int } fun MyEntityMapper.selectMany(completer: SelectCompleter) = selectList(this::findListBy, myEntity_.all, myEntity, completer) fun MyEntityMapper.update(completer: UpdateCompleter) = update(this::update, MyEntity_, completer)
MyEntity_表定义类(非Spring组件)
class MyEntity_ : SqlTable("my_entity") { val name = column<String>(name = "NAME", jdbcType = VARCHAR) val price = column<String>(name = "PRICE", jdbcType = VARCHAR) val age = column<String>(name = "AGE", jdbcType = VARCHAR) val all = listOf( name, price, age ) companion object { val myEntity_ = MyEntity_() } }
当前核心问题:class MyEntity_ : SqlTable("my_entity")中的表名"my_entity"为硬编码,希望替换为application.properties中的配置值;同时想了解Spring中如何为接口注入动态值。
一、动态替换表名的实现方案
方案1:将MyEntity_转为Spring组件,通过构造函数注入表名
修改MyEntity_类,移除静态单例,通过@Value注入配置中的表名:
@Component class MyEntity_(@Value("\${table.names.my_entity}") tableName: String) : SqlTable(tableName) { val name = column<String>(name = "NAME", jdbcType = VARCHAR) val price = column<String>(name = "PRICE", jdbcType = VARCHAR) val age = column<String>(name = "AGE", jdbcType = VARCHAR) val all = listOf(name, price, age) }
在application.properties中添加配置:
table.names.my_entity=my_entity
后续使用时,通过Spring的@Autowired注入MyEntity_实例,修改原扩展函数:
fun MyEntityMapper.selectMany(myEntity_: MyEntity_, completer: SelectCompleter) = selectList(this::findListBy, myEntity_.all, myEntity, completer)
方案2:通过配置类创建MyEntity_实例(不修改类本身为组件)
如果不想将MyEntity_标记为Spring组件,可通过配置类动态传入表名:
首先修改MyEntity_构造函数:
class MyEntity_(tableName: String) : SqlTable(tableName) { val name = column<String>(name = "NAME", jdbcType = VARCHAR) val price = column<String>(name = "PRICE", jdbcType = VARCHAR) val age = column<String>(name = "AGE", jdbcType = VARCHAR) val all = listOf(name, price, age) }
创建配置类提供Bean:
@Configuration class TableConfig { @Value("\${table.names.my_entity}") private lateinit var myEntityTableName: String @Bean fun myEntity_(): MyEntity_ { return MyEntity_(myEntityTableName) } }
同样在application.properties中配置表名,使用时通过@Autowired注入MyEntity_实例即可。
二、Spring中为接口注入动态值的方法
接口无法直接实例化,需通过以下方式实现动态值注入:
方法1:为接口编写实现类,注入动态值
定义接口后,创建Spring管理的实现类,在实现类中注入配置值:
interface TableNameProvider { fun getMyEntityTableName(): String } @Component class TableNameProviderImpl(@Value("\${table.names.my_entity}") private val tableName: String) : TableNameProvider { override fun getMyEntityTableName(): String { return tableName } }
后续在需要的地方注入TableNameProvider接口即可获取动态值。
方法2:结合MyBatis Provider注入动态值(针对Mapper场景)
如果需要在MyBatis的Provider类中使用动态值,可将Provider类转为Spring组件,注入配置值:
@Component class SqlProviderAdapter { @Value("\${table.names.my_entity}") private lateinit var myEntityTableName: String fun select(selectStatement: SelectStatementProvider): String { // 动态拼接SQL时使用myEntityTableName return "SELECT NAME, PRICE, AGE FROM $myEntityTableName ${selectStatement.whereClause}" } }
注意确保MyBatis能识别Spring管理的Provider类,可通过@MapperScan配置或在Mapper的@SelectProvider中指定Spring Bean的名称。
内容的提问来源于stack exchange,提问作者Pavel Bukhalov

