MyBatis(九) 整合Spring、整合SpringMVC

z1340954953發表於2018-07-18

MyBatis整合Spring分為下面幾個部分
* 配置資料來源
* 配置SqlSessionFactory
* 配置SqlSessionTemplate
* 配置Mapper
* 事務處理

配置SqlSessionFactory

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	<!-- jdbc.properties 中的key必須定義為 jdbc.username,格式開頭的 -->
	<property name="username" value="${jdbc.username}"></property>
	<property name="password" value="${jdbc.password}"></property>
	<property name="url" value="${jdbc.url}"></property>
	<property name="driverClassName" value="${jdbc.driverClassName}"></property>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<!-- 依賴資料來源 -->
	<property name="dataSource" ref="dataSource"></property>
	<!-- 依賴mybatis-config.xml配置檔案 -->
	<property name="configLocation" value="classpath:mybatis-config.xml"></property>
</bean>

SqlSessionFactory的建立依賴資料來源和mybatis-config配置檔案(mybatis配置檔案中就不需要再配置資料來源),classpath表示從類路徑下獲取,這樣就建立了mybatis的上下文

需要注意一點,屬性檔案注入時候,key必須是jdbc.開頭的, 比如jdbc.username,jdbc.password,去掉jdbc.會出錯。

SqlSessionFactoryBean的原始碼,看出mybatis的配置可以IOC進行設定,不必全部寫在xml中,再引入,但不建議這麼做。

public class SqlSessionFactoryBean
implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent>
{
private Resource configLocation;
private Resource[] mapperLocations;
private DataSource dataSource;
private TransactionFactory transactionFactory;
private Properties configurationProperties;
private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
private SqlSessionFactory sqlSessionFactory;
private String environment = SqlSessionFactoryBean.class.getSimpleName();
private boolean failFast;
private Interceptor[] plugins;
private TypeHandler<?>[] typeHandlers;
private String typeHandlersPackage;
private Class<?>[] typeAliases;
private String typeAliasesPackage;
private Class<?> typeAliasesSuperType;
private DatabaseIdProvider databaseIdProvider;
private ObjectFactory objectFactory;
private ObjectWrapperFactory objectWrapperFactory;

配置SqlSessionTemplate

SqlSessionTemplate是一個模板類,通過代理生成SqlSession的代理物件執行資料庫操作。

原始碼看出,建立SqlSessionTemplate需要注入一個SqlSessionFactory

public class SqlSessionTemplate implements SqlSession{
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
private final PersistenceExceptionTranslator exceptionTranslator;
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory)
{
  this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType)
{
  this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator)
{
  Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
  Assert.notNull(executorType, "Property 'executorType' is required");
  
  this.sqlSessionFactory = sqlSessionFactory;
  this.executorType = executorType;
  this.exceptionTranslator = exceptionTranslator;
  this.sqlSessionProxy = ((SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor(null)));
}

配置:

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
	<constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg>
	<!-- 指定ExecutorType: simple,batch,resume 預設是simple ,可以不指定 -->
	<constructor-arg index="1" value="BATCH"></constructor-arg>
</bean>

直接使用SqlSessionTemplate(可以不看)

直接在Dao層使用,將它注入到Dao中,並實現一個公共的Dao的基類

public class BaseDAOImpl {
	public SqlSessionTemplate sqlSessionTemplate;

	public SqlSessionTemplate getSqlSessionTemplate() {
		return sqlSessionTemplate;
	}

	public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
		this.sqlSessionTemplate = sqlSessionTemplate;
	}
}
public interface StudentDao {
	Student getStudent(Integer id);
	List<Student> getStudentList(String name);
	int deleteInfo(Integer id);
}
public class StudentDaoImpl extends BaseDAOImpl implements StudentDao {

	@Override
	public Student getStudent(Integer id) {
		Student stu = this.sqlSessionTemplate.selectOne("cn.bing.mybatisTest.StudentDao.getStudent", id);
		return stu;
	}
	@Override
	public List<Student> getStudentList(String name) {
		List<Student> list = this.sqlSessionTemplate.selectList("cn.bing.mybatisTest.StudentDao.getStudentList", name);
		return list;
	}
	@Override
	public int deleteInfo(Integer id) {
		int count = this.sqlSessionTemplate.delete("cn.bing.mybatisTest.StudentDao.deleteInfo", id);
		return count;
	}

}

xml中配置這個Dao

<bean id="studentDao" class="cn.bing.mybatisTest.StudentDaoImpl">
	<property name="sqlSessionTemplate" ref="sqlSession"></property>
</bean>

這和IBatis時代的程式設計方式一致,不建議使用

配置Mapper

大部分場景不建議直接使用SqlSessionTemplate或者SqlSession的方式,而是採用Mapper介面程式設計的方式,讓SqlSession在開發過程中消失。

在MyBatis中,Mapper只需要一個介面,而不是一個實現類,MyBatis會通過動態代理生成一個代理物件來執行。

MyBatis-Spring團隊提供了一個MapperFactoryBean類作為中介,可以生成Mapper,配置這個類需要三個引數

* mapperInterface,定製mapper介面

*  SqlSessionFactory, 當SqlSessionTemplate屬性沒有配置時候,才去啟用

* SqlSessionTemplate,當被設定時候,SqlSessionFactory將作廢

