生命不息,折騰不止。
折騰能遇到很多坑,填坑我理解為成長。
兩個月前自己倒騰了一套用開源框架構建的 JavaWeb 後端解決方案。
Spring + SpringMVC + Druid + JPA(Hibernate impl) 給你一個穩妥的後端解決方案
引入到項目組後經過幾番打磨,現在也出落的有模有樣。
最近將工程中的 Hibernate 換了換 Mybatis 試試,畢竟人都需要新鮮感。
我 Hibernate 接觸的要比 MyBatis 早,作為最流行的兩 ORM 框架,個人認為其中很多思想都相通。
但 MyBatis 特有的 ResultMap 構想,能進行更為細致的 SQL 調整和優化。
在開發社區、版本更新速度、支持的工具上,Hibernate 比 MyBatis 更勝一籌。
項目 Git 地址:https://git.oschina.net/LanboEx/sdm
由 Controller 層接受前端參數並響應請求,攜帶數據跳轉頁面。
Controller 層注入 ServiceInter, ServiceImpl 層組織業務數據。
ServiceImpl 層注入 Mybatis Mapper, Mapper 進行數據的訪問。
和 Hibernate 類似整個 dao 層,都可以由工具生成,工程中使用的是 org.mybatis.generator 插件。
web.xml pom.xml淺坑這裡就不說了,下面梳理下比較深的幾個坑。
如果你以前遇到過這些問題,並且有比我還完美的解決方法,請賜教。
a. MapperScannerConfigurer 提前初始化導致 spring 注入配置文件失效
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.rambo.sdm.dao.inter"/> <property name="sqlSessionFactory " ref="sessionFactory"/> </bean>
因為希望 Spring 能掃描 Mapper 接口類加載 Mapper.xml 並自動生成實現代理類,注入到相應的 ServiceImpl 中。
剛開始配置如上,但是發現 Spring 無法正常加載配置文件中的信息。
也就是用 ${jdbc.username} 這樣之類的表達式,無法獲取到 properties 文件裡的內容。
幾次嘗試未果之後,發現 MapperScannerConigurer 實際是在解析加載 bean 定義階段,這個時候設置 sqlSessionFactory 的話。
會導致提前初始化一些類,PropertyPlaceholderConfigurer 還沒來得及替換定義中的變量,導致把表達式當作字符串復制了。
將 sqlSessionFactory 替換為 sqlSessionFactoryBeanName 問題解決,配置如下:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.rambo.sdm.dao.inter"/> <property name="sqlSessionFactoryBeanName" value="sessionFactory"/> </bean>
b. dao 層數據表主鍵自動生成
在編寫工程例子運行後,發現提示錯誤 UUID 不為 NUll。
自動生成的 mapper.xml 中,對於主鍵(自增序列/uuid)需要自己配置,這點確實有點 low。
自己配置就自己配置吧,mapper.xml 中 UUID 配置如下:
<selectKey keyProperty="uuid" resultType="String" order="BEFORE"> select replace(uuid(),'-','') UUID </selectKey>
在項目推進中,生成數據表配置文件後需要研發手動在 mapper.xml 中的新增方法中添加主鍵生成策略,不僅繁瑣而且出問題的概率極大。
試著摸索有沒有什麼統一配置的地方,發現了一種但還是不夠完美。統一配置在 generatorConfig.xml 生成表的地方:
<table tableName="user" domainObjectName="UserPO"> <generatedKey column="uuid" sqlStatement="SELECT REPLACE(UUID(),'-','') UUID FROM DUAL"/> </table>
主鍵生成策略使用 SQL 語句這點,就注定 Mybatis 在數據庫移植方面無法盡善盡美。
c. maven 編譯後未將 xml 文件編譯到 class文件夾下
工程中需要輸出的編譯目錄的 xml 有兩部分,各數據表 mapper.xml 和 框架之間的各 xml。
編譯運行時報錯:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)
說是未綁定? 輾轉半天,發現 mapper.xml 沒有被編譯到對應的文件夾下。
maven build --> resources 節點下新增子 resource 子節點:
<resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource>
添加子節點後,mapper.xml 到對應的文件夾下了,但工程中原 Resources 下的文件確沒有像以前編譯到 classes 下。
maven build --> resources 節點下繼續新增子 resource 子節點後解決:
<resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> <filtering>true</filtering> </resource>
d. jetty 插件啟動web 項目時,會同時啟動 mybatis 逆向工程插件
當使用 jetty:run 啟動 web 項目後,總會有莫名其妙的問題。
報錯君是這樣的:java.lang.IllegalArgumentException: Result Maps collection already contains value for com.rambo.sdm.dao.inter.UserPOMapper.BaseResultMap
順著啟動日志發現,發現每次 jetty:run 時,mybatis.generator 插件會先運行,逆向數據庫工程。
逆向生成就逆向生成吧,按道理需要生成的東西已經存在的話,跳過即可。
generator 插件運行機制還是有點問題的,類跳過,但配置文件會將內容追加進去,所以才有了上述那個報錯。