Spring学习笔记-7(Spring框架整合mybatis框架)

文章目录

  • 1.概述
  • 2.具体操作
    • 2.1.导入依赖包
    • 2.2.配置applicationContext.xml文件
    • 2.3.编写pojo实体类
    • 2.4.编写mapper
    • 2.5.编写service调用相应的mapper
  • 3.声明式事务
    • 3.1.基本使用
      • 3.1.1.配置applicationContext.xml文件
      • 3.1.2.编写pojo、mapper、service
    • 3.2.声明式事务的属性
      • 3.2.1.事务的传递性(propagation属性)
      • 3.2.2.事务的隔离级别(isolation属性)
      • 3.2.3.事务回滚(rollback-for,no-rollback-for属性)
      • 3.2.4.其他属性

1.概述

Spring框架除了提供了IOC、DI和AOP等的核心功能,还支持数据访问和集成功能。可以将mybatis框架整合到Spring框架中来实现对数据库的操作。

2.具体操作

2.1.导入依赖包

为了让两个框架正常工作,需要导入他们各自的依赖包。此外为了让mybatis框架能正常整合到Spring中还需要导入一个mybatis-spring依赖包。

  • mybatis:mybatis框架基础依赖包。
  • mybatis-spring:让mybatis整合到spring中的依赖包。
  • mysql-connector-java:mysql数据库的jdbc驱动。(根据所使用数据库的不同,需要导入不同的驱动包)
    在这里插入图片描述
  • spring-jdbc:在spring中使用jdbc所需的依赖包。
  • spring-tx:在spring中使用声明式事务所需的依赖包。
  • spring-xxx:其他spring框架功能所需的依赖包。
    Spring学习笔记-7(Spring框架整合mybatis框架)_第1张图片

2.2.配置applicationContext.xml文件

之前单独使用mybatis的时候,需要编写一个mybatis.xml文件对mybatis进行配置。而现在由于整合到spring框架中了,所以对mybatis的配置也可以在applicationContext.xml文件中完成。

  • 配置数据源对象:包含数据库的url、用户名、密码、数据库驱动类等数据库访问配置。
  • 配置sqlSessionFactory对象:之前使用mybatis的时候是使用SqlSessionFactoryBuilder来获取sqlSessionFactory的,现在由于spring的ioc作用,可以通过配置bean来获取该对象。
  • 配置扫描器对象:相当于mybatis.xml中mappers标签下的package标签,扫描相应的mapper.xml文件和对应的接口,并整合为一个实现类。

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

    
    
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="jdbc:mysql://localhost:3306/ssm?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Hongkong"/>
        <property name="username" value="username"/>
        <property name="password" value="password"/>
        
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    bean>

    
    
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
    bean>

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

    
    <bean id="userServiceImpl" class="com.bear.sxt.service.UserServiceImpl">
        
        <property name="userMapper" ref="userMapper"/>
    bean>
beans>

2.3.编写pojo实体类

通过编写实体类完成类与数据库表的关系映射,例如定义一个user表,里面有id号、用户名和密码。

public class User {

    private int id;
    private String username;
    private String password;

    public User() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

2.4.编写mapper

编写相应的mapper.xml和接口类,由于2.2中的配置,框架会自动生成一个相应实现类。
在这里插入图片描述

  • UserMapper.xml文件包含了sql语句


<mapper namespace="com.bear.sxt.mapper.UserMapper">
    <select id="selectByUser" resultType="com.bear.sxt.pojo.User" parameterType="com.bear.sxt.pojo.User">
        select *
        from user
        where username = #{username} and password = #{password};
    select>
mapper>
  • UserMapper接口包含了与UserMapper.xml中sql操作的id同名的抽象方法
public interface UserMapper {

    User selectByUser(User user);
}

2.5.编写service调用相应的mapper

如我们需要在UserServiceImpl中调用UserMapper中的sql语句,需要定义一个UserMapper类型(就是之前定义的那个接口类)的成员变量,并编写get/set方法。然后就可以直接使用UserMapper实现类的方法了。

public class UserServiceImpl implements UserService {

