@param注解_启用 parameters 编译选项简化 mybatis @Param 注解重复问题
在使用 mybatis 查詢的時候, 只需要定義一個查詢接口, mybatis 會為我們注入注解實現(xiàn)或是 xml 實現(xiàn). 但當我們需要傳遞參數(shù)時, 通常需要 @Param 來定義一個名稱, 但經(jīng)常的, 我們也不難發(fā)現(xiàn), 這個名稱與參數(shù)名稱通常是一樣的:
User findUser(@Param("username") String username, @Param("password") String password);如上, username 和 password 都重復了.
而之所以要這么使用, 是因為 xml 中 ${xxx} 所引用的名稱就來自于 @Param 里定義的值:
<select id="findUser" resultType="net.xiaogd.demo.mybatis.entity.User">select * from user where username = #{username} and password = #{password}
select>
這就帶來一個重復的問題, 可否簡化這個定義, 使得無需重復錄入名稱, 甚至完全地去掉呢? 比如像下面這樣:
User findUser(String username, String password);答案是可以的, 下面就來說下怎么去做到這一點.
前置條件
首先項目需要使用 jdk8 或以上;
其次, 需要增加一個編譯時的選項?-parameters.
也即是這樣去編譯:?javac -parameters
通常, 如果沒有加上這個選項, 編譯后的方法參數(shù)簽名會變成這樣:
User findUser(String arg0, String arg1);實際的名稱會變成如上所示的?arg0,?arg1?這樣沒有太多含義的, 畢竟解析器并不關心實際的名稱, 有含義的名稱只是給人閱讀的而已.
下面就說說怎么去引入這個編譯選項以使得可以保留有意義的參數(shù)名, 包括 maven 中的設置及 IDE 中的設置(包括 Intellij IDEA 和 Eclipse)
maven 編譯時的選項
對于 maven, 可以在編譯插件 maven-compiler-plugin 中使用 compilerArgs 增加參數(shù)來實現(xiàn):
<plugin><groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<configuration>
<compilerArgs>
<arg>-parametersarg>
compilerArgs>
configuration>
plugin>
注: 對于較新的 maven 版本(>= 3.6.2), 也可以直接使用 parameters 配置項:
<plugin><groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<configuration>
<parameters>trueparameters>
configuration>
plugin>
參見:?https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#parameters
另注: 如果你使用 spring boot 2.0 及以后的版本并依賴了 spring-boot-starter-parent, 默認情況下已經(jīng)啟用了這一選項:
參見這里的說明:?https://stackoverflow.com/questions/31845676/how-to-compile-spring-boot-applications-with-java-8-parameter-flag/49316086#49316086
你可以通過查看最終生效的 Effective POM 來確認這一點, 對于 Eclipse, 在打開的 pom.xml 文件下方選項卡中選擇"Effective POM", 然后搜索 maven-compiler-plugin 關鍵字可以找到相關配置:?
對于 Intellij IDEA, 在 maven 窗口中 右鍵--Show Effective POM:?
或者通過實際是否正常運行來確認這一點, 如果不是很確定, 當然你可以如上所述在自己的 pom.xml 文件中顯式地配置上它.
IDE 編譯時保留參數(shù)名稱
說完了 maven 中的配置, 下面再說說在 IDE 中的類似設置.
注: 通常, 如果 maven 中設置了相應選項, 在項目作為 maven 項目導入并構建時, 這些額外的設置也會生效, 無需額外再作設置. 但考慮到 IDE 的版本及可能存在 bug 等各類原因, 如果在 IDE 中運行不正常, 那么則需要額外檢查及配置.
Intellij IDEA 中的設置 -parameters
對于 IDEA, 在下述位置 Settings > Build, Execution, Deployment > Compiler > Java Compiler > Additional command line parameters(額外的命令行參數(shù)) 的輸入框中, 輸入-parameters:
參考:?https://stackoverflow.com/questions/39217830/how-to-use-parameters-javac-option-in-intellij
如前所述, 如果沒有設置也運行正常, 則不必去設置.
Eclipse 中的設置 -parameters
對于 Eclipse, 則是檢查 Store information about method parameters 選項, 看看是否已經(jīng)是勾選上, 如果沒有, 則把它勾上:
如前所述, 如果沒有勾選也運行正常或者默認已經(jīng)勾選上了, 則不必再去勾選.
另注: 因為以上設置涉及編譯, 所以通常需要重新編譯項目(如果設置后沒有自動觸發(fā) rebuild), 如果還不生效, 甚至可能需要重啟 IDE.
沒有配置成功時的異常
如果沒有加入 -parameters 選項或因其它原因沒有啟用成功, 則去掉 @Param 注解后可能會遇到下述異常:
exception: org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'username' not found. Available parameters are [arg1, arg0, param1, param2]
完整的異常如下:
exception:org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'username' not found. Available parameters are [arg1, arg0, param1, param2]
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
at com.sun.proxy.$Proxy78.selectList(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:230)
at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:137)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:75)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)
at com.sun.proxy.$Proxy86.findUserByUsernameAndPassword(Unknown Source)
at net.xiaogd.demo.mybatis.dao.user.UserDaoTest.testXmlDao(UserDaoTest.java:76)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'username' not found. Available parameters are [arg1, arg0, param1, param2]
at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:202)
at org.apache.ibatis.reflection.wrapper.MapWrapper.get(MapWrapper.java:45)
at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:122)
at org.apache.ibatis.executor.BaseExecutor.createCacheKey(BaseExecutor.java:219)
at org.apache.ibatis.executor.CachingExecutor.createCacheKey(CachingExecutor.java:146)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:82)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433)
... 38 more
那么, 按照其提示, 可以將相應的 xml 語句調(diào)整為:
<select id="findUser" resultType="net.xiaogd.demo.mybatis.entity.User">select * from user where username = #{arg0} and password = #{arg1}
select>
或者是使用 param1, param2 這樣的名稱(注意, 與 arg 從 0 編號不同, 這里是從 1 開始編號)
<select id="findUser" resultType="net.xiaogd.demo.mybatis.entity.User">select * from user where username = #{param1} and password = #{param2}
select>
自然, 使用這些沒有太多含義的編號參數(shù)名, 代碼的可讀性就差了很多, 參數(shù)是否正確對上了也不容易看出來.
mybatis 版本及 useActualParamName(use-actual-param-name) 的問題
最后, 還有一個配置 useActualParamName(使用實際的參數(shù)名稱) 可能導致一些異常, 這點與 mybatis 不同版本的缺省配置不同有關, 也與項目本身是否顯式配置了這一參數(shù)值有關.
在沒有啟用 -parameters 以保留方法參數(shù)名并且沒有用 @Param 設置一個有效的名稱時, 有時你可能會發(fā)現(xiàn)使用 arg0 也還是提示找不到參數(shù):
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'arg0' not found. Available parameters are [0, 1, param1, param2]
完整的異常如下:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'arg0' not found. Available parameters are [0, 1, param1, param2]at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
at com.sun.proxy.$Proxy78.selectList(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:230)
at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:137)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:75)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)
at com.sun.proxy.$Proxy86.findUserByUsernameAndPassword(Unknown Source)
at net.xiaogd.demo.mybatis.dao.user.UserDaoTest.testXmlDao(UserDaoTest.java:76)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'arg0' not found. Available parameters are [0, 1, param1, param2]
at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:202)
at org.apache.ibatis.reflection.wrapper.MapWrapper.get(MapWrapper.java:45)
at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:122)
at org.apache.ibatis.executor.BaseExecutor.createCacheKey(BaseExecutor.java:219)
at org.apache.ibatis.executor.CachingExecutor.createCacheKey(CachingExecutor.java:146)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:82)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433)
... 38 more
而這是由于 mybatis 版本及 useActualParamName(use-actual-param-name) 選項設置的原因.
在早期的版本中(< 3.4.1), useActualParamName 選項的默認值是 false, 而之后的版本(>= 3.4.1), 這個選項的默認值則是 true.
只有設置為 true, 才能利用 -parameters 配置帶來的好處.
或者就是項目本身配置了其它不同于缺省的值, 如果使用了 spring boot 項目, 可以檢查如下選項:
mybatis.configuration.use-actual-param-name=false或是在 xml 配置文件中設置:
<setting name="useActualParamName" value="false" />如果設置了 false, 那么就要寫成 #{0}, #{1} 這樣:
<select id="findUser" resultType="net.xiaogd.demo.mybatis.entity.User">select * from user where username = #{0} and password = #{1}
select>
如果以前有大量這樣的寫法, 而你為了兼容它們不想去調(diào)整, 那你就無法利用 -parameters 配置帶來的好處.
無論是使用 {0}, {1}, 還是使用 {arg0}, {arg1}, 可讀性都不是很好, 而且在后續(xù)如果需要增加參數(shù), 還容易引入錯誤, 因此并不推薦這樣的寫法.
關于啟用 -parameters 編譯選項簡化 mybatis @Param 注解重復問題就介紹到這里.
總結
以上是生活随笔為你收集整理的@param注解_启用 parameters 编译选项简化 mybatis @Param 注解重复问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pandas fillna_【Pytho
- 下一篇: 32位选择进位加法器_32位加减法器设计