生命不息,折騰不止。
折騰能遇到很多坑,填坑我理解為成長。
兩個月前自己倒騰了一套用開源框架構建的 JavaWeb 后端解決方案。
Spring + SpringMVC + Druid + JPA(Hibernate impl) 給你一個穩妥的后端解決方案
引入到項目組后經過幾番打磨,現在也出落的有模有樣。
最近將工程中的 Hibernate 換了換 Mybatis 試試,畢竟人都需要新鮮感。
我 Hibernate 接觸的要比 MyBatis 早,作為最流行的兩 ORM 框架,個人認為其中很多思想都相通。
但 MyBatis 特有的 ResultMap 構想,能進行更為細致的 SQL 調整和優化。
在開發社區、版本更新速度、支持的工具上,Hibernate 比 MyBatis 更勝一籌。
項目 Git 地址:https://git.oschina.net/LanboEx/sdm
1.方案整體一覽
由 Controller 層接受前端參數並響應請求,攜帶數據跳轉頁面。
Controller 層注入 ServiceInter, ServiceImpl 層組織業務數據。
ServiceImpl 層注入 Mybatis Mapper, Mapper 進行數據的訪問。
和 Hibernate 類似整個 dao 層,都可以由工具生成,工程中使用的是 org.mybatis.generator 插件。


2.遇到的坑
淺坑這里就不說了,下面梳理比較深的幾個坑。
如果你以前遇到過這些問題,並且有比我還完美的解決方法,請賜教。
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文件夾下
工程中需要輸出到編譯目錄的配置文件有兩部分,各數據表 mapper.xml 和 框架之間的各種各種的 .xml/.properties。
編譯運行時報錯: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 插件運行機制還是有點問題的,生成的類它跳過,但配置文件會將內容追加進去,所以才有了上述那個報錯。
移除 generator 插件 executions --> execution 下 goals 子節點問題得以解決。
<executions>
<execution>
<id>Generate MyBatis Artifacts</id>
</execution>
</executions>
需要逆向工程時,手動啟動插件即可。