Mybatis-Tiny一个比Mybatis-Plus用着更顺手的框架

基本简介

Mybatis-Tiny是什么?Mybatis-Tiny是一个基于Mybatis框架的一层极简的扩展,它旨在使用DSL的方式对单表进行CRUD操作,类似于Mybatis-Plus框架,但它绝不是重复造轮子!区别于别的类似框架(如Mybatis-Plus、Fluent-Mybatis等)的实现方式,它采用一种逆向曲线救国的实现方式,通过较少的代码,极简的扩展实现了类似于他们大多数的功能,完全满足日常开发中对单表的各种CRUD操作。

项目地址:https://github.com/penggle/mybatis-tiny

快速入门

Talk is cheap,show me the code!

  • 插入操作

    ProductBaseInfo productBase = ...;
    List<ProductSaleSpec> productSaleSpecs = ...;
    productBaseInfoMapper.insert(productBase);
    //基于JDBC-Batch特性的批量插入操作。
    productSaleSpecMapper.batchUpdate(productSaleSpecs, 
                                      productSaleSpec -> productSaleSpecMapper.insert(productSaleSpec));
    
    //打印日志:
    
     - ==>  Preparing: INSERT INTO t_product_base_info( product_id, product_name, product_url, product_tags, product_type, audit_status, online_status, shop_id, remark, create_time, update_time ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
     - ==> Parameters: null, 24期免息【当天发】Huawei/华为Mate40 5G手机官方旗舰店50pro直降mate40e官网30正品4G鸿蒙正品30全网通(String), https://detail.tmall.com/item.htm?id=633658852628(String), ["手机通讯","手机","手机"](String), 1(Integer), 0(Integer), 1(Integer), 111212422(Long), null, 2022-04-27 00:43:42(String), 2022-04-27 00:43:42(String)
     - <==    Updates: 1
    
     - ==>  Preparing: INSERT INTO t_product_sale_spec( product_id, spec_no, spec_name, spec_index, remark, create_time, update_time ) VALUES ( ?, ?, ?, ?, ?, ?, ? )
     - ==> Parameters: 1(Long), 101(String), 4G全网通(String), 1(Integer), null, 2022-04-27 00:43:42(String), 2022-04-27 00:43:42(String)
     - ==> Parameters: 1(Long), 102(String), 5G全网通(String), 2(Integer), null, 2022-04-27 00:43:42(String), 2022-04-27 00:43:42(String)
     - ==> Parameters: 1(Long), 201(String), 亮黑色(String), 1(Integer), null, 2022-04-27 00:43:42(String), 2022-04-27 00:43:42(String)
     - ==> Parameters: 1(Long), 202(String), 釉白色(String), 2(Integer), null, 2022-04-27 00:43:42(String), 2022-04-27 00:43:42(String)
     - ==> Parameters: 1(Long), 203(String), 秘银色(String), 3(Integer), null, 2022-04-27 00:43:42(String), 2022-04-27 00:43:42(String)
     - ==> Parameters: 1(Long), 204(String), 夏日胡杨(String), 4(Integer), null, 2022-04-27 00:43:42(String), 2022-04-27 00:43:42(String)
     - ==> Parameters: 1(Long), 205(String), 秋日胡杨(String), 5(Integer), null, 2022-04-27 00:43:42(String), 2022-04-27 00:43:42(String)
     - ==> Parameters: 1(Long), 301(String), 8+128GB(String), 1(Integer), null, 2022-04-27 00:43:42(String), 2022-04-27 00:43:42(String)
     - ==> Parameters: 1(Long), 302(String), 8+256GB(String), 2(Integer), null, 2022-04-27 00:43:42(String), 2022-04-27 00:43:42(String)
    
  • 更新操作

    //根据ID更新
    ProductBaseInfo productBase = ...;
    Map<String,Object> updateColumns1 = MapLambdaBuilder.of(productBase)
            //取productBase实例中对应字段的值
            .with(ProductBaseInfo::getProductName)
            .with(ProductBaseInfo::getRemark)
            //如果productBase实例中对应字段的值为空值(null|空串|空数组|空集合)则取default值"1"
            .withDefault(ProductBaseInfo::getProductType, 1)
            //忽略productBase实例中对应字段的值,只取override值"0"
            .withOverride(ProductBaseInfo::getAuditStatus, 0)
            .withOverride(ProductBaseInfo::getOnlineStatus, 0)
            .withOverride(ProductBaseInfo::getUpdateTime, DateTimeUtils.formatNow())
            .build();
    productBaseInfoMapper.updateById(productBase.getProductId(), updateColumns1);
    //实现了领域实体的identity()方法则productBase.identity()与productBase.getProductId()是等效的
    //productBaseInfoMapper.updateById(productBase.identity(), updateColumns);
    
    //根据条件更新
    Map<String,Object> updateColumns2 = MapLambdaBuilder.<ProductBaseInfo>ofEmpty()
            .withOverride(ProductBaseInfo::getOnlineStatus, 0)
            .withOverride(ProductBaseInfo::getUpdateTime, DateTimeUtils.formatNow())
            .build();
    QueryCriteria<ProductBaseInfo> updateCriteria2 = LambdaQueryCriteria.ofSupplier(ProductBaseInfo::new)
            .eq(ProductBaseInfo::getProductType, 1)
          .in(ProductBaseInfo::getAuditStatus, 0, 1)
            .limit(5);
    productBaseInfoMapper.updateByCriteria(updateCriteria2, updateColumns2);
    
    //批量更新
    List<ProductSaleStock> productSaleStocks = ...;
    String nowTime = DateTimeUtils.formatNow();
    productSaleStockMapper.batchUpdate(productSaleStocks, productSaleStock -> {
        Map<String,Object> updateColumns = MapLambdaBuilder.of(productSaleStock)
            .withOverride(ProductSaleStock::getSellPrice, productSaleStock.getSellPrice() - productSaleStock.getSellPrice() % 100)
            .withOverride(ProductSaleStock::getUpdateTime, nowTime)
            .build();
        //new一个联合主键实例
        ID productSaleStockId = new ID()
            .addKey(ProductSaleStock::getProductId, productSaleStock.getProductId())
            .addKey(ProductSaleStock::getProductId, productSaleStock.getSpecNo());
        productSaleStockMapper.updateById(productSaleStockId, updateColumns);
        //或者实现了领域实体的identity()方法,则可以如下直接调用
        //productSaleStockMapper.updateById(productSaleStock.identity(), updateColumns);
    });
    
  • 查询操作

    //根据ID查
    ProductBaseInfo productBase1 = productBaseInfoMapper.selectById(1L);
    
    ProductBaseInfo productBase2 = productBaseInfoMapper.selectById(10L, new QueryColumns(ProductBaseInfo::getProductId, ProductBaseInfo::getProductName, ProductBaseInfo::getAuditStatus, ProductBaseInfo::getOnlineStatus));
    
    ID id = new ID().addKey(ProductSaleSpec::getProductId, 1L).addKey(ProductSaleSpec::getSpecNo, "101");
    ProductSaleSpec productSaleSpec = productSaleSpecMapper.selectById(id);
    
    //根据多个ID查询
    List<ProductBaseInfo> productBases = productBaseInfoMapper.selectListByIds(Arrays.asList(5L, 6L, 7L, 8L, 9L));
    
    //根据多个联合主键查询实体对象列表
    List<ID> ids = new ArrayList<>();
    ids.add(new ID().addKey(ProductSaleSpec::getProductId, 1L).addKey(ProductSaleSpec::getSpecNo, "101"));
    ids.add(new ID().addKey(ProductSaleSpec::getProductId, 1L).addKey(ProductSaleSpec::getSpecNo, "102"));
    ids.add(new ID().addKey(ProductSaleSpec::getProductId, 1L).addKey(ProductSaleSpec::getSpecNo, "103"));
    List<ProductSaleSpec> productSaleSpecs = productSaleSpecMapper.selectListByIds(ids);
    
     - ==>  Preparing: SELECT product_id AS productId, spec_no AS specNo, spec_name AS specName, spec_index AS specIndex, remark AS remark, DATE_FORMAT(create_time, '%Y-%m-%d %T') AS createTime, DATE_FORMAT(update_time, '%Y-%m-%d %T') AS updateTime FROM t_product_sale_spec WHERE (product_id = ? AND spec_no = ?) OR (product_id = ? AND spec_no = ?) OR (product_id = ? AND spec_no = ?)
     - ==> Parameters: 1(Long), 101(String), 1(Long), 102(String), 1(Long), 103(String)
     - <==      Total: 2
    
    //根据条件查询
    QueryCriteria<ProductSaleSpec> queryCriteria1 = LambdaQueryCriteria.ofSupplier(ProductSaleSpec::new)
                    .eq(ProductSaleSpec::getProductId, 1L)
                    .eq(ProductSaleSpec::getSpecNo, "101");
    ProductSaleSpec productSaleSpec = productSaleSpecMapper.selectByCriteria(queryCriteria1);
    
    ProductSaleStock queryRequest1 = ...;
    QueryCriteria<ProductSaleStock> queryCriteria2 = LambdaQueryCriteria.of(queryRequest1)
                    .eq(ProductSaleStock::getProductId)
                    .likeRight(ProductSaleStock::getSpecNo)
                    .between(ProductSaleStock::getStock, queryRequest1.getMinStock(), queryRequest1.getMaxStock())
                    .orderBy(OrderBy.desc(ProductSaleStock::getSellPrice));
    List<ProductSaleStock> productStocks = productSaleStockMapper.selectListByCriteria(queryCriteria2);
    
    QueryCriteria<ProductBaseInfo> queryCriteria3 = LambdaQueryCriteria.of(queryRequest2)
                    .and(nestedCriteria -> nestedCriteria.like(ProductBaseInfo::getProductName, "华为")
                            .or().like(ProductBaseInfo::getProductName, "HUAWEI"))
                    .eq(ProductBaseInfo::getProductType)
                    .eq(ProductBaseInfo::getOnlineStatus)
                    .in(ProductBaseInfo::getAuditStatus, queryRequest.getAuditStatuses().toArray())
                    .orderBy(OrderBy.desc(ProductBaseInfo::getCreateTime))
                    .dynamic(true); //自动过滤掉为空值(null|空串|空数组|空集合)的查询参数
    List<ProductBaseInfo> productBases1 = productBaseInfoMapper.selectListByCriteria(queryCriteria3);
    
    //分页查询1
    Page page = Page.of(1, 10);
    QueryCriteria<ProductBaseInfo> queryCriteria4 = LambdaQueryCriteria.of(queryRequest)
                    .likeRight(ProductBaseInfo::getProductName)
                    .eq(ProductBaseInfo::getProductType)
                    .eq(ProductBaseInfo::getOnlineStatus)
                    .in(ProductBaseInfo::getAuditStatus, queryRequest.getAuditStatuses().toArray())
                    .orderBy(page.getOrderBys())
                    .dynamic(true); //自动过滤掉为空值(null|空串|空数组|空集合)的查询参数(条件)
    List<ProductBaseInfo> productBases2 = productBaseInfoMapper.selectPageListByCriteria(queryCriteria4, new RowBounds(page.offset(), page.limit()));
    //设置总记录数
    page.setTotalRowCount(productBaseInfoMapper.selectPageCountByCriteria(queryCriteria4));
    
    //分页查询2(等效与上面)
    Page page = Page.of(2, 10);
    List<ProductBaseInfo> productBases2 = EntityMapperHelper.selectEntityObjectListByPage(productBaseInfoMapper, queryCriteria4, page);
    
  • 删除操作

    //根据ID删除
    productBaseInfoMapper.deleteById(2L);
    productExtraInfoMapper.deleteById(2L);
    
    //根据条件删除
    QueryCriteria<ProductSaleSpec> queryCriteria1 = LambdaQueryCriteria.ofSupplier(ProductSaleSpec::new)
                    .eq(ProductSaleSpec::getProductId, 2L)
                    .limit(5);
    productSaleSpecMapper.deleteByCriteria(queryCriteria1);
    
  • 更多示例请见:https://github.com/penggle/mybatis-tiny/tree/main/mybatis-tiny-examples

特性及限制

  • 支持单一主键或联合主键,单一主键时主键策略支持:IDENTITY(数据库自增的),SEQUENCE(基于序列的),NONE(无,客户端自己设置主键)

    重复造轮子的初衷也是被Mybatis-Plus只能使用单一主键给恶心到了

  • 到目前为止,Mybatis-Tiny没有任何可配置的配置项。Mybatis-Tiny的数据库方言配置与Mybatis本身的方言配置一致,即通过databaseId来实现方言。也就是说Mybatis-Tiny的方言数据库类型取自Configuration.databaseId字段,如果应用程序未设置(通过DatabaseIdProvider来设置),则Mybatis-Tiny会自动设置。

    目前Mybatis-Tiny支持主流的数据库:mysql,mariadb,oracle,db2,sqlserver,postgresql,h2,hsql,sqlite,clickhouse

    对于非主流数据库,可参照<a href="#alternativeDbDialect">非主流数据库方言支持</a>

  • Entity实体类是基于注解的(注解类的设计基本与JPA的注解规范一致);实体类必须实现EntityObject接口,例如:

    @Table("t_product_base_info")
    public class ProductBaseInfo implements EntityObject {
    
        /** 商品ID */
        @Id(strategy=GenerationType.IDENTITY)
        private Long productId;
    
        /** 商品名称 */
        private String productName;
    
      ...
    
        /** 审核状态:0-待审核,1-审核通过,2-审核不通过 */
        private Integer auditStatus;
    
        /** 上下架状态:0-已下架,1-已上架 */
        private Integer onlineStatus;
        
        /** 所属店铺ID */
        //shopId字段在所有update操作时不会被更新(不在update列中)
        @Column(updatable=false)
        private Long shopId;
    
        /** 商品备注 */
        private String remark;
    
        /** 创建时间 */
        //createTime字段在所有update操作时不会被更新(不在update列中)
        @Column(updatable=false, select="DATE_FORMAT({name}, '%Y-%m-%d %T')")
        private String createTime;
    
        /** 最近修改时间 */
        @Column(select="DATE_FORMAT({name}, '%Y-%m-%d %T')")
        private String updateTime;
    
        //以下属于辅助字段
    
        /** productType的查询结果辅助字段 */
        @Transient
        private String productTypeName;
    
        /** auditStatus的查询结果辅助字段 */
        @Transient
        private String auditStatusName;
    
        /** onlineStatus的查询结果辅助字段 */
        @Transient
        private String onlineStatusName;
    
        /** auditStatus的IN查询条件辅助字段 */
        @Transient
        private List<Integer> auditStatuses;
        
        //getter/setter...
        
        /**
         * 实现该方法是可选的!
         * 
         * 返回领域实体的主键值,当存在联合主键时,在CRUD时特别有用
         * 联合主键(com.penglecode.codeforce.common.domain.ID)
         */
        @Override
        public Long identity() {
            return productId;
        }
    
        /**
         * 实现该方法是可选的!
         *
         * 这个方法在所有SELECT操作返回结果集前都会由Mybatis
         * 插件DomainObjectQueryInterceptor自动执行
         * 
         * 通过实现该方法来实现诸如枚举decode能力
         */
        @Override
        public ProductBaseInfo processOutbound() {
            Optional.ofNullable(ProductTypeEnum.of(productType)).map(ProductTypeEnum::getTypeName).ifPresent(this::setProductTypeName);
            Optional.ofNullable(ProductAuditStatusEnum.of(auditStatus)).map(ProductAuditStatusEnum::getStatusName).ifPresent(this::setAuditStatusName);
            Optional.ofNullable(ProductOnlineStatusEnum.of(onlineStatus)).map(ProductOnlineStatusEnum::getStatusName).ifPresent(this::setOnlineStatusName);
            return this;
        }
        
    }
    
  • 支持基于Lambda的DSL方式查询是必须的,例如:

    ProductBaseInfo queryRequest = ...
    QueryCriteria<ProductBaseInfo> queryCriteria = LambdaQueryCriteria.of(queryRequest)
            .likeRight(ProductBaseInfo::getProductName)
            .eq(ProductBaseInfo::getProductType)
            .eq(ProductBaseInfo::getOnlineStatus, 1) //固定某个查询条件值
            .in(ProductBaseInfo::getAuditStatus, queryRequest.getAuditStatuses().toArray())
            .orderBy(OrderBy.desc(ProductBaseInfo::getCreateTime))
            .limit(5)
            .dynamic(true); //自动过滤掉空值(null|空串|空数组|空集合)查询参数;
    List<ProductBaseInfo> productBases = productBaseInfoMapper.selectListByCriteria(queryCriteria);
    
  • 支持指定SELECT返回列、UPDATE更新列那都是必须的,例如:

    //更新指定列
    ProductBaseInfo updateRequest = ...
    Map<String,Object> updateColumns = MapLambdaBuilder.of(updateRequest)
            .with(ProductBaseInfo::getProductName)
            .with(ProductBaseInfo::getRemark)
            .withDefault(ProductBaseInfo::getProductType, 1)
            .withOverride(ProductBaseInfo::getAuditStatus, 0)
            .withOverride(ProductBaseInfo::getOnlineStatus, 0)
            .withOverride(ProductBaseInfo::getUpdateTime, DateTimeUtils.formatNow())
            .build();
    productBaseInfoMapper.updateById(updateRequest.identity(), updateColumns);
    
    //查询返回指定列
    ProductBaseInfo productBase = productBaseInfoMapper.selectById(1L, new QueryColumns(ProductBaseInfo::getProductId, ProductBaseInfo::getProductName, ProductBaseInfo::getAuditStatus, ProductBaseInfo::getOnlineStatus));
    
  • 自带基于RowBounds的分页功能,不管是调用BaseEntityMapper#selectPageListByCriteria(QueryCriteria<T>, RowBounds)还是调用自定义的分页查询方法XxxMapper#selectXxxListByPage(Xxx condition, RowBounds)都将会被自动分页,例如:

    public List<ProductBaseInfo> queryProductListByPage(ProductBaseInfo queryRequest, Page page) {
        QueryCriteria<ProductBaseInfo>> queryCriteria = LambdaQueryCriteria.of(queryRequest)
                    .like(ProductBaseInfo::getProductName)
                    .eq(ProductBaseInfo::getProductType)
                    .eq(ProductBaseInfo::getOnlineStatus)
                    .in(ProductBaseInfo::getAuditStatus, queryRequest.getAuditStatuses().toArray())
                    .orderBy(page.getOrderBys())
                    .dynamic(true); //自动过滤掉为空值(null|空串|空数组|空集合)的查询参数
        List<ProductBaseInfo> productBases = productBaseInfoMapper.selectPageListByCriteria(queryCriteria, new RowBounds(page.offset(), page.limit()));
        page.setTotalRowCount(productBaseInfoMapper.selectPageCountByCriteria(queryCriteria)); //设置总记录数
        return productBases;
    }
    
  • 在Xxx实体对象的XxxMapper中自定义方法肯定是可以的:

    ProductBaseInfoMapper.xml

  • WHERE条件逻辑嵌套查询仅支持嵌套一层(在单表操作中仅支持一层嵌套已经能满足绝大多数要求了),例如:

    QueryCriteria<ProductBaseInfo> queryCriteria = LambdaQueryCriteria.of(queryRequest)
                    //仅支持一层嵌套
                    .and(nestedCriteria -> nestedCriteria.like(ProductBaseInfo::getProductName, "华为")
                            .or().like(ProductBaseInfo::getProductName, "HUAWEI"))
                    .eq(ProductBaseInfo::getProductType)
                    .eq(ProductBaseInfo::getOnlineStatus)
                    .in(ProductBaseInfo::getAuditStatus, queryRequest.getAuditStatuses().toArray())
                    .orderBy(page.getOrderBys())
                    .dynamic(true); //自动过滤掉为空值(null|空串|空数组|空集合)的查询参数
    

    上面DSL语句的实际输出SQL如下:

    - ==>  Preparing: SELECT t.product_id productId, t.product_name productName, t.product_type productType, t.audit_status auditStatus, t.online_status onlineStatus FROM t_product_base_info t WHERE ( t.product_name like ? OR t.product_name like ? ) AND t.product_type = ? AND t.audit_status in ( ? , ? , ? ) ORDER BY t.create_time DESC LIMIT 0, 10
    - ==> Parameters: %华为%(String), %HUAWEI%(String), 1(Integer), 0(Integer), 1(Integer), 2(Integer)
    - <==      Total: 10
    
  • 扩展了Mybatis的org.apache.ibatis.executor.Executor,叫DynamicExecutor,用于解决在使用mybatis-spring框架时在同一个事务中不能切换ExecutorType的蛋疼问题(如果你硬要这么做,你将会得到一个异常:'Cannot change the ExecutorType when there is an existing transaction'),这个Mybatis本身设计导致(SqlSession中固化了ExecutorType),派生出DynamicExecutor就是来解决这个问题的。

  • 仅支持单表CRUD操作,不支持多表JOIN,不支持聚合查询(聚合函数+GROUP BY)

    写这个框架的当初初衷仅仅是为了能够省去编写千篇一律的单表CRUD(XxxMapper.xml),如果做多表JOIN及聚合查询的话,则就失去了使用Mybatis的意义了,还不如直接使用JPA。试想你把一个复杂查询通过DSL的方式写在JAVA代码中,这跟十多年前在JAVA或者JSP代码中写SQL一样,感觉很恶心。

  • 仅提供了通用的BaseEntityMapper,没有提供BaseService之类的,BaseEntityMapper的方法如下:

    mapper.png
  • 支持对BaseEntityMapper的扩展,扩展基础Mapper方法是基于约定的,例如存在这样的扩展:

    package com.penglecode.codeforce.mybatistiny.examples.extensions;
    
    import com.penglecode.codeforce.common.domain.EntityObject;
    import com.penglecode.codeforce.mybatistiny.dsl.QueryColumns;
    import com.penglecode.codeforce.mybatistiny.mapper.BaseEntityMapper;
    import org.apache.ibatis.annotations.Param;
    
    /**
     * 增强功能的BaseEntityMapper扩展
     *
     * @author pengpeng
     * @version 1.0
     */
    public interface EnhancedBaseMapper<T extends EntityObject> extends BaseEntityMapper<T> {
    
        /**
         * 通过标准MERGE INTO语句来进行合并存储
         *
         * @param mergeEntity       - 被更新的实体对象
         * @param updateColumns     - 如果是update操作,此参数可指定被更新的列
         * @return
         */
        int merge(@Param("mergeEntity") T mergeEntity, @Param("updateColumns") QueryColumns... updateColumns);
    
    }
    

    基于约定的,你必须在同样package下存在EnhancedBaseMapper.ftl

    Freemarker模板中的预置参数集见EntityMapperTemplateParameter

    OK,这就扩展好了,就是这么简单!

使用方式

Mybatis-Tiny是一层很薄的东西,没有任何特性化的自定义配置,其仅依赖Mybatis本身(不依赖于Spring或SpringBoot)

其Maven依赖:

<dependency>
    <groupId>io.github.penggle</groupId>
    <artifactId>mybatis-tiny-core</artifactId>
    <!-- 版本说明:3.5指的是基于Mybatis 3.5.x版本的意思 -->
    <version>3.5</version>
</dependency>

下面列举三种使用场景。

  • 只使用Mybatis(无Spring、SpringBoot等大型框架的支持)

    //我不管你其他配置是啥,只要sqlSessionFactory实例是通过DecoratedSqlSessionFactoryBuilder弄出来的就行了!!!
    SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new DecoratedSqlSessionFactoryBuilder();
    SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
    
  • 仅与Spring框架(准确地说是mybatis-spring)集成使用

    引入相关Maven依赖后在配置类上使用注解@EnableMybatisTiny即可,例如:

    import com.penglecode.codeforce.mybatistiny.EnableMybatisTiny;
    import com.penglecode.codeforce.mybatistiny.core.DecoratedSqlSessionFactoryBuilder;
    import com.penglecode.codeforce.mybatistiny.examples.BasePackage;
    import com.zaxxer.hikari.HikariConfig;
    import com.zaxxer.hikari.HikariDataSource;
    import org.apache.commons.lang3.ArrayUtils;
    import org.apache.ibatis.annotations.Mapper;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.context.EnvironmentAware;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    @Configuration
    @EnableMybatisTiny
    @EnableTransactionManagement(proxyTargetClass=true)
    @MapperScan(basePackageClasses=BasePackage.class, annotationClass=Mapper.class)
    @ComponentScan(basePackageClasses=BasePackage.class)
    @PropertySource(value="classpath:application.yml", factory=YamlPropertySourceFactory.class)
    public class MybatisConfiguration {
    
        @Bean
        public DataSource dataSource() {
            Properties properties = ...
            return new HikariDataSource(new HikariConfig(properties));
        }
    
        @Bean
        public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            sqlSessionFactoryBean.setDataSource(dataSource);
            //这里用不用Mybatis-Tiny提供的DecoratedSqlSessionFactoryBuilder是可选的
            //如果不用,在Spring环境下Mybatis-Tiny框架是有应对的弥补措施的
            //sqlSessionFactoryBean.setSqlSessionFactoryBuilder(new DecoratedSqlSessionFactoryBuilder());
            sqlSessionFactoryBean.setConfigLocation(getConfigLocation());
            sqlSessionFactoryBean.setTypeAliasesPackage(getTypeAliasesPackage());
            sqlSessionFactoryBean.setTypeAliasesSuperType(getTypeAliasesSuperType());
            sqlSessionFactoryBean.setMapperLocations(getMapperLocations());
            return sqlSessionFactoryBean;
        }
    
        @Bean
        public DataSourceTransactionManager transactionManager(DataSource dataSource) {
            return new DataSourceTransactionManager(dataSource);
        }
        
        ...
        
    }
    
  • 仅与SpringBoot框架(准确地说是mybatis-spring-boot-starter)集成使用

    引入相关Maven依赖后在SpringBoot启动类上使用注解@EnableMybatisTiny即可,例如:

    import com.penglecode.codeforce.mybatistiny.EnableMybatisTiny;
    import com.penglecode.codeforce.mybatistiny.examples.BasePackage;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @EnableMybatisTiny
    @SpringBootApplication(scanBasePackageClasses=BasePackage.class)
    public class MybatisTinyExampleApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MybatisTinyExampleApplication.class, args);
        }
    
    }
    

    mybatis-spring-boot-starterDataSource的配置照旧就好了,application.yml例如:

    #SpringBoot应用的名称
    spring:
        application:
            name: mybatis-tiny-examples-springboot
        #Hikari 连接池配置
        datasource:
            hikari:
                #连接池名字
                pool-name: defaultHikariCP
                #最小空闲连接数量
                minimum-idle: 5
                #空闲连接存活最大时间,默认600000(10分钟)
                idle-timeout: 180000
                #连接池最大连接数,默认是10
                maximum-pool-size: 10
                #池中连接的默认自动提交行为,默认值true
                auto-commit: true
                #池中连接的最长生命周期,0表示无限生命周期,默认1800000(30分钟)
                max-lifetime: 1800000
                #等待来自池的连接的最大毫秒数,默认30000(30秒)
                connection-timeout: 30000
                #连接测试语句
                connection-test-query: SELECT 1
            username: root
            password: 123456
            url: jdbc:mysql://127.0.0.1:3306/examples?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=GMT%2B8&useSSL=false&rewriteBatchedStatements=true&useCursorFetch=true
    
    #Mybatis-SpringBoot配置
    mybatis:
        config-location: classpath:config/mybatis/mybatis-config.xml
        mapper-locations: classpath*:com/penglecode/codeforce/mybatistiny/examples/**/*Mapper.xml
        type-aliases-package: com.penglecode.codeforce.mybatistiny.examples
        type-aliases-super-type: com.penglecode.codeforce.common.domain.DomainObject
    
  • 其他框架集成Mybatis-Tiny

    我不管你其他框架具体是啥,只要sqlSessionFactory实例是通过DecoratedSqlSessionFactoryBuilder弄出来的就行了!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容