    UserMapper userMapper;

    public UserMapper getUserMapper() {
        return userMapper;
    }

    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Override
    public User login(User user) {
        return userMapper.selectByUser(user);
    }
}
  • 定义get/set方法是为了能在applicationContext.xml文件中对userMapper变量进行赋值。
    
    <bean id="userServiceImpl" class="com.bear.sxt.service.UserServiceImpl">
        
        <property name="userMapper" ref="userMapper"/>
    bean>

3.声明式事务

上述的select操作没有涉及事务提交、session关闭、错误回滚等的操作,对于读操作来说这样是不会看出有什么错误的,但对于增、删、改操作来说,如果没有提交事务,则不会产生真正的作用的。Spirng框架有AOP功能所以可以使用AOP来插入上述的事务操作。

  • 编程式事务:由程序员写控制代码。
  • 声明式事务:事务控制代码由Spring写好,需要声明哪些方法需要这些事务和如何进行事务控制。

3.1.基本使用

3.1.1.配置applicationContext.xml文件

在2的配置基础上加上txManagertxAdivceaop配置。由于是声明式事务由spring写好,所以我们无需理会具体的实现,按照下面的配置配置即可给相应的方法使用上事务。


<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: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/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">

    
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    bean>

    
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            
            
            
            
            <tx:method name="select*" propagation="REQUIRED"/>
            <tx:method name="insert*"/>
            <tx:method name="*" read-only="true"/>
        tx:attributes>
    tx:advice>

    
    <aop:config>
        <aop:pointcut id="myPointCut" expression="execution(* com.bear.sxt.service.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointCut"/>
    aop:config>
beans>

3.1.2.编写pojo、mapper、service

  • User实体类与2中一致。
  • 在2中的基础上加上了一个插入user的sql命令。


<mapper namespace="com.bear.sxt.mapper.UserMapper">
    
