Spring整合Mybatis

整合mybatis和spring,像一个框架一样使用

使用的技术是IOC

  • 为什么ioc能把俩个框架集成在一起?
    是因为ioc能够创建对象。
  • 可以把mybatis框架中的对象交给spring统一创建,开发人员就不用同时面对两个或多个框架了,只需要面对一个spring。

mybatis的使用

定义dao接口,
定义mapper文件
创建dao代理对象 dao.getMapper(StudentDao.class)

  • 主配置文件
  1. 数据库信息
  <!-- 配置数据源(连接池) -->
            <dataSource type="POOLED">
                <!-- 配置连接数据库的4个基本信息 -->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>

我们会使用独立的连接池类替换mybatis默认自己带的,把连接池对象也交给spring创建。

  1. mapper文件位置
  <mappers>
        <mapper resource="com/neuedu/dao/IUserDao.xml"/>
        <mapper resource="com/neuedu/dao/DeptDao.xml"/>
    </mappers>


以上可得我们需要创建的对象
独立的连接池对象, 使用阿里的druid连接池
sqlSessionFactory对象
创建dao对象

整合过程

  • 新建maven项目
  • 加入maven的依赖
    spring依赖
    mybatis依赖
    mysql驱动
    spring事务的依赖
    mybatis和spring集成的依赖:mybatis官方提供的,用来在spring项目中创建mybatis的SqlSessionFactory。dao对象的
  • 创建实体类
  • 创建dao接口和mapper文件
  • 创建mybatis主配置文件
  • 创建spring的配置文件:声明mybatis的对象交给spring创建
    数据源
    sqlSessFactory
    ...

依赖

<dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.1.10.RELEASE</version>
    </dependency>
<!--    spring事务用到的-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.5</version>
    </dependency>
<!--    mybatis spring整合-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
    </dependency>
<!--    阿里的连接池-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.12</version>
    </dependency>
  <build>
<!--    目的是把src/main/java下的xml文件输出到classes中-->
    <resources>
        <resource>
<!--          所在目录-->
          <directory>src/main/java</directory>
<!--          includes中的.properties 和 .xml都会被扫描到-->
          <includes>
            <include>**/*.properties</include>
            <include>**/*.xml</include>
          </includes>
          <filtering>false</filtering>
        </resource>
    </resources>
</build>

Druid连接池

https://github.com/alibaba/druid

  1. 通用配置
    DruidDataSource大部分属性都是参考DBCP的,如果你原来就是使用DBCP,迁移是十分方便的。
 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> 
     <property name="url" value="${jdbc_url}" />
     <property name="username" value="${jdbc_user}" />
     <property name="password" value="${jdbc_password}" />

     <property name="filters" value="stat" />

     <property name="maxActive" value="20" />
     <property name="initialSize" value="1" />
     <property name="maxWait" value="60000" />
     <property name="minIdle" value="1" />

     <property name="timeBetweenEvictionRunsMillis" value="60000" />
     <property name="minEvictableIdleTimeMillis" value="300000" />

     <property name="testWhileIdle" value="true" />
     <property name="testOnBorrow" value="false" />
     <property name="testOnReturn" value="false" />

     <property name="poolPreparedStatements" value="true" />
     <property name="maxOpenPreparedStatements" value="20" />

     <property name="asyncInit" value="true" />
 </bean>

在上面的配置中,通常你需要配置url、username、password,maxActive这三项。
Druid会自动跟url识别驱动类名,如果连接的数据库非常见数据库,配置属性driverClassName
asyncInit是1.1.4中新增加的配置,如果有initialSize数量较多时,打开会加快应用启动时间

在spring配置文件中找其他配置文件需要用classpath:...

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/java_ssm"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>


    <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory01">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis.xml"/>
     </bean>

<!--    连接dao对象
        在内部调用getMapper 生成每个dao接口的代理对象

-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory01"/>
<!--        把所有接口都创建好对象
            多个包用逗号隔开