<bean id="studentDao" class = "org.mybatis.spring.mapper.MapperFactoryBean">
	<property name="mapperInterface" value="cn.bing.mapper.StudentMapper"></property>
	<!-- sqlSessionTemplate沒有設定時候,才會啟用 -->
	<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
	<!-- 設定sqlSessionTemplate,sqlSessionFactory設定無效 -->
	<property name="sqlSessionTemplate" ref="sqlSession"></property>
</bean>

但是如果存在多個Dao呢?

使用MapperScannerConfigurer,自動掃描的形式來配置對映器

* basePackage屬性,指定Spring掃描的Mapper包

* annotationClass屬性,表示如果類被這個註解標識的時候,才進行掃描

* sqlSessionFactoryBeanName,指定在Spring中定義sqlSessionFactory的bean名稱,如果它被定義,sqlSessionFactory將不起作用

* sqlSessionTemplateBeanName,指定在Spring中定義sqlSessionTemplate的bean名稱,如果它被定義

sqlSessionFactoryBeanName將不起作用

* markerInterface,指定是實現了什麼介面就認為是Mapper。需要提供一個公共的介面去標識。

在Spring配置中給Dao類加上一個註解@Repository標識,標識是Dao類

@Repository
public interface StudentMapper {
	public Student queryStudentByNameAndSex(@Param("name")String stuName,@Param("sex")String sex);
	public Student queryStudentInfo(@Param("id")int id);
	public int getCount(int id);
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="cn.bing.mapper"></property>
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
		<property name="annotationClass" value="org.springframework.stereotype.Repository"></property>
	</bean>

 配置MapperSannerConfigurer 時候,如果是配置sqlSessionFactory屬性,會報錯Could not load driver Class

,後面改為sqlSessionFacotryBeanName

關於Could not load driverClass ${jdbc.driverClassName}問題解決方案

配置事務

使用宣告式事務配置,有兩個解決方法,一種是xml配置使用aop織入事務,還有一種是啟用註解@Transactional配置

最終的xml配置_springContext.xml

<?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:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

	<context:property-placeholder location="classpath:jdbc.properties" />
	<!-- 註解支援 -->
	<context:annotation-config></context:annotation-config>
	<!-- 掃描包,自動建立bean -->
	<context:component-scan base-package="cn.bing.service"
		use-default-filters="false">
		<context:include-filter type="annotation"
			expression="org.springframework.stereotype.Service" />
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>

	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<!-- jdbc.properties 中的key必須定義為 jdbc.username,格式開頭的 -->
		<property name="username" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
		<property name="url" value="${jdbc.url}"></property>
		<property name="driverClassName" value="${jdbc.driverClassName}"></property>
	</bean>
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 依賴資料來源 -->
		<property name="dataSource" ref="dataSource"></property>
		<!-- 依賴mybatis-config.xml配置檔案 -->
		<property name="configLocation" value="classpath:mybatis-config.xml"></property>
	</bean>
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="cn.bing.mapper"></property>
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
		<property name="annotationClass" value="org.springframework.stereotype.Repository"></property>
	</bean>
	<!-- 配置事務 -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<tx:advice id="advice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="query*" propagation="REQUIRED" read-only="true" />
			<tx:method name="update*" propagation="REQUIRED" read-only="false"
				rollback-for="java.lang.Exception" />
			<tx:method name="add*" propagation="REQUIRED" read-only="false" />
		</tx:attributes>
	</tx:advice>
	<aop:config>
		<aop:pointcut id="studentServicePointCut" expression="execution (* cn.bing.service..*.*(..))" />
		<aop:advisor advice-ref="advice" pointcut-ref="studentServicePointCut" />
	</aop:config>
</beans>

出現的問題:

配置MyBatis的MapperScannerConfigurer的屬性sqlSessionFactory(會出現bug),改為配置為sqlSessionFactoryBeanName,值為SqlSessionFactory配置的Bean的id

整合SpringMVC

將springContext.xml交給ContextLoaderListener載入,優先於SpringMVC.xml被dispatcher載入

出現的問題

springContext.xml 回去掃描@Service的類

SpringMVC.xml會掃描@Controller的類

兩個掃描不能有衝突,否則會出現事務失效的問題

springContext.xml中掃包的時候,加上不能掃描@Controller的類

<context:component-scan base-package="cn.bing.service"
		use-default-filters="false">
		<context:include-filter type="annotation"
			expression="org.springframework.stereotype.Service" />
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>

SpringMVC.xml配置掃包的時候,不能掃描@Service的類

<!-- 配置controller掃描包 -->
	<context:component-scan base-package="cn.bing.controller" use-default-filters="false" >
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
		<!-- !!!最好加上這句讓SpringMVC管理的時候排除Service層,避免事務失效的問題。 -->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service" />
	</context:component-scan>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  	<display-name>zhibing_mybatis</display-name>
  	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:springContext.xml</param-value>
  	</context-param>
	<listener>
		<description>spring listener</description>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>*.action</url-pattern>
	</filter-mapping>
	<servlet>
		<description>spring mvc servlet</description>
		<servlet-name>springmvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<description>springmvc config</description>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springMVC.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>springmvc</servlet-name>
		<url-pattern>*.action</url-pattern>
	</servlet-mapping>
	
    <welcome-file-list>
    	<welcome-file>index.jsp</welcome-file>
  	</welcome-file-list>
</web-app>

 

相關文章