解決mapper重名問題

LemonDus發表於2024-12-05

問題

公司有一個整合開發平臺,匯入資料庫表會自動生成實體類、mapper和xml等檔案,這是一件很方便的事,可以省去很多沒有技術性的重複工作。

但是最近我在使用這個平臺的時候遇到了一個問題,那就是mapper衝突問題。當老表進行匯入的時候,會生成與之前專案中已有mapper一樣的名字,比如原專案中有個PersonMapper.java的檔案,匯入後就會有兩個PersonMapper.java,這樣就會衝突報錯,導致專案無法啟動。

怎麼解決呢?將原先的mapper命名進行修改?顯然是不現實的,也違背了開放封閉原則。

原因

首先需要明確,為什麼兩個一樣命名的mapper檔案會衝突,因為在在進行物件注入的時候,@Mapper在生成beanName的時候預設把首字母小寫之後返回類名,如果存在兩個命名完全一樣的檔案返回的beanName也是一樣的,因此@Resource會不知道注入哪一個從而衝突。

清楚原因後,要做的就是當注入的時候怎麼區分這兩個mapper檔案,進行不同的注入。

解決

在思考一番後,決定使用@MapperScan去解決這個問題,核心在於使用其中的nameGenerator欄位。

@MapperScan中的nameGenerator繼承於BeanNameGenerator
mmct_namegenerator
而在Spring的容器中,是使用BeanNameGenerator去命名檢測到的元件,可以透過重寫nameGenerator方法去重新命名檢測到的元件,具體解決程式碼如下所示:

@Configuration
@MapperScan(
 value = {"xxx", "xxx"},
 sqlSessionFactoryRef = "sqlSessionFactory",
 nameGenerator = ComposerNameGenerator.class
)


public class ComposerNameGenerator implements BeanNameGenerator {
 public ComposerNameGenerator() {
 }

// definition 是被生成名字的BeanDefinition例項;
// registry是生成名字後註冊進的BeanDefinitionRegistry。
 public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
    // 拿到Bean定義資訊裡面的BeanClassName全類名
    // 注意這個不是必須的,因為如果是繼承關係,配上父類的依舊行了
    String generatedBeanName = definition.getBeanClassName();
    if (generatedBeanName == null) {
      // 若沒有配置本類全類名,去拿到父類的全類名+$child"倆表示自己
      if (definition.getParentName() != null) {
      	generatedBeanName = definition.getParentName() + "$child";
      }
      // 工廠Bean的  就用方法的名字+"$created"
      else if (definition.getFactoryBeanName() != null) {
        generatedBeanName = definition.getFactoryBeanName() + "$created";
      }
    }
    // 若一個都沒找到,拋錯
     if (!StringUtils.hasText(generatedBeanName)) {
         throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither 'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
     } else {
         return generatedBeanName;
     }
 }
}

這樣配置後,即使是一樣命名的mapper檔案,只要不在同一個包中依舊可以正常注入。

相關文章