什么是MyBatis-Spring?

MyBatis-Spring就是帮助你将MyBatis代码无缝的整合到Spring中。Spring将会加载必要的sqlSessionFactory类和session类。

配置数据源

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:app.properties</value>
</list>
</property>
</bean> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property> <property name="maxPoolSize" value="20" />
<property name="minPoolSize" value="5" />
<property name="acquireIncrement" value="3" />
<property name="initialPoolSize" value="5"></property>
</bean>

配置SqlSessionFactory

我们需要在Spring应用上下文定义一个SqlSessionFactory和数据源dataSource。在MyBatis-Spring中,SqlSessionFacotoryBean调用其getObject()方法去创建SqlSessionFactory实例。

创建SqlSessionFactory实例中用到了享元设计模式,是一个种结构型模式。

  • 享元模式(Flyweight Pattern):主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了极少对象数量从而改善应用所需的对象结构的方式。

    • 优点:大大减少对象的创建,降低系统的内存,提供效率。
    • 缺点: 提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部的状态而改变,否则会造成系统的混乱。

言归正传,首先在这个SqlSessionFactoryBean类中,调用getObject()时,会判断sqlSessionFacotory是否为空,如果不为空直接返回sqlSessionFacotory。如果为空,则调用afterPropertiesSet()方法。

public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
this.afterPropertiesSet();
} return this.sqlSessionFactory;
}

afterPropertiesSet()会检查dataSourcesqlSessionFactoryBuilder是否为空。接着会调用buildSqlSessionFactory()方法。

public void afterPropertiesSet() throws Exception {
Assert.notNull(this.dataSource, "Property 'dataSource' is required");
Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = this.buildSqlSessionFactory();
}

buildSqlSessionFactory()方法会读取xml配置,然后创建sqlSessionFactory实例。

protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
//...省略几百行代码
return this.sqlSessionFactoryBuilder.build(configuration);
}

言归正传,配置如下:

  • mapperLocations: 我们存放MyBatismapper.xml文件路径。
  • typeAliasesPackage:一般对应着我们实体类所在的包。多个package之间可以用逗号或者分号等来进行分隔。
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="com.helping.wechat.model"></property>
<property name="mapperLocations" value="classpath*:com/helping/wechat/model/**/mysql/*Mapper.xml" />
</bean>

事务

一个使用MyBatis-Spring的主要原因是它允许MyBatis参与到Spring的事务管理中,而不是给MyBatis创建一个新的事务管理器。MyBatis-Spring利用了存在Spring中的org.springframework.jdbc.datasource.DataSourceTransactionManager

我们可以通过配置<tx:annotation-driven transaction-manager="transactionManager"/>来使用@Transactional注解面对切面编程。同时我们也可以通常的AOP来配置Service层的事务。

在事务处理期间,一个单独的SqlSession对象将会被创建和使用,当事务完成时,这个Session就会以合适的方式进行提交和回滚。

具体配置如下:

 <!-- 声明式事务 -->
<bean name="txmanager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 事务开启必须使用session -->
<property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 当Service层的方法不是以delete,insert,update,save方法开头时,发生异常时无法进行事务回滚
但是可以通过@Transactional注解在类或者方法上,可以启动事务。
-->
<tx:annotation-driven transaction-manager="transactionManager"/> <!-- 任何RuntimeException将触发事务回滚,但是任何Checked Exception不会触发事务回滚 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="delete*" propagation="REQUIRED" read-only="false" isolation="DEFAULT"/>
<tx:method name="insert*" propagation="REQUIRED" read-only="false" isolation="DEFAULT"/>
<tx:method name="update*" propagation="REQUIRED" read-only="false" isolation="DEFAULT"/>
<tx:method name="save*" propagation="REQUIRED" read-only="false" isolation="DEFAULT"/>
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice> <aop:config>
<aop:pointcut expression="execution(* com.helping.wechat.service..*.*(..))" id="pc"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc" order="1"/>
</aop:config>

使用SqlSession

  • MyBatis中,我们可以使用SqlSessionFactory来创建SqlSession。一旦获得了一个session之后,我们可以使用它来执行映射语句,提交或者回滚。当我们不需要它的时候,我们可以关闭session

  • 而在MyBatis-Spring之后,你不再需要SqlSessionFactory了,我们的Dao层可以注入一个线程安全的SqlSession,然后进行提交或者回滚,最后关闭session

SqlSession Template

SqlSession TemplateMyBatis-Spring的核心,这个类负责MyBatisSqlSession

this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);

我们可以知道SqlSessionTemplate类的构造器第一个参数是SqlSessionFactory的实例。SqlSessionTemplate实现了SqlSession接口。

public class SqlSessionTemplate implements SqlSession {
//...省略n行
}

之前的我是这样配置的:

<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean> <bean id="baseDao" abstract="true">
<property name="sqlSessionTemplate" ref="sqlSessionTemplate" />
</bean> <bean id="adminUserDao" class="com.helping.wechat.dao.admin.mybatis.AdminUserDao" parent="baseDao" />

