MyBatis原理浅析

简介

MyBatis是一个轻量级的ORM框架,它简化了对关系数据库的使用,开发人员可以在XML或注解中编写SQL来完成对数据库的操作。
如果完全使用XML方式,SQL语句可以集中维护,做到与Java代码完全隔离,便于对SQL调优。

原理及流程

  1. 加载配置:配置来源于两个地方,一是配置文件,一是Java代码的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
  2. SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参。
  3. SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。
  4. 结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者基本数据类型,并将最终结果返回。

技术盏

反射、jdk动态代理、Ognl表达式引擎、缓存

反射和动态代理

解析是将SQL或存储过程定义表述为Mybatis中对应对象的过程,例如将执行的Sql标签( , …)解析为 MappedStatement ;输入参数定义标签解析为 ParameterMap ;结果列定义标签解析为 ResultMap

Mybatis支持将一个类的方法映射到一个 mapper 文件里的对应 statement sql,将方法名与DML SQL标签的id对应起来,这样我们就可以透明地使用 interface 的方式结合了面向对象的方式来与数据库操作,这样做更趋近于面向对象的编程风格。其中用到jdbc动态代理原理,用 MapperProxy 动态代理了需要执行的接口方法,主要代理逻辑在 MapperMethod 中实现,负责用接口的名称以及方法名称找到解析好的 MappedStatement 然后调用 SqlSession 中对应的执行逻辑执行。

缓存

Mybatis的一级缓存是SqlSession级别。第一次执行select时候会发现sqlsession缓存没有记录,会去数据库查找,然后把结果保存到缓存,第二次同等条件查询下,就会从缓存中查找到结果。另外为了避免脏读,每次执行更新新增删除时候会清空当前sqlsession缓存。

二级缓存是namespace级别的。同一个namespace下的搜寻语句共享一个二级缓存。如果开启了二级缓存,则先从二级缓存中查找,查找不到则委托为SimpleExecutor查找,而它则会先从一级缓存中查找,查找不到则从数据库查找。

mybaits的二级缓存一般不怎么使用,默认一级缓存是开启的。

#基于注解方式的demo

