美文网首页
mybatis-spring原理简析+tkmybatis简单使用

mybatis-spring原理简析+tkmybatis简单使用

作者: 灿烂的GL | 来源:发表于2021-02-04 14:54 被阅读0次

    一、mybatis

    mybatis的使用大家应该熟悉,但是对于怎么实现sql的调用和结果返回一直带有疑问,以我们项目探索如下:
    配置文件配置数据库信息如下:

    spring.datasource.url=jdbc:mysql://localhost:3306/sqldatabase?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2B8
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.username=XXX
    spring.datasource.password=XXX
    spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
    

    启动类上为了扫描到mapper会加上@MapperScan的注解,使spring扫描到你的mapper,或者可以在每个mapper上添加@Mapper注解

    @SpringBootApplication
    //basePackages里是所有mapper文件的相对路径
    @MapperScan(basePackages = {"com.cmcc.web.mapper"})
    @EnableCreateCacheAnnotation
    @Slf4j
    public class Application {
            do something
    }
    

    @MapperScan注解如下,

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(tk.mybatis.spring.annotation.MapperScannerRegistrar.class)
    public @interface MapperScan {
    }
    

    再看看@Import导入的

    //EnvironmentAware主要解析配置文件,ResourceLoaderAware主要获取资源加载器,可以获得外部资源文件,ImportBeanDefinitionRegistrar注册bean
    public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    ...something
    //主要是装载ClassPathMapperScanner实体类,重写ImportBeanDefinitionRegistrar的registerBeanDefinitions类
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
            AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
            ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
            // this check is needed in Spring 3.1
            if (resourceLoader != null) {
                scanner.setResourceLoader(resourceLoader);
            }
    
            Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
            if (!Annotation.class.equals(annotationClass)) {
                scanner.setAnnotationClass(annotationClass);
            }
    
            Class<?> markerInterface = annoAttrs.getClass("markerInterface");
            if (!Class.class.equals(markerInterface)) {
                scanner.setMarkerInterface(markerInterface);
            }
    
            Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
            if (!BeanNameGenerator.class.equals(generatorClass)) {
                scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
            }
    
            Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
            if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
                scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
            }
    
        scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
            scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
    //获取value属性放入basePackages ,方便之后doScan扫描
            List<String> basePackages = new ArrayList<String>();
            for (String pkg : annoAttrs.getStringArray("value")) {
                if (StringUtils.hasText(pkg)) {
                    basePackages.add(pkg);
                }
            }
            for (String pkg : annoAttrs.getStringArray("basePackages")) {
                if (StringUtils.hasText(pkg)) {
                    basePackages.add(pkg);
                }
            }
            for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
                basePackages.add(ClassUtils.getPackageName(clazz));
            }
            //优先级 mapperHelperRef > properties > springboot
            String mapperHelperRef = annoAttrs.getString("mapperHelperRef");
            String[] properties = annoAttrs.getStringArray("properties");
            if (StringUtils.hasText(mapperHelperRef)) {
                scanner.setMapperHelperBeanName(mapperHelperRef);
            } else if (properties != null && properties.length > 0) {
                scanner.setMapperProperties(properties);
            } else {
                try {
                    scanner.setMapperProperties(this.environment);
                } catch (Exception e) {
                    ...something
                }
            }
         //注册filter
            scanner.registerFilters();
         //扫描包路径并注册bd
            scanner.doScan(StringUtils.toStringArray(basePackages));
        }
    

    doScan方法会扫描包路径并注册bd,内方法processBeanDefinitions处理注册后的bd(包括:映射接口类名、sqlSessionFactory、sqlSessionTemplate、mapperFactoryBean、设置属性按类型注入等),spring在进行实例化db的时候会按类型注入的方式找到类型为SqlSessionFactoryBean的进行注入

       public Set<BeanDefinitionHolder> doScan(String... basePackages) {
            Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    
            if (beanDefinitions.isEmpty()) {
                logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
            } else {
                processBeanDefinitions(beanDefinitions);
            }
    
            return beanDefinitions;
        }
    

    简单总结如下:
    在spring中使用mybatis需要SqlSessionFactoryBean和至少一个映射器,SqlSessionFactoryBean会生成SqlSessionFactory,SqlSessionFactory会注入到spring中,里面包含db的环境信息和mybatis的配置文件路径等,我们在使用过程中通过@MapperScan来实现这一步;我们在调用mapper的时候会生成反向代理,mapper的bean即MapperFactoryBean,MapperFactoryBean会生成SqlSession来实现sql的执行和关闭,异常被转化成spring异常以DataAccessException形式抛出。

    参考(阅读顺序如下):

    1、mybatis-spring中间件逻辑-详细
    2、官网

    二、 tkmybatis

    tkmybatis是在mybatis上做了一层封装的jar包,主要用于单表的增删改查,不能做连表查询,好处是避免使用xml文件。

    基本配置:

    pom文件:

       <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper-spring-boot-starter</artifactId>
                <version>2.0.4</version>
                <exclusions>
                    <exclusion>
                        <groupId>javax.persistence</groupId>
                        <artifactId>persistence-api</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    

    实体类---需要加上JPA注解如@Table(name="表名") @Column列名,column里也有一些参数,比如可以插入数据可以为空设置(@Column(name = "size" , nullable = false)),其他的参数可以自己参照方法
    dao-----可以继承tkmybatis里mapper的方法


    可以继承的mapper.png

    以idsmapper为例里面涉及到一些封装方法


    image.png
        @DeleteProvider(type = IdsProvider.class, method = "dynamicSQL")
        int deleteByIds(String ids);
    

    IdsProvider里做了sql的拼接,sql调用实现底层和mybatis一样

        public String deleteByIds(MappedStatement ms) {
            final Class<?> entityClass = getEntityClass(ms);
            StringBuilder sql = new StringBuilder();
            sql.append(SqlHelper.deleteFromTable(entityClass, tableName(entityClass)));
            Set<EntityColumn> columnList = EntityHelper.getPKColumns(entityClass);
            if (columnList.size() == 1) {
                EntityColumn column = columnList.iterator().next();
                sql.append(" where ");
                sql.append(column.getColumn());
                sql.append(" in (${_parameter})");
            } else {
                throw new MapperException("继承 deleteByIds 方法的实体类[" + entityClass.getCanonicalName() + "]中必须只有一个带有 @Id 注解的字段");
            }
            return sql.toString();
        }
    

    上边的方法可以用于一些常用增删改查操作,对于复杂查询等操作tkmybatis还提供了Example和Condition 类可以做多条件查询,然后调用ExampleMapper中的方法

    @tk.mybatis.mapper.annotation.RegisterMapper
    public interface ExampleMapper<T> extends
            SelectByExampleMapper<T>,
            SelectOneByExampleMapper<T>,
            SelectCountByExampleMapper<T>,
            DeleteByExampleMapper<T>,
            UpdateByExampleMapper<T>,
            UpdateByExampleSelectiveMapper<T> {
    
    }
    

    详见Example常见方法
    总结:
    1、Tkmybatis场景一常用的查询mapper中继承的mapper里的方法(需要注意的是如:IdsMapper<这里是数据库表对应的实体类>)
    2、复杂查询等操作借助Example或者Condition

    相关文章

      网友评论

          本文标题:mybatis-spring原理简析+tkmybatis简单使用

          本文链接:https://www.haomeiwen.com/subject/unlruhtx.html