-->
        <property name="basePackage" value="com.lz.dao"/>
    </bean>
<!--    引入service-->
    <bean class="com.lz.service.impl.AccountServiceImpl" id="accountService">
        <property name="ac" ref="accountDao"/>
    </bean>
</beans>
  • test类

public class MyTest {
    String config = "ApplicationContext.xml";
    ApplicationContext ac = new ClassPathXmlApplicationContext(config);
    @Test
    public void myTest01(){
        AccountServiceImpl service = (AccountServiceImpl) ac.getBean("accountService");
        Account account = new Account();
        account.setMoney(100000);
        account.setName("人生");
        service.insertInto(account);
    }

    @Test
    public void test02(){
        AccountServiceImpl service = (AccountServiceImpl) ac.getBean("accountService");
        List<Account> all = service.findAll();
        for(Account a : all){
            System.out.println(a);
        }
    }
}

  • druid连接池
  • 配置工厂
  • 配置dao包自动创建对象

使用properties文件配置连接池时一定要加jdbc.

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/java_ssm
jdbc.username=root
jdbc.password=root

<!--    扫描jdbc配置文件的位置-->
    <context:property-placeholder location="classpath:druid.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

spring的事务处理

  • 什么是事务?
    在数据库中,提出了事务。事务是指一组sql语句的集合,集合中有多条sql语句,我们希望多个sql语句都能成功,或者都失败,这些sql语句的执行是一致的,作为一个整体执行。

  • 在什么时候想到使用事务
    当操作涉及到多个表,或者是多个sql语句的增删改,需要保证这些语句都是成功才能完成我的功能,或者都失败,保证操作是符合要求的。

  • 通常使用JDBC访问数据库,还是mybatis访问数据库怎么处理事务
    jdbc: conn.commit(); conn.rollback();
    mybatis: sqlsession.commit(); sqlsession.rollback();
    hibername访问数据库:session.commit(); session.rollback();

  • 上个问题中事务的处理方式,有什么不足
    1.不同的数据库访问技术,处理事务的对象,方法不同,需要了解不同数据库访问技术使用事务的原理。
    2.掌握多种数据库中事务的处理逻辑,什么时候提交事务,什么时候回顾事务。
    3.处理事务的多种方法。
    总结就是多种数据库的访问技术,有不同的事务处理机制,对象,方法。

  • 怎么解决不足
    spring提供一种事务处理的统一模型,能使用统一步骤,完成多种不同数据库访问技术的事务处理。
    使用spring的事务处理机制,可以完成mybatis等访问数据库的事务处理。

处理事务,需要怎么做,做什么

把事务信息交给spring就可以了
1.事务内容提交,回滚事务,使用的事务管理器对象,代替你完成commit,rollback
事务管理器是一个接口和他众多的实现类
接口:PlatformTransactionManager,定义了事务重要方法commit, rollback
实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。
mybatis访问数据库-- spring创建好的是DataSourceTransactionManager
怎么使用: 你需要告诉spring 你用的是那种数据库的访问技术,怎么告诉spring呢?
声明数据库访问技术对于的事务管理器的实现类,在spring的配置文件中使用<bean>声明就可以了。
例如:
<bean id="xxx" class="....DataSourceTransactionManager">
2.你的业务需要什么样的事务,说明事务类型。
1. 事务的隔离级别: 都是以ISOLATION_XXX
myql默认是REPEATABLE_READ(可重复读), oracle默认READ_COMMITTED(读以提交)。
DEFAULT:采用DB默认的事务隔离级别。
READ_UNCOMMITTED:读未提交。
READ_COMMITTED:读以提交 解决脏读,存在不可重复读与幻读。
REPEATABLE_READ:可重复读 解决脏读、不可重复读,存在幻读。
SERIALIZABLE:串行化。不存在并发问题。
2. 事务的超时时间: 表示一个方法最长的执行时间,如果方法执行时超过了时间,事务就回滚。单位时秒,整数值, 默认是 -1。

  1. 事务的传播行为 7种
    PROPAGATION_REQUIRED:如果存在事务,就使用当前事务,如果没有就新建事务。
  • 事务提交事务,回滚事务的时机
  1. 当你的业务方法执行成功,没有异常抛出,spring在方法执行后提交事务。事务管理器commit
  2. 当你的业务方法抛出运行时异常,spring执行回滚,调用事务管理器的rollback。
  3. 当你的业务方法抛出非运行时异常,主要是受差异常,提交事务
    受查异常:在你写代码中,必须处理的异常。例如IOException、 SQLException
  • 总结spring事务
    管理事务的是 事务管理器和它的实现类
    spring 的事务是一个统一模型
    指定要需要的事务管理器实现类,使用<bean>
    指定那些类,哪些方法需要加入事务的功能
    指定方法需要的隔离级别,传播行为,超时。

