什麼是動態SQL,就是在不一樣的條件下,sql語句不相同的意思,曾經在「酒店會員管理系統」中寫過大量的多條件查詢,那是在SSH的環境中,因此只能在代碼中進行判斷,如下是其中一個多條件查詢的例子:前端
1 public Collection<Card> getCardsByMN(int requestPage, String cardId, 2 String userName, String typeofcredential, String sex, 3 String integral) { 4 int m=(requestPage-1)*PageSplitConfig.pageSize; 5 int n=PageSplitConfig.pageSize; 6 String sql="from Card where 1=1 "; 7 if(cardId!=null&&!"".equals(cardId.trim())){ 8 sql=sql+" and cardId='"+cardId.trim()+"'"; 9 } 10 if(userName!=null&&!"".equals(userName.trim())){ 11 sql=sql+" and userName='"+userName.trim()+"'"; 12 } 13 if(typeofcredential!=null&&!"".equals(typeofcredential.trim())&&!"''".equals(typeofcredential.trim())){ 14 sql=sql+" and credentialType.credentialTypeName='"+typeofcredential.trim()+"'"; 15 } 16 if(sex!=null&&!"".equals(sex.trim())&&!"不限".equals(sex.trim())){ 17 sql=sql+" and sex='"+sex+"'"; 18 } 19 if(integral!=null&&!"".equals(integral.trim())){ 20 sql=sql+" and integral='"+integral.trim()+"'"; 21 } 22 return this.cardDao.getAllCards(sql,m,n); 23 }
這是在Service方法中進行多條件的判斷並最終造成多條件查詢的SQL代碼的方法,最終該SQL代碼會被DAO執行。java
和這個形式很是類似,Mybatis中對這種狀況充分作了考慮,它使用配置文件的方式以XML標籤的形式對以上的邏輯進行了從新描述。mysql
在SSH環境中,因爲Hibernate不支持動態SQL,因此只能在代碼中進行SQL的動態拼接,這麼作的弊端是顯而易見的,並且該弊端在個人「酒店會員管理系統」中充分的暴露了出來。個人酒店會員管理系統中對對於日誌的處理有兩種形式,一種是多條件查詢日誌,一種是導出日誌,這兩個功能都涉及到了動態SQL拼接的問題,可是因爲不是同一個Service方法,因此動態SQL的拼接必須重寫兩次,這就顯得很是的麻煩,並且很是容易發生錯誤。可是若是使用Mybatis的話這種麻煩就會很是容易避免了。git
這裏的需求假設爲:傳入一個Student對象,程序會根據該對象屬性值是否爲空做爲依據判斷是否將該條件拼接到sql中;在映射文件中的配置方式和普通查詢相比使用的標籤都是select標籤,可是裏面須要使用mybatis中的邏輯判斷標籤進行判斷:github
<!-- 這裏是動態查詢的方法 --> <select id="selectAllByCondition" parameterType="com.kdyzm.domain.Student" resultMap="studentMap"> select * from student <where> <if test="id !=null"> and studentid=#{id} </if> <if test="name !=null"> and name=#{name} </if> <if test="age!=null"> and age=#{age} </if> <if test="password !=null"> and password=#{password} </if> </where> </select>
where標籤是一個很是智能的標籤,它可以經過不一樣的條件自動判斷是否須要加上where關鍵字,若是你傳入的對象中的屬性值都爲空,那麼它就不會加上where關鍵字俄並且不須要使用where 1=1 這種寫法;每一個test中都須要加上and ,if標籤也是智能標籤,不贅述。web
java代碼:spring
1 String statement = "com.kdyzm.domain.Student.selectAllByCondition"; 2 Student student = new Student(); 3 student.setId(1); 4 // student.setAge(13); 5 List<Student> list = session.selectList(statement, student); 6 for (Student stu : list) { 7 System.out.println(stu); 8 }
其方法原理和多條件查詢有諸多類似之處,不贅述。sql
1 <!-- 這裏是動態更新的方法 --> 2 <update id="updateStudentByCandition" parameterType="com.kdyzm.domain.Student"> 3 update student 4 <set> 5 <!-- <if test="id!=null"> 6 studentid=#{id} 7 </if> --> 8 <if test="name!=null"> 9 name=#{name} 10 </if> 11 <if test="password!=null"> 12 password=#{password} 13 </if> 14 <if test="age!=null"> 15 age=#{age} 16 </if> 17 <if test="clazz!=null"> 18 clazz=#{clazz} 19 </if> 20 </set> 21 where studentid=#{id} 22 </update>
這裏的動態sql的使用方法須要注意的事項:必須至少有一個字段不爲空,不然就會報錯;我本想着若是將以上代碼中的註釋部分放開的話就可以避免這種狀況了,可是這只是理想情況,若是加上了id的判斷,就會出錯,緣由未知。數據庫
項目源代碼:https://github.com/kdyzm/day79_2_ssiexpress
mybatis框架的功能和hibernate功能類似,既然hibernate可以和spring整合並和struts框架整合以後造成所謂的SSH框架,那麼mybatis框架能不能和spring、hibernate整合以後造成SSI框架呢?(Mybatis前身是Ibatis,到如今爲止該框架中的不少包結構仍然採用ibatis的稱呼,因此mybatis只是換了個名稱而已,這裏仍然使用SSI的稱呼也只是從衆而已),確定是能夠的。hibernate和Mybatis同樣,就算沒有spring也可以獨立運行,可是既然要和spring整合了,那麼Session對象的建立必定都交給了spring容器,這就須要一箇中間的插件了,hibernate和spring整合的時候有一個org.springframework.orm-3.1.0.RELEASE.jar包,該jar包是hibernate和spring整合的關鍵,包括整合的過程當中使用到的「本地會話工廠Bean」以及事務管理器,在該包中都有定義,spring發行版中會自帶和hibernate整合相關的jar包,可是mybatis並無這種待遇,mybatis爲了可以爲了和spring進行整合,它本身開發了和spring整合的jar包。這個jar包須要咱們本身到網上下載。
和spring整合須要的jar包的下載地址:https://github.com/mybatis/spring
這裏我是用了spring3.2的版本,有不少人習慣使用mybatis-spring-1.0.0版本的插件,可是在spring3.2版本下使用該插件的話會出現直接拋出某個異常,可使用mybatis-spring-1.1.1版本的插件解決該異常,並且使用前者查詢出來的結果須要強制類型轉換,使用後者的時候就不須要進行強制類型轉換。
mybatis和spring整合的過程與hibernate和spring整合的過程幾乎徹底相同,有了hibernate和spring整合的經驗,mybatis和spring整合就太簡單了。
使用的spring的版本:spring3.2
使用的數據庫鏈接池:c3p0-0.9.5
使用的mybatis版本:mybatis3.1.1
使用的mybatis-spring版本:mybatis-spring-1.1.1
這裏就不和struts2進行整合了,實際上和struts2的整合就徹底沒有必需要了,實際上和struts2的整合仍是和spring的整合,略。
jar包一覽圖:
1 c3p0-0.9.5.jar 2 com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar 3 com.springsource.net.sf.cglib-2.2.0.jar 4 com.springsource.org.aopalliance-1.0.0.jar 5 com.springsource.org.apache.commons.codec-1.3.0.jar 6 com.springsource.org.apache.commons.logging-1.1.1.jar 7 com.springsource.org.aspectj.tools-1.6.6.RELEASE.jar 8 com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar 9 com.springsource.org.quartz-1.6.2.jar 10 commons-lang-2.5.jar 11 log4j.jar 12 mchange-commons-java-0.2.9.jar 13 mybatis-3.1.1.jar 14 mybatis-spring-1.1.1.jar 15 mysql-connector-java-5.1.18-bin.jar 16 org.springframework.aop-3.1.0.RELEASE.jar 17 org.springframework.asm-3.1.0.RELEASE.jar 18 org.springframework.aspects-3.1.0.RELEASE.jar 19 org.springframework.beans-3.1.0.RELEASE.jar 20 org.springframework.context-3.1.0.RELEASE.jar 21 org.springframework.context.support-3.1.0.RELEASE.jar 22 org.springframework.core-3.1.0.RELEASE.jar 23 org.springframework.expression-3.1.0.RELEASE.jar 24 org.springframework.jdbc-3.1.0.RELEASE.jar 25 org.springframework.orm-3.1.0.RELEASE.jar 26 org.springframework.transaction-3.1.0.RELEASE.jar 27 org.springframework.web-3.1.0.RELEASE.jar 28 org.springframework.web.servlet-3.1.0.RELEASE.jar 29 slf4j-api-1.5.8.jar 30 slf4j-log4j12.jar
這裏使用jdbc.properties,將其放到classpath的根目錄下
1 jdbc.username=root 2 jdbc.password=5a6f38 3 jdbc.url=jdbc:mysql://localhost:3306/mybatis 4 jdbc.driver=com.mysql.jdbc.Driver 5 6 #C3p0 Configuration 7 c3p0.minPoolSize=2 8 c3p0.maxPoolSize=10 9 c3p0.initPoolSize=2 10 c3p0.increment=2
由於該配置文件中須要配置的全部內容都轉移到了spring中的配置文件中進行配置了。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/tx/spring-tx-2.5.xsd"> <context:component-scan base-package="com.kdyzm.dao"></context:component-scan> <context:property-placeholder location="classpath:jdbc.properties" /> <!-- 第一步仍是配置數據源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="driverClass" value="${jdbc.driver}"></property> <property name="initialPoolSize" value="${c3p0.initPoolSize}"></property> <property name="minPoolSize" value="${c3p0.minPoolSize}"></property> <property name="maxPoolSize" value="${c3p0.maxPoolSize}"></property> <property name="acquireIncrement" value="${c3p0.increment}"></property> </bean> <!-- 下一步,配置Session建立工廠 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <!-- <property name="configLocation" value="classpath:mybatis-config.xml"></property> --> <property name="mapperLocations"> <list> <value>classpath:com/kdyzm/domain/Student.xml</value> </list> </property> </bean> <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg> </bean> </beans>
重點是黃色背景部分的配置,形式上和hibernate的和spring的整合過程如出一轍,只是換了個類而已。
以上沒有配置事務,可是已經可以進行正常測試了。
測試的過程略,想看直接到git中的相應項目中查看。
和hibernate和spring整合過程當中事務的配置方式一模一樣,可是須要注意下面黃色背景部分的配置,事務管理器和hibernate使用的org.springframework.orm.hibernate3.HibernateTransactionManager是不相同的。
<!-- 事務的配置 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="save*" isolation="DEFAULT" read-only="false" propagation="REQUIRED"/> <tx:method name="update*" isolation="DEFAULT" read-only="false" propagation="REQUIRED"/> <tx:method name="new*" isolation="DEFAULT" read-only="false" propagation="REQUIRED"/> <tx:method name="delete*" isolation="DEFAULT" read-only="false" propagation="REQUIRED"/> <tx:method name="get*" read-only="true"/> <tx:method name="select*" read-only="true"/> <tx:method name="find*" read-only="true"/> <!-- 其它方法默認所有加上事務 --> <tx:method name="*" isolation="DEFAULT" read-only="false" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut expression="execution(* com.kdyzm.service.*.*(..))" id="txAop"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txAop"/> </aop:config>
產生的疑問:保存Student對象的同時不能級聯保存Clazz的問題,因爲使用的是OGNL表達式,因此在前端頁面中使用的Struts2的規則能夠仿照這搬過來。
測試代碼:
StudentService service=(StudentService) context.getBean("studentService"); ClazzService clazzService=(ClazzService) context.getBean("clazzService"); Student student=new Student(); student.setId(8); student.setName("小吧"); student.setPassword("xiaozhang"); student.setAge(120); Clazz clazz=clazzService.getClazzById(2); student.setClazz(clazz); int result=service.saveStudent(student); System.out.println("執行結果是:"+result);
配置文件:
<insert id="insertIntoStudent" parameterType="com.kdyzm.domain.Student"> insert into student(studentid,name,password,age,clazz) values(#{id},#{name},#{password},#{age},#{clazz.id}) </insert>
注意在這裏的寫法:若是直接寫#{clazz},確定直接報錯,由於數據庫中的字段類型是int類型,可是clazz在這裏是Clazz對象。