github上有一个Mybatis-Spring的项目,专门用于辅助完成mybatis与spring的整合,大大简化了整合难度,使用步骤:
准备工作:
maven依赖项:
1 <properties> 2 <java-version>1.6</java-version> 3 <spring.version>3.2.8.RELEASE</spring.version> 4 </properties> 5 <dependencies> 6 7 <!-- Spring --> 8 <dependency> 9 <groupId>org.springframework</groupId> 10 <artifactId>spring-core</artifactId> 11 <version>${spring.version}</version> 12 </dependency> 13 14 <dependency> 15 <groupId>org.springframework</groupId> 16 <artifactId>spring-expression</artifactId> 17 <version>${spring.version}</version> 18 </dependency> 19 20 <dependency> 21 <groupId>org.springframework</groupId> 22 <artifactId>spring-beans</artifactId> 23 <version>${spring.version}</version> 24 </dependency> 25 26 <dependency> 27 <groupId>org.springframework</groupId> 28 <artifactId>spring-context</artifactId> 29 <version>${spring.version}</version> 30 </dependency> 31 <dependency> 32 <groupId>org.springframework</groupId> 33 <artifactId>spring-context-support</artifactId> 34 <version>${spring.version}</version> 35 </dependency> 36 <dependency> 37 <groupId>org.springframework</groupId> 38 <artifactId>spring-web</artifactId> 39 <version>${spring.version}</version> 40 </dependency> 41 42 <dependency> 43 <groupId>org.springframework</groupId> 44 <artifactId>spring-webmvc</artifactId> 45 <version>${spring.version}</version> 46 </dependency> 47 48 <dependency> 49 <groupId>org.springframework</groupId> 50 <artifactId>spring-oxm</artifactId> 51 <version>${spring.version}</version> 52 </dependency> 53 54 <dependency> 55 <groupId>org.springframework</groupId> 56 <artifactId>spring-jdbc</artifactId> 57 <version>${spring.version}</version> 58 </dependency> 59 60 <dependency> 61 <groupId>org.springframework</groupId> 62 <artifactId>spring-tx</artifactId> 63 <version>${spring.version}</version> 64 </dependency> 65 66 <dependency> 67 <groupId>org.springframework</groupId> 68 <artifactId>spring-aop</artifactId> 69 <version>${spring.version}</version> 70 </dependency> 71 72 73 <dependency> 74 <groupId>org.aspectj</groupId> 75 <artifactId>aspectjweaver</artifactId> 76 <version>1.7.3</version> 77 </dependency> 78 79 <dependency> 80 <groupId>aopalliance</groupId> 81 <artifactId>aopalliance</artifactId> 82 <version>1.0</version> 83 </dependency> 84 85 <!-- json --> 86 <dependency> 87 <groupId>org.codehaus.jackson</groupId> 88 <artifactId>jackson-mapper-asl</artifactId> 89 <version>1.9.3</version> 90 </dependency> 91 92 <dependency> 93 <groupId>org.codehaus.jackson</groupId> 94 <artifactId>jackson-jaxrs</artifactId> 95 <version>1.9.9-redhat-2</version> 96 </dependency> 97 98 <!-- logback --> 99 <dependency> 100 <groupId>org.slf4j</groupId> 101 <artifactId>slf4j-api</artifactId> 102 <version>1.7.7</version> 103 </dependency> 104 105 <dependency> 106 <groupId>ch.qos.logback</groupId> 107 <artifactId>logback-core</artifactId> 108 <version>1.1.2</version> 109 </dependency> 110 111 <dependency> 112 <groupId>ch.qos.logback</groupId> 113 <artifactId>logback-classic</artifactId> 114 <version>1.1.2</version> 115 </dependency> 116 117 <dependency> 118 <groupId>commons-logging</groupId> 119 <artifactId>commons-logging</artifactId> 120 <version>1.1.3</version> 121 </dependency> 122 123 <!-- Servlet --> 124 <dependency> 125 <groupId>javax.servlet</groupId> 126 <artifactId>servlet-api</artifactId> 127 <version>2.5</version> 128 <scope>provided</scope> 129 </dependency> 130 131 <!-- oracle --> 132 <dependency> 133 <groupId>com.oracle</groupId> 134 <artifactId>ojdbc6</artifactId> 135 <version>11.2.0.3</version> 136 </dependency> 137 138 <!-- mybatis --> 139 <dependency> 140 <groupId>org.mybatis</groupId> 141 <artifactId>mybatis-spring</artifactId> 142 <version>1.2.2</version> 143 </dependency> 144 145 <dependency> 146 <groupId>org.mybatis</groupId> 147 <artifactId>mybatis</artifactId> 148 <version>3.2.7</version> 149 </dependency> 150 151 152 </dependencies>
一、先创建entity类
1 package com.cnblogs.yjmyzz.entity; 2 3 import java.io.Serializable; 4 5 public class BasCarrierEntity implements Serializable { 6 7 private static final long serialVersionUID = -971818065984481747L; 8 9 private int recId; 10 private int cyear; 11 private String carrier; 12 13 public int getRecId() { 14 return recId; 15 } 16 17 public void setRecId(int recId) { 18 this.recId = recId; 19 } 20 21 public int getCyear() { 22 return cyear; 23 } 24 25 public void setCyear(int cyear) { 26 this.cyear = cyear; 27 } 28 29 public String getCarrier() { 30 return carrier; 31 } 32 33 public void setCarrier(String carrier) { 34 this.carrier = carrier; 35 } 36 37 public String toString() { 38 return "recid:" + recId + ",cyear:" + cyear + ",carrier:" + carrier; 39 } 40 41 }
这是一个POJO类,没有任何花头.
二、创建Mapper接口
1 package com.cnblogs.yjmyzz.mybatis.mapper; 2 3 import com.cnblogs.yjmyzz.entity.BasCarrierEntity; 4 5 public interface CarrierMapper { 6 7 BasCarrierEntity getCarrierById(int recId); 8 9 void insertCarrier(BasCarrierEntity entity); 10 11 void deleteCarrier(BasCarrierEntity entity); 12 13 }
相当于传统ORM数据库应用里的DAO层接口
三、定义sql映射文件 BasCarrier.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 3 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 4 5 <mapper namespace="com.cnblogs.yjmyzz.mybatis.mapper.CarrierMapper"> 6 7 <cache /> 8 9 <select id="getCarrierById" resultType="BasCarrierEntity" 10 parameterType="int"> 11 select t.recid recId,t.cyear cyear,t.carrier carrier 12 from T_BAS_CARRIER t where 13 t.recid = #{recId} 14 </select> 15 16 <insert id="insertCarrier" parameterType="BasCarrierEntity"> 17 <selectKey keyProperty="recId" order="BEFORE" resultType="int"> 18 select seq_t_bas_carrier.nextval as recId from dual 19 </selectKey> 20 insert into 21 t_bas_carrier(recid,cyear,carrier) 22 values(#{recId,jdbcType=NUMERIC},#{cyear,jdbcType=NUMERIC},#{carrier,jdbcType=VARCHAR}) 23 </insert> 24 25 <delete id="deleteCarrier" parameterType="BasCarrierEntity"> 26 delete from 27 t_bas_carrier where 1=1 28 <if test="carrier != null"> 29 and carrier like #{carrier} 30 </if> 31 </delete> 32 33 </mapper>
以上这三板斧是必不可少的,注:xml中sql语句的id要与mapper接口中的方法名完全相同,这样不用其它任何额外配置,运行时,mybatis就能自动将接口中的方法与sql关联起来。
四、Spring-Database.xml 配置文件
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xsi:schemaLocation=" 7 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 8 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 9 http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd 10 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 11 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd" 12 default-autowire="byName"> 13 14 <bean id="dataSource" 15 class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 16 <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> 17 <property name="url" value="${db-url}" /> 18 <property name="username" value="${db-username}" /> 19 <property name="password" value="${db-password}" /> 20 </bean> 21 22 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 23 <!-- mybatis主配置文件,不能与后面的mapperLocations/typeAliasesPackage同时使用 --> 24 <!-- <property name="configLocation" value="classpath:mybatis-config.xml"></property> --> 25 <property name="dataSource" ref="dataSource" /> 26 <!-- mapper中的类型别名 --> 27 <property name="typeAliasesPackage" value="com.cnblogs.yjmyzz.entity"></property> 28 <!-- mybatis 映射sql xml路径 --> 29 <property name="mapperLocations" value="classpath:mybatis/**/*.xml"></property> 30 </bean> 31 32 <!-- 扫描指定包下的Mapper,自动创建mapper bean实例 --> 33 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> 34 <property name="basePackage" value="com.cnblogs.yjmyzz.mybatis.mapper" /> 35 </bean> 36 37 <!-- 事务管理 --> 38 <bean id="transactionManager" 39 class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 40 <property name="dataSource" ref="dataSource" /> 41 </bean> 42 43 <tx:advice id="txAdvice" transaction-manager="transactionManager"> 44 <tx:attributes> 45 <!-- 仅do开头的Service层方法,发生Exception异常时,才会事务回滚 --> 46 <tx:method name="do*" read-only="false" rollback-for="java.lang.Exception" /> 47 <!-- 其它方法,只读事务,不回滚 --> 48 <tx:method name="*" propagation="SUPPORTS" read-only="true" /> 49 </tx:attributes> 50 </tx:advice> 51 52 <aop:config> 53 <!-- 仅拦截service层方法 --> 54 <aop:pointcut id="pc" 55 expression="execution(* com.cnblogs.yjmyzz.service..*.*(..))" /> 56 <aop:advisor pointcut-ref="pc" advice-ref="txAdvice" /> 57 </aop:config> 58 59 <!-- <tx:annotation-driven /> --> 60 </beans>
真正属于mybatis专用的配置是22-35行,其它都是些db开发的常规配置。
事务这里有一个要特别注意的问题:因为Spring/SpringMVC的父子容器问题,默认情况下@Service的xxxService并不具备增加事务处理能力,需要做点特殊配置
a) spring-mvc servlet的配置文件里,参考下面配置:
1 <context:component-scan base-package="com.cnblogs.yjmyzz"> 2 <context:exclude-filter type="annotation" 3 expression="org.springframework.stereotype.Service" /> 4 </context:component-scan>
即:将@Service所对应的Service类排除掉,因为Spring-MVC compent-scan注入的只是普通的bean,不具备增加事务处理能力,所以要排除掉.
b) spring 主配置文件里,参考下面配置:
<context:component-scan base-package="com.cnblogs.yjmyzz"> </context:component-scan>
这样处理后,compent-scan扫描到的Service类,就交由Spring,而非Spring-MVC来注入了。
另外还有一个事务自动提交的问题,虽然在配置中已经明确指定了仅Service层do开头的方法,才支持可写事务,其它方法都是只读事务,但如果你运行一下会发现,其它非do开头的方法如果执行一条insert语句,仍然会提交成功。
造成这个的原因在于datasource,这里我们采用的是org.springframework.jdbc.datasource.DriverManagerDataSource数据源,这种数据源不支持defaultAutoCommit属性,如果不想使用自动提交,可以把datasource节点换成
1 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 2 destroy-method="close"> 3 <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> 4 <property name="url" value="${db-url}" /> 5 <property name="username" value="${db-username}" /> 6 <property name="password" value="${db-password}" /> 7 <property name="defaultAutoCommit" value="false" /> 8 <property name="initialSize" value="2" /> 9 <property name="maxActive" value="5" /> 10 <property name="maxWait" value="60000" /> 11 </bean>
注意第7行,defaultAutoCommit设置成false即可
如果使用Oracle,事务这里还有一个小坑,read-only不管设置成true/false,效果都是一样的,原因见:http://docs.oracle.com/cd/B19306_01/java.102/b14355/apxtips.htm
Transaction Isolation Levels and Access Modes
Read-only connections are supported by the Oracle server, but not by the Oracle JDBC drivers.
即:使用oracle jdbc驱动时,不支持read-only属性
五、mybatis在eclipse控制台显示sql语句的问题
mybatis支持多种日志组件,默认顺序如下:
• SLF4J
• Apache Commons Logging
• Log4j 2
• Log4j
• JDK logging
而上一篇刚学习的logback组件,正好是SLF4J的实现之一,所以如果采用logback来记录日志,mybatis不用做任何额外配置,将logback.xml扔在resource目录下即可
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <configuration scan="true" scanPeriod="1800 seconds" 3 debug="false"> 4 5 <property name="USER_HOME" value="logs" /> 6 <property scope="context" name="FILE_NAME" value="mybatis-logback" /> 7 8 <timestamp key="byDay" datePattern="yyyy-MM-dd" /> 9 10 <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 11 <encoder> 12 <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 13 </pattern> 14 </encoder> 15 </appender> 16 17 <appender name="file" 18 class="ch.qos.logback.core.rolling.RollingFileAppender"> 19 <file>${USER_HOME}/${FILE_NAME}.log</file> 20 21 <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> 22 <fileNamePattern>${USER_HOME}/${byDay}/${FILE_NAME}-${byDay}-%i.log.zip 23 </fileNamePattern> 24 <minIndex>1</minIndex> 25 <maxIndex>10</maxIndex> 26 </rollingPolicy> 27 28 <triggeringPolicy 29 class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> 30 <maxFileSize>5MB</maxFileSize> 31 </triggeringPolicy> 32 <encoder> 33 <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n 34 </pattern> 35 </encoder> 36 37 </appender> 38 39 <logger name="com.cnblogs.yjmyzz" level="debug" additivity="true"> 40 <appender-ref ref="file" /> 41 <!-- <appender-ref ref="STDOUT" /> --> 42 </logger> 43 44 <root level="debug"> 45 <appender-ref ref="STDOUT" /> 46 </root> 47 </configuration>
注:log的level要设置成DEBUG。
logback与spring mvc整合的方法,上一篇已经讲过,就不再重复了,这里只给出web.xml的配置
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 5 <context-param> 6 <param-name>logbackConfigLocation</param-name> 7 <param-value>classpath:logback.xml</param-value> 8 </context-param> 9 <listener> 10 <listener-class>com.cnblogs.yjmyzz.util.LogbackConfigListener</listener-class> 11 </listener> 12 13 <context-param> 14 <param-name>contextConfigLocation</param-name> 15 <param-value>classpath:root-context.xml</param-value> 16 </context-param> 17 <filter> 18 <filter-name>CharacterEncodingFilter</filter-name> 19 <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> 20 <init-param> 21 <param-name>encoding</param-name> 22 <param-value>utf-8</param-value> 23 </init-param> 24 </filter> 25 <filter-mapping> 26 <filter-name>CharacterEncodingFilter</filter-name> 27 <url-pattern>/*</url-pattern> 28 </filter-mapping> 29 <listener> 30 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 31 </listener> 32 <servlet> 33 <servlet-name>appServlet</servlet-name> 34 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 35 <init-param> 36 <param-name>contextConfigLocation</param-name> 37 <param-value>classpath:servlet-context.xml</param-value> 38 </init-param> 39 <load-on-startup>0</load-on-startup> 40 </servlet> 41 <servlet-mapping> 42 <servlet-name>appServlet</servlet-name> 43 <url-pattern>/</url-pattern> 44 </servlet-mapping> 45 46 </web-app>