spring + mybatis实现数据库CRUD操作原理

事务

数据库事务

  • 事务:逻辑上的多条sql的分组。该分组内的事务要么全部成功(commit),要么全部失败(rollback)。
  • 数据库默认自动提交事务,即发一条sql就执行一条。
  • 单条sql语句不存在事务概念。
  • mysql事务语法:
start transaction
...
...
commit/rollback
- start transaction:开启事务
- rollback:回滚事务
- commit:提交事务

ACID

  • 原子性:事务内的一组sql,要么全部成功,要么全部失败
  • 一致性:事务执行之前和执行之后状态一致,e.g.拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
  • 隔离性:多个并发事务之间相互隔离,感知不到其他事务的存在
  • 持久性:事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作

隔离级别

没有隔离可能会出现的错误

  • 脏读:A事务读到B事务未提交的更改数据
  • 不可重复读:A事务读数据Data为状态S1, B事务将Data状态更新为S2并提交。A事务再次读Data,Data的状态
  • 幻读

JDBC事务

  • jdbc从数据库获取connection,默认情况下connection会自动向数据库提交它发送的sql。
  • jdbc事务流程:
// 关闭自动提交,相当于mysql的start transaction
connection.setAutoCommit(false); 
...
...
connection.commit()/rollback();

Spring事务

spring结合mybatis配置

  • spring数据库配置
class="org.mybatis.spring.mapper.MapperScannerConfigurer"
      p:basePackage="com.cmbchina.ccd.pluto.babylon.*.dao"/>

id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
      p:dataSource-ref="dataSource"
      p:configLocation="classpath:mybatis-config.xml"/>

id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
      p:dataSource-ref="dataSource"/>

transaction-manager="transactionManager"/>
  • mybatis-config.xml


<configuration>
    
    
    

    
    <typeAliases>
        <package name="com.cmbchina.ccd.pluto.babylon.deploy.model"/>
        <package name="com.cmbchina.ccd.pluto.babylon.deploy.vo"/>
        <package name="com.cmbchina.ccd.pluto.babylon.resource.model"/>
        <package name="com.cmbchina.ccd.pluto.babylon.resource.vo"/>
    typeAliases>

    
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper">
            
            <property name="dialect" value="sqlserver"/>
            
            
            
            <property name="offsetAsPageNum" value="true"/>
            
            
            <property name="rowBoundsWithCount" value="true"/>
            
            
            <property name="pageSizeZero" value="true"/>
            
            
            
            <property name="reasonable" value="false"/>
            
            <property name="supportMethodsArguments" value="false"/>
            
            <property name="returnPageInfo" value="none"/>
        plugin>
    plugins>
configuration>

mybatis技术内幕

SqlSession与SqlSessionFactory(https://my.oschina.net/zudajun/blog/665956)

  • SqlSessionFactory: SqlSession工厂bean,singleton,在spring启动时载入mybatis全局配置文件,创建SqlSessionFactory。
  • SqlSession: 程序与数据库交互的一次会话,SqlSession封装数据库增删改查及事务方法。
  • SqlSession生命周期:
    • 开启spring事务:一个事务共用一个SqlSession
    • 未开启spring事务:调用一次mybatis方法一个SqlSession

动态代理,自动映射Mapper的底层实现原理(https://my.oschina.net/zudajun/blog/666223)

  • Mybatis中声明一个interface接口,没有编写任何实现类,Mybatis就能返回接口实例,并调用接口方法返回数据库数据

实现原理:动态代理

  • 动态代理实现sql映射例子
/**
 * invocation handler of a proxy instance
 * Created by z673414 on 2018/6/29.
 *
 * @author z673414
 */
public class MapperProxy implements InvocationHandler {
    public static void main(String[] args) {
        MapperProxy mapperProxy = new MapperProxy();
        UserMapper userMapper = mapperProxy.newInstance(UserMapper.class);
        User user = userMapper.getUserById(1);

        System.out.println(user);
    }

    /**
     * Processes a method invocation on a proxy instance and returns
     * the result.  This method will be invoked on an invocation handler
     * when a method is invoked on a proxy instance that it is
     * associated with.
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())) {
            try {
                // 诸如hashCode()、toString()、equals()等方法,将target指向当前对象this
                return method.invoke(this, args);
            } catch (Exception t) {
                t.printStackTrace();
            }
        }

        return new User(1, "xiaoming");
    }

    /**
     * 创建proxy对象
     *
     * @param clz
     * @param 
     * @return
     */
    public  T newInstance(Class clz) {
        return (T) Proxy.newProxyInstance(clz.getClassLoader(), new Class[]{clz}, this);
    }
}
  • mybatis动态代理的实现
public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class mapperInterface;
  private final Map methodCache;

  public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
    ......
  }

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    // 调用sqlSession执行CRUD操作
    return mapperMethod.execute(sqlSession, args); 
  }

  private MapperMethod cachedMapperMethod(Method method) {
    ......
  }

}
- spring事务执行:


- Spring创建MapperScannerConfigurer bean,searches recursively starting from a base package for interfaces and registers them as {@code MapperFactoryBean},将sqlSessionFactory注入每个mapperFactoryBean
- MapperFactoryBean: BeanFactory that enables injection of MyBatis mapper interfaces
    - getSqlSession().getConfiguration().addMapper(this.mapperInterface)
- Spring创建SqlSessionFactory bean,解析配置文件生成Configuration对象
    - Configuration生成MapperRegistry对象
        - MapperRegistry getMapper()生成mapperProxyFactory
            - MapperProxyFactory newInstance()生成mapperInterface的代理对象,用于调用mybatis方法,执行数据库CRUD操作

你可能感兴趣的:(spring)