最近在寫一個 Mybatis 代碼自動生成插件,用的是Mybatis來擴展,其中有一個需求就是 生成javaMapper文件和 xmlMapper文件的時候 希望另外生成一個擴展類和擴展xml文件。原文件不修改,只存放一些基本的信息,開發過程中只修改擴展的Ext文件

形式如下:

SrcTestMapper.java

package com.test.dao.mapper.srctest;import com.test.dao.model.srctest.SrcTest;import com.test.dao.model.srctest.SrcTestExample;import java.util.List;import org.apache.ibatis.annotations.Param;public interface SrcTestMapper { long countByExample(SrcTestExample example); int deleteByExample(SrcTestExample example); int deleteByPrimaryKey(Integer id); int insert(SrcTest record); int insertSelective(SrcTest record); List selectByExample(SrcTestExample example); SrcTest selectByPrimaryKey(Integer id); int updateByExampleSelective(@Param("record") SrcTest record, @Param("example") SrcTestExample example); int updateByExample(@Param("record") SrcTest record, @Param("example") SrcTestExample example); int updateByPrimaryKeySelective(SrcTest record); int updateByPrimaryKey(SrcTest record);}123456789101112131415161718192021222324252627282930

SrcTestMapperExt.java

package com.test.dao.mapper.srctest;import com.test.dao.model.srctest.SrcTest;import org.apache.ibatis.annotations.Param;import javax.annotation.Resource;import java.util.List;/*** SrcTestMapperExt接口* Created by shirenchuang on 2018/6/30.*/@Resourcepublic interface SrcTestMapperExt extends SrcTestMapper { List selectExtTest(@Param("age") int age);}123456789101112131415161718192021

SrcTestMapper.xml

1234567891011

SrcTestMapperExt.xml

12345678

注意:這裏返回的resultMap=”BaseResultMap” 這個Map並沒有再這個xml中定義,這樣能使用嗎?

上面是我生成的代碼;並且能夠正常使用;

那麼SrcTestMapperExt.xml是如何繼承SrcTestMapper.xml中的定義的呢?

1. 修改命名空間,使他們的命名空間相同,namespace=”com.test.dao.mapper.srctest.SrcTestMapperExt”

2. 光這樣還不夠,因爲這個時候你去運行的時候會報錯

Caused by: org.apache.ibatis.builder.BuilderException: Wrong namespace. Expected 'com.test.dao.mapper.srctest.SrcTestMapper' but found 'com.test.dao.mapper.srctest.SrcTestMapperExt'.1

因爲Mybatis中是必須要 xml的文件包名和文件名必須跟 Mapper.java對應起來的

比如com.test.dao.mapper.srctest.SrcTestMapper.java這個相對應的是

com.test.dao.mapper.srctest.SrcTestMapper.xml

必須是這樣子,沒有例外,否則就會報錯

show the code

MapperBuilderAssistant

public void setCurrentNamespace(String currentNamespace) { if (currentNamespace == null) { throw new BuilderException("The mapper element requires a namespace attribute to be specified."); } if (this.currentNamespace != null && !this.currentNamespace.equals(currentNamespace)) { throw new BuilderException("Wrong namespace. Expected '" + this.currentNamespace + "' but found '" + currentNamespace + "'."); } this.currentNamespace = currentNamespace; }123456789101112

這個this.currentNamespace 和參數傳進來的currentNamespace比較是否相等;

參數傳進來的currentNamespace就是我們xml中的

值;

然後this.currentNamespace是從哪裏設置的呢?this.currentNamespace = currentNamespace;

跟下代碼:MapperAnnotationBuilder

public void parse() { String resource = type.toString(); if (!configuration.isResourceLoaded(resource)) { loadXmlResource(); configuration.addLoadedResource(resource); assistant.setCurrentNamespace(type.getName()); parseCache(); parseCacheRef(); Method[] methods = type.getMethods(); for (Method method : methods) { try { // issue #237 if (!method.isBridge()) { parseStatement(method); } } catch (IncompleteElementException e) { configuration.addIncompleteMethod(new MethodResolver(this, method)); } } } parsePendingMethods(); }12345678910111213141516171819202122

看到 assistant.setCurrentNamespace(type.getName());

它獲取的是 type.getName() ;這個type的最終來源是

MapperFactoryBean

@Override protected void checkDaoConfig() { super.checkDaoConfig(); notNull(this.mapperInterface, "Property 'mapperInterface' is required"); Configuration configuration = getSqlSession().getConfiguration(); if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) { try { configuration.addMapper(this.mapperInterface); } catch (Exception e) { logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e); throw new IllegalArgumentException(e); } finally { ErrorContext.instance().reset(); } } }123456789101112131415161718

看configuration.addMapper(this.mapperInterface);這行應該就明白了

加載mapperInterface的時候會跟相應的xml映射,並且會去檢驗namespace是否跟mapperInterface相等!

那麼既然命名空間不能修改,那第一條不白說了?還怎麼實現Mapper.xml的繼承啊?

別慌,既然是這樣子,那我們可以讓 MapperInterface 中的SrcTestMapper.java別被加載進來就行了啊!!

只加載 MapperExt.java不就行了?

3. 修改applicationContext.xml,讓Mapper.java不被掃描

Mapper.java接口掃描配置

123456789

basePackage 把Mapper.java掃描進去沒有關係,重點是

這樣 MapperScanner會把沒有配置註解的過濾掉;

回頭看我們的MapperExt.java配置文件是有加上註解的

/*** SrcTestMapperExt接口* Created by shirenchuang on 2018/6/30.*/@Resourcepublic interface SrcTestMapperExt extends SrcTestMapper { List selectExtTest(@Param("age") int age);}12345678

這樣子之後,基本上問題就解決了,還有一個地方特別要注意一下的是.xml文件的配置

123456

這樣配置沒有錯,但是我之前的配置寫成了

這樣子 MapperExt.xml 沒有被掃描進去,在我執行單元測試的時候

@Test public void selectExt(){ List tests = srcTestService.selectExtTest(9); System.out.println(tests.toString()); }12345

err_console

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.test.dao.mapper.srctest.SrcTestMapperExt.selectExtTest12

但是執行 ` srcTestService.insertSelective(srcTest);不會出錯

原因就是 insertSelective是在SrcTestMapper.xml中存在 ,已經被註冊到

com.test.dao.mapper.srctest.SrcTestMapperExt命名空間了,但是selectExtTest由於沒有被註冊,所以報錯了;

有興趣可以下載閱讀或者直接使用我整合的

查看原文 >>
相關文章