其实没有必要去Spring.xml去配置Dao层,Service层,Controller类。可以用注解代替,后面会讲到。对了,SqlSessionTemplate其实也不需要配,SqlSession我们可以通过new SqlSessionTemplate()创建出来。

SqlSessionDaoSupport

SqlSessionDaoSupport是一个抽象的支持类,为你提供SqlSession。我们自定义的Dao层的实现类继承它就行了。调用getSession()做你想要的。SqlSessionDaoSupport内部实现:

public abstract class SqlSessionDaoSupport extends DaoSupport {

  private SqlSession sqlSession;

  private boolean externalSqlSession;

  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
} public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSession = sqlSessionTemplate;
this.externalSqlSession = true;
} /**
* Users should use this method to get a SqlSession to call its statement methods
* This is SqlSession is managed by spring. Users should not commit/rollback/close it
* because it will be automatically done.
*
* @return Spring managed thread safe SqlSession
* 返回线程安全的sqlSession
*/
public SqlSession getSqlSession() {
return this.sqlSession;
} /**
* {@inheritDoc}
*/
protected void checkDaoConfig() {
notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
}
}

看到getSession()方法的注释写着,用户不应该去提交,回滚,或者关闭session的操作,因为session会自动关闭它。这个方法返回的是一个线程安全的sqlSession


使用注解代替XML的配置

如果不使用注解,你的Spring.xml配置肯定是多的爆炸,比如这样:

    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean> <bean id="baseDao" abstract="true">
<property name="sqlSessionTemplate" ref="sqlSessionTemplate" />
</bean> <bean id="adminUserDao" class="com.helping.wechat.dao.admin.mybatis.AdminUserDao" parent="baseDao" /> <bean id="adminUserService" class="com.helping.wechat.service.admin.impl.AdminUserService" >
<property name="adminUserDao" ref="adminUserDao" />
</bean> <bean id="adminUserController" class="com.helping.wechat.controller.admin.AdminUserController" >
<property name="adminUserService" ref="adminUserService" />
</bean> <bean id="ordinaryUserDao" class="com.helping.wechat.dao.admin.mybatis.OrdinaryUserDao" parent="baseDao" /> <bean id="ordinaryUserService" class="com.helping.wechat.service.admin.impl.OrdinaryUserService" >
<property name="ordinaryUserDao" ref="ordinaryUserDao" />
</bean> <bean id="ordinaryUserController" class="com.helping.wechat.controller.admin.OrdinaryUserController" >
<property name="ordinaryUserService" ref="ordinaryUserService" />
</bean>

如果使用了@Service@Component@Repository@Controller@Resource@Autowired 这些注解,这些配置统统可以不要。首先要在SpringMVC.xml配置自动扫描注解的tag

        <context:component-scan base-package="com.helping.wechat.controller" />
<context:component-scan base-package="com.helping.wechat.handler" />
<context:component-scan base-package="com.helping.wechat.dao" />
<context:component-scan base-package="com.helping.wechat.service" />
  • @Service用于标注业务Service层的类上,有value这个属性。
@Service(value = "adminUserService")
public class AdminUserService implements IAdminUserService { @Resource(name = "adminUserDao")
private IAdminUserDao adminUserDao;
}
  • @Controller用于标注控制Controller层的类上。
@Controller
@RequestMapping("/api/admin")
public class AdminUserController extends BaseController { @Resource(name = "adminUserService")
private IAdminUserService adminUserService;
}
  • @Repository用于标注在Dao层的类上,有value这个属性。
@Repository(value = "adminUserDao")
public class AdminUserDao extends SqlSessionDaoSupport implements IAdminUserDao { }
  • @Component就是把类注册到Spring.xml文件中。
@Component
public abstract class BaseDao {
protected SqlSession sqlSession; protected SqlSessionFactory sqlSessionFactory; @Resource(name = "sqlSessionFactory")
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
} public void setSqlSession(SqlSession sqlSession) {
this.sqlSession = sqlSession;
} public SqlSession getSqlSession() {
return sqlSession;
}
}

Dao层的类继承它就行了,就可以调用getSqlSession()进行想要的操作了。

  • @Resource默认按名称进行装配字段或者setter方法。名称可以通过name属性进行指定,如果没有指定name属性。当注解写在字段上,默认按字段名进行安装名称查找,如果注解写在setter方法上,默认按属性名进行装配,当找不到与名称相匹配的bean时候,才会按照类型进行装配。如果name属性指定了,那么就只能按名称进行装配了。
@Service(value = "adminUserService")
public class AdminUserService implements IAdminUserService { @Resource(name = "adminUserDao")
private IAdminUserDao adminUserDao;
}
  • @Autowired:默认按类型装配,默认情况下必须要求依赖对象必须存在,如果要运行null值,可以设置它的required属性为false。如果我们想要使用名称进行装配,可以结合@Qualifier注解使用。
    @Autowired
public void setAdminUserService(IAdminUserService adminUserService) {
this.adminUserService = adminUserService;
}

尾言

明天去看看通用MapperpageHelper怎么配置。晚安,地球人。