spring框架中提供的事务处理的方案

适合中小项目使用的,注解方案

@Transactional注解增加事务
放在public方法上代表事务
属性:
propagation: 事务传播行为。
isolation:用于设置事务的隔离级别。该属性类型为:Isolation枚举。
rollbackFor:指定需要回归的异常类。
rollbackForClassName:
noRollbackFor:指定哪些不回滚

  • 使用步骤
  1. 声明事务管理器对象
    <bean id="xx" class="DataSourceTransactionManager">
  1. 开启事务注解驱动,告诉spring框架, 我要使用注解的方式管理事务。
    spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事务的功能。
    spring给业务方法加入事务:
    在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用aop的环绕通知。
    @Around("你要增加的事务功能的业务方法名称")
    Object myAround(){
    try{
    method();
    spring事务管理器.commit()
    }catch(Exception e){
    apring事务管理器的rollback()
    }
    }
    1. 在你的方法的上面加入@Trancational
  • spring事务处理
    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:context="http://www.springframework.org/schema/context" 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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    <context:property-placeholder location="classpath:druid.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
<!--    声明mybatis提供的sqlsessionFactoryBean类,这个类内部创建sqlSessionFactory的-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--        set注入把数据库连接池赋给了dataSource-->
        <property name="dataSource" ref="dataSource"/>
<!--        mybatis主配置文件的位置
            configLocation 是Resource类型, 读取配置文件的
            它的赋值,使用value,指定文件的路径,使用classpath:表示文件位置-->
        <property name="configLocation" value="classpath:mybatis.xml"/>
    </bean>

    <bean  class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <property name="basePackage" value="com.lz.dao"/>
    </bean>

    <bean class="com.lz.service.impl.BuyGoodServiceImpl" id="buyService">
        <property name="sd" ref="saleDao"/>
        <property name="gd" ref="goodsDao"/>
    </bean>

<!--        spring的事务处理-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--        指定数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

<!--    开启事务注解驱动-->
    <tx:annotation-driven  transaction-manager="transactionManager"/>
</beans>

注解开启事务

public class BuyGoodServiceImpl implements BuyGoodsService {
    private GoodsDao gd;
    private SaleDao sd;

    /**
     *  rollbackfor : 指定异常回滚
     * @param goodId
     * @param nums
     */
    @Transactional(
            propagation = Propagation.REQUIRED,
            isolation = Isolation.DEFAULT,
            readOnly = false,
            rollbackFor = {
                    NullPointerException.class,
                    NotClassException.class,
                    RuntimeException.class
            }
    )
    @Override
    public void buy(int goodId, int nums) {
        // 记录销售信息
        Sales sales = new Sales();
        sales.setGid(goodId);
        sales.setNums(nums);
        sd.insertSale(sales);
        // 更新库存
        Goods goods = gd.selectGoods(goodId);
        if(goods == null){
            throw new NullPointerException("编号是+"+ goodId +"商品不存在");
        } else if(goods.getAmount() < nums){
            throw new NotClassException("商品库存不足");
        }
        // 修改库存
        Goods goods1 = new Goods();
        goods1.setId(goodId);
        goods1.setAmount(nums);
        gd.updateGoods(goods1);
    }
    public void setGd(GoodsDao gd) {
        this.gd = gd;
    }