    <select id="selectByUser" resultType="com.bear.sxt.pojo.User" parameterType="com.bear.sxt.pojo.User">
        select *
        from user
        where username = #{username} and password = #{password};
    select>

    
    <insert id="insertUser" parameterType="com.bear.sxt.pojo.User">
        insert into user (id, username, password) values (default, #{username}, #{password});
    insert>
mapper>
  • 在2中的userServiceImpl中编写方法调用mapper中的方法。
public class UserServiceImpl implements UserService {

    UserMapper userMapper;

    public UserMapper getUserMapper() {
        return userMapper;
    }

    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Override
    public User selectByUser(User user) {
        return userMapper.selectByUser(user);
    }

    @Override
    public int insertUser(User user) {
        return userMapper.insertUser(user);
    }
}

3.2.声明式事务的属性

3.2.1.事务的传递性(propagation属性)

当一个具有事务控制的方法被另一个有事务控制的方法调用时,如何对事务进行管理。可以通过tx:method标签中的propagation属性进行设置。如下面的代码中在插入用户前需要检测数据库中是否已经存在了这个用户,即具有事务控制的方法insertUser调用了另一个具有事务控制的方法selectByUser。这两个方法的事务如何管理(新建事务、在同一个事务中执行、报异常…),这时就需要设置事务的传播行为了。

    @Override
    public User selectByUser(User user) {
        return userMapper.selectByUser(user);
    }

    @Override
    public int insertUser(User user) {
        User temp = userMapper.selectByUser(user);
        if (null == temp) {
            return userMapper.insertUser(user);
        } else {
            return 0;
        }
    }

propagation属性有如下可选值:

  • required(默认值):如果当前有事务则在当前事务中执行,否则新建一个事务。
  • supports:如果当前有事务则在当前事务中执行,否则在非事务状态下执行。
  • mandatory:必须在事务内部执行,如果当前有事务,就在事务中执行,如果没有则报错。
  • required_new:如果当前没有事务,则新建一个事务,如果当前有事务则把当前事务挂起,再新建一个事务。
  • not_supported:必须在非事务中执行,如果当前有事务,则把当前事务挂起。
  • never:必须在非事务中执行,如果当前有事务,则报错。
  • nested:必须在事务中执行,如果当前没有事务,则新建一个事务,否则创建一个嵌套事务。
    Spring学习笔记-7(Spring框架整合mybatis框架)_第2张图片

3.2.2.事务的隔离级别(isolation属性)

用于在多线程或并发访问下保证访问到的数据具有有效性。

并发引起的问题

  • 脏读
    事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据。
  • 不可重复读
    事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。(可通过锁该条数据解决。)
  • 幻读
    系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。(可通过锁该数据表解决。)

数据库隔离级别

  • 读未提交
    读未提交也叫脏读,指一个事务可以读取其他事务未提交的数据。
  • 读已提交
    SQL Server默认的隔离级别,在事务未提交之前所做的修改对于其他事务是不可见的。
  • 可重复读
    • MySQL默认的隔离级别,保证同一个事务中多次相同的查询结果是一致的,如事务A一开始查询了一条记录,这时事务B修改了该记录并提交,隔了几秒钟后事务A相同的查询并提交,可重复读保证事务A两次查询的结果是相同的。
    • 这里可能有疑问,不是数据都修改了吗,为什么事务A的第二次查询不取最新的数据呢?其实可重复读的意义在于:一个事务不会受另一个事务影响。
      SQL语句的执行不是在事务提交之后,一个事务即使一直没提交,在该阶段都可以执行多条SQL语句(如在一个事务中我使用insert语句100条记录,然后再提交,如果该100条数据插入出错了,就回滚,一条都不插)。
    • 正是事务的原子性,可重复读才有意义。如一条记录中有账单、当前余额、上个月余额。我需要校验这几个数据是否正确。我在一个事务中先取了账单和上个月余额,然后进行相减得出当前余额,接着取了当前余额和刚刚的计算结果进行对比,然后提交事务。如果在去当前余额的过程中,另一个事务提交了新账单,那当前余额必然会改变,如果没有可重复读的话,上述校验必然出错。
    • 可重复读的实现是通过多版本并发控制来实现的。每个事务取数据时都会带一个版本号,事务里的操作都是对同一个版本的数据操作。
  • 可串行化
    可串行化保证了读取的范围内没有新的数据插入,比如事务第一次查询得到某个范围的数据,第二次查询也同样得到相同范围的数据,中间没有新的数据插入到该范围中。

isolation属性有以下可选值

  • default(默认值):由数据库底层自动判断需要使用什么隔离级别。
  • read_uncommitted:读未提交。
  • read_committed:读已提交。
  • repeatable_read:可重复读。
  • serializable:串行化。
    Spring学习笔记-7(Spring框架整合mybatis框架)_第3张图片

3.2.3.事务回滚(rollback-for,no-rollback-for属性)

  • rollback-for:该属性定义了出现什么异常的时候进行回滚,用法为rollback-for=“异常类的全限定路径”。
  • no-rollback-for:该属性定义了出现什么异常的时候不进行回滚,用法同上。

如,在insert*方法中设置出现java.lang.Exception异常的时候事务回滚。

  <tx:method name="insert*" rollback-for="java.lang.Exception"/>

当检测到同名同密码的user时抛出Exception异常,由于设置了异常回滚所以不会插入test用户信息,如果没有设置异常回滚,则会插入test用户信息

    public int insertUser(User user) throws Exception{
        User temp = userMapper.selectByUser(user);
        if (null == temp) {
            return userMapper.insertUser(user);
        } else {
            /*测试异常回滚*/
            User test = new User();
            test.setUsername("test");
            test.setPassword("password");
            userMapper.insertUser(test);
            throw new Exception("用户已存在");
        }
    }

3.2.4.其他属性

Spring学习笔记-7(Spring框架整合mybatis框架)_第4张图片

你可能感兴趣的:(框架,#Spring,#MyBatis,spring,mybatis)