CREATE TABLE `user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `user_name` varchar(100) DEFAULT NULL,
  `create_time` int(11) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>1.4.3.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
 </dependencyManagement>

 <dependencies>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-dbcp2</artifactId>
            <version>2.1.1</version>
        </dependency>
 </dependencies>

application.properties

project.name=mybatis
server.port=7005
management.port=7006

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

logging.level.root=info

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

    <!-- 我们不会将这个Bean进行实例化,class属性中定义了PropertyPlaceholderConfigurer类,可以告诉Spring我们实际上要注册一个properties文件-->
    <bean id="propertyFileConfigForDB"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location">
            <value>classpath:application.properties</value>
        </property>
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"/>
    <!-- 声明式事物控制 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
        <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和MyBatis完美整合,不需要mybatis的配置映射文件 -->
    <bean id="sqlSessionFactor" class="org.mybatis.spring.SqlSessionFactoryBean" primary="true">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- DAO接口所在包名,Spring会自动查找其下的类 ,包下的类需要使用@MapperScan注解,否则容器注入会失败 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.test.dao.*"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactor"/>
    </bean>
</beans>

主类上添加注解 @ImportResource({“classpath:mybatis.xml”}),以引入配置mybatis.xml

public class UserDo {
    public Long getId() {
        return id;
    }

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

    private Long id;
    private String user_name;
    private Long create_time;
    private Integer age;

    public UserDo() {}

    public String getUser_name() {
        return user_name;
    }

    public void setUser_name(String user_name) {
        this.user_name = user_name;
    }

    public Long getCreate_time() {
        return create_time;
    }

    public void setCreate_time(Long create_time) {
        this.create_time = create_time;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}




@MapperScan
public interface UserMapper {
    @Insert("insert into user (user_name,create_time,age) values ("
            + "#{user_name},#{create_time},#{age})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    Long insertUser(UserDo userDo);

    @Delete("delete from user where id = #{id}")
    Long deleteUser( Long id);

    @Update("update user set user_name=#{user_name}, create_time=#{create_time},"
      + " age=#{age} where id=#{id}")
    Long updateUser(UserDo userDo);

    @Select("select * from user where id=#{id}")
    UserDo selectUserById(Long id);
}



public interface UserService {
    Long createUser(User user);
    Long deleteUser(Long id);
    Long updateUser(User user);
    User selectUser(Long id);
}



@Service
public class UserServiceImpl implements UserService {
    private static Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Resource
    private UserMapper userMapper;

    @Override
    public Long createUser(User user) {
        try {
            return userMapper.insertUser(new UserDo(user));
        } catch (Exception ex) {
            logger.error("exception occurred: {}", ex);
        }

        return null;
    }

    @Override
    public Long deleteUser(Long id) {
        try {
            return userMapper.deleteUser(id);
        } catch (Exception ex) {
            logger.error("exception occurred: {}", ex);
        }
        return null;
    }

    @Override
    public Long updateUser(User user) {
        try {
            return userMapper.updateUser(new UserDo(user));
        } catch (Exception ex) {
            logger.error("exception occurred: {}", ex);
        }
        return null;
    }

    @Override
    public User selectUser(Long id){
        try {
            UserDo userDo =  userMapper.selectUserById(id);
            return new User(userDo);
        } catch (Exception ex) {
            logger.error("exception occurred: {}", ex);
        }
        return null;
    }
}

通过maven插件自动生成entity、mapper

<generatorConfiguration>
    <classPathEntry location="etc\mybatis-generator\mysql-connector-java-5.1.46.jar"/>
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
        <plugin type="com.itfsw.mybatis.generator.plugins.BatchInsertPlugin"/>
        <plugin type="com.xxx.finance.commons.service.mybatis.generator.plugin.MySQLPaginationPlugin"></plugin>
        <commentGenerator>
            <property name="addRemarkComments" value="true"/>
        </commentGenerator>

        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://xx.xx.xxx.xxx:xxxx/risk_alpha_engine"
                        userId="xxxxx" password="xxxxx">
        </jdbcConnection>
        <javaModelGenerator targetPackage="com.xxx.fin.phecda.strategy.dal.entity" targetProject="src/main/java"/>
        <sqlMapGenerator targetPackage="sql" targetProject="src/main/resources"/>
        <javaClientGenerator targetPackage="com.xxx.fin.phecda.strategy.dal.mapper" targetProject="src/main/java"
                             type="com.xxx.finance.commons.service.mybatis.generator.MySimpleJavaMapperGenerator"/>
        <table tableName="sys_rule_element_rel" enableUpdateByPrimaryKey="false" enableDeleteByPrimaryKey="false" enableDeleteByExample="false"/>
        <table tableName="flow_strategy_log" enableUpdateByPrimaryKey="false" enableDeleteByPrimaryKey="false" enableDeleteByExample="false"/>
        <table tableName="sys_biz_flow_version" enableUpdateByPrimaryKey="true" enableDeleteByPrimaryKey="false" enableDeleteByExample="false"/>
    </context>
</generatorConfiguration>
<dependencies>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
    </dependencies>
    
	<build>
        <plugins>
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.5</version>
                <configuration>
                    <verbose>true</verbose>
                    <overwrite>true</overwrite>
                    <configurationFile>${basedir}/etc/mybatis-generator/generatorConfig.xml</configurationFile>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>com.xxx.finance</groupId>
                        <artifactId>commons-service-mybatis</artifactId>
                        <version>1.4.1-SNAPSHOT</version>
                    </dependency>
                    <dependency>
                        <groupId>com.itfsw</groupId>
                        <artifactId>mybatis-generator-plugin</artifactId>
                        <version>1.0.5</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

sharding-sphere分表分库

https://tomoya92.github.io/2020/06/03/sharding-sphere-horizontal-split/

<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>

<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>

<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>

<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-namespace</artifactId>

<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>

<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>

参考资料

入门+原理
https://zhuanlan.zhihu.com/p/34446308
https://mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=2247483937&idx=1&sn=85727e94ad3d1af1ef99cfefa420520c&chksm=ebd74320dca0ca367f869427202eb029bf3cf8dd4dc71dae26d925aaaabc2c8ea704735b3b87&scene=21###wechat_redirect

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值