作者:cmazxiaoma
链接:https://www.jianshu.com/p/67c99ebdd5d8
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

MyBatist庖丁解牛(四)的更多相关文章

  1. MyBatist庖丁解牛(一)

    站在巨人的肩膀上,感谢! https://www.jianshu.com/p/ec40a82cae28?utm_campaign=maleskine&utm_content=note& ...

  2. MyBatist庖丁解牛(五)

    很多时候我们在自己的每个service中没有中注入SqlSessionTemplate; 但是我们直接调用mapper接口方法就直接能够操作数据库 这个是为什么??下面开始解惑: Mybatis Sq ...

  3. MyBatist庖丁解牛(三)

    从MyBatis代码实现的角度来看,MyBatis的主要的核心部件有以下几个: SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能: Ex ...

  4. MyBatist庖丁解牛(二)

    站在巨人的肩膀上 https://blog.csdn.net/xiaokang123456kao/article/details/76228684 一.概述 我们知道,Mybatis实现增删改查需要进 ...

  5. TLD网络资源汇总--学习理解之(四)

    原文:http://blog.csdn.net/mysniper11/article/details/8726649 引文地址:http://www.cnblogs.com/lxy2017/p/392 ...

  6. 干货 | 45张图庖丁解牛18种Queue,你知道几种?

    在讲<21张图讲解集合的线程不安全>那一篇,我留了一个彩蛋,就是Queue(队列)还没有讲,这次我们重点来看看Java中的Queue家族,总共涉及到18种Queue.这篇恐怕是市面上最全最 ...

  7. 构建一个基本的前端自动化开发环境 —— 基于 Gulp 的前端集成解决方案(四)

    通过前面几节的准备工作,对于 npm / node / gulp 应该已经有了基本的认识,本节主要介绍如何构建一个基本的前端自动化开发环境. 下面将逐步构建一个可以自动编译 sass 文件.压缩 ja ...

  8. 《Django By Example》第四章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:祝大家新年快乐,这次带来<D ...

  9. 如何一步一步用DDD设计一个电商网站(四)—— 把商品卖给用户

    阅读目录 前言 怎么卖 领域服务的使用 回到现实 结语 一.前言 上篇中我们讲述了“把商品卖给用户”中的商品和用户的初步设计.现在把剩余的“卖”这个动作给做了.这里提醒一下,正常情况下,我们的每一步业 ...

随机推荐

  1. PAT 天梯赛 L1-049. 天梯赛座位分配 【循环】

    题目链接 https://www.patest.cn/contests/gplt/L1-049 思路 用一个二维数组来保存一个学校每个队员的座位号 然后需要判断一下 目前的座位号 与该学校当前状态下最 ...

  2. Shell中括号的作用

    Shell中括号的作用 作者:Danbo 时间:2015-8-7 单小括号() ①.命令组.括号中的命令将会断开一个子Shell顺序执行,所以括号中的变量不能被脚本余下的部分使用.括号中多个命令之间用 ...

  3. DIY固件系列教程——实现开机LOGO三屏动画的完全替换【转】

    本文转载自:http://blog.csdn.net/sdgaojian/article/details/9192433 本教程需要用到如下工具:1,7Z压缩工具2,AddCrc32效验工具3,raw ...

  4. Ubuntu 14.04 下 android studio 安装 和 配置【转】

    本文转载自:http://blog.csdn.net/xueshanfeihu0/article/details/52979717 Ubuntu 14.04 下 android studio 安装 和 ...

  5. shell动态变量

    面对变量中嵌套变量,可以这么做 other_devops_ip="......." options='_ip' tennat_name='other_devops' tennat_ ...

  6. 对服务器上所有Word文件做全文检索的解决方案-Java

    一.背景介绍    Word文档与日常办公密不可分,在实际应用中,当某一文档服务器中有很多Word文档,假如有成千上万个文档时,用户查找打开包含某些指定关键字的文档就变得很困难,目前这一问题没有好的解 ...

  7. <编程>比较两种素数表生成算法+计算程序运行时间+通过CMD重定向测试程序

    最近学习加密算法,需要生成素数表,一开始使用简单的循环,从2开始判断.代码如下: #include<iostream> #include<cstdio> #include< ...

  8. 并不对劲的bzoj5322:loj2543:p4561:[JXOI2018]排序问题

    题目大意 \(T\)(\(T\leq10^5\))组询问 每次给出\(n,m,l,r\),和\(n\)个数\(a_1,a_2,...,a_n\),要找出\(m\)个可重复的在区间\([l,r]\)的数 ...

  9. SDOI2017 Round1 Day2 题解

    T2好厉害啊……AK不了啦……不过要是SCOI考这套题就好了240保底. BZOJ4819 新生舞会 模板题,分数规划+二分图最大权匹配. 费用流跑得过,可以不用KM. UPD:分数规划用迭代跑得飞快 ...

  10. asio 中strand的作用

    namespace { // strand提供串行执行, 能够保证线程安全, 同时被post或dispatch的方法, 不会被并发的执行. // io_service不能保证线程安全 boost::a ...