    public void setSd(SaleDao sd) {
        this.sd = sd;
    }

}

测试类

public class MyTest {
    private String config = "applicationContext001.xml";
    private ApplicationContext ac = new ClassPathXmlApplicationContext(config);
    @Test
    public void Test01(){
        BuyGoodsService service = (BuyGoodsService) ac.getBean("buyService");
        System.out.println(service.getClass().getName());
        service.buy(1005, 100);
    }
}

  • 遇到的问题

mysql数据库默认引擎是MyISAM,它不支持事务; 所以改成常用的innorDB 支持事务。

 @Transactional(
            propagation = Propagation.REQUIRED,
            isolation = Isolation.DEFAULT,
            readOnly = false,
            rollbackFor = {
                    RuntimeException.class
            }
    和@Transactional 等效 都是默认的
  • 默认抛出运行时异常
  • 可以放在公共类和方法上(public)
  • rollbackFor:
    处理逻辑:
    1. spring框架会首先检查方法抛出的异常是不是在rollbackFor的属性值中
    如果异常在rollbackFor列表中,不管时什么类型的异常一定会回滚。
    2. 如果异常类型不在列表中,则看是不是runtiomException异常,如果是则回滚。

适合大型项目,有很多的类,方法,需要大量配置事务,使用aspectj框架功能,在spring配置文件中声明类,方法需要事务。这种方式业务和事务配置完全分离。

实现步骤: 都是在xml配置文件中实现。
1. 加入依赖aspectj

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.1.10.RELEASE</version>
  </dependency>
  1. 声明事务管理对象
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="manager"/>
  1. 声明方法需要事务的类型(配置方法的事务属性(隔离级别,传播行为,超时))
  2. 配置aop:指定哪些类需要代理。
  • spring xml配置
 <context:property-placeholder location="classpath:druid.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>

        <property name="configLocation" value="classpath:mybatis.xml" />
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <property name="basePackage" value="com.lz.dao" />
    </bean>

    <bean id="buyService" class="com.lz.service.impl.BuyGoodServiceImpl">
        <property name="sd" ref="saleDao"/>
        <property name="gd" ref="goodsDao"/>
    </bean>

<!--    声明事务管理对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
<!--    声明业务方法事务属性(隔离级别,传播方式)
    id: 自定义名称, 表示<tx:advice> 和 </tx:advice>之间的配置内容
    transaction-manager: 表示事务管理器对象id
-->
    <tx:advice transaction-manager="transactionManager" id="myAdvice">
<!--        表示要配置事务的属性-->
        <tx:attributes>
<!--            表示给具体的方法配置属性
                    name : 完整的方法名
                            方法名可以用通配符 *表示任意字符
                    其他属性和之前的意义
 -->
            <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
                       rollback-for="java.lang.NullPointerException,
                                com.lz.execp.NotClassException"/>
        </tx:attributes>
    </tx:advice>
<!--    配置aop-->
    <aop:config>
<!--        配置切入点表达式, 指定哪些包中类,要使用事务
                id:切入点表达式的名称,唯一值
                expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象
 -->
        <aop:pointcut id="servciePt" expression="execution(* *..service..*.*(..))"/>
        <!--
            配置增墙器:
                        关联advice和pointcut
                        advice-ref: 通知 , 上面的tx:advice那里的配置
                        pointcut-ref: 切入点表达式的id
        -->
        <aop:advisor advice-ref="myAdvice" pointcut-ref="servciePt"/>
    </aop:config>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,271评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,725评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,252评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,634评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,549评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,985评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,471评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,128评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,257评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,233评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,235评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,940评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,528评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,623评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,858评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,245评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,790评论 2 339