mybatis 在当前项目中的实际应用及自定义分页的实现

这篇具有很好参考价值的文章主要介绍了mybatis 在当前项目中的实际应用及自定义分页的实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

mybatis 在当前项目中的实际应用及自定义分页的实现

项目中分页代码的解耦

实现目标

实现目标,使用spring 提供的分页相关的类,避免代码中直接使用PageHelper

具体实现

创建自定义PageHelper,并使用spring-data-common提供的具体实现类操作分页

maven 中引入相关依赖

<dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-commons</artifactId>
            <version>2.6.2</version>
</dependency>
public class PageHelper {

    //创建分页参数
    public static Pageable createPageable(int page, int size ) {
        return PageRequest.of(page,size);
    }

    //创建分页参数
    public static Pageable createPageable(int page, int size, Sort sort) {
        return PageRequest.of(page,size,sort);
    }

    public static Page createPage(List content, Pageable pageable, long total) {
        return new PageImpl(content,pageable,total);
    }

    public static Page doSelectPage(Pageable pageable,Select select) {
        com.github.pagehelper.Page page = com.github.pagehelper.PageHelper.startPage(pageable.getPageNumber(), pageable.getPageSize()).doSelectPage(() -> select.doSelect());
        return createPage(page,pageable,page.getTotal());
    }


}

public interface Select {

    void doSelect();

}

这样我们在项目中就不直接与pageHelper 打交道了,将代码控制在自定义的 PageHelper 中了。

具体应用

首先我们的controller层面的过滤bean class 统一继承 PagedRequest.class

public class PagedRequest extends Request {

    /**
     * 页号
     */
    private Integer pageNum;

    /**
     * 单页大小(rows)
     */
    private Integer pageSize;

    public Integer getPageNum() {
        return pageNum;
    }

    public void setPageNum(Integer pageNum) {
        this.pageNum = pageNum;
    }

    public Integer getPageSize() {
        return pageSize;
    }

    public void setPageSize(Integer pageSize) {
        this.pageSize = pageSize;
    }
}

到controller层面 因为要调用service,所以转化为 domain对象 我们使用 spring data 提供的 domain Pageable.class

//调用我们自己的工具类创建分页对象
Pageable pageable =
                PagingHelper.createPageable(merchantAuditListReq.getPageNum(), merchantAuditListReq.getPageSize());

将 Pageable 对象和 过滤条件对象传递给service方法

 Page<MerchantAuditListResultModel> page_result = merchantService.getMerchAuditList(pageable, merchantAuditListParamModel);

service 的方法的返回值必须返回 spring data 提供的domain Page 作为返回值

service 层调用具体的查询语句需要使用mybatis 的分页插件,这里提供了一个工具类 PagingHepler.class

//select 方法有两个参数  1 分页数据  2 具体执行的查询方法
Page<MerchantInfoDetailResult> page = PagingHelper.select(pageable, () ->
                custMerchantInfoMapper.getMerchantDetailInfoByExample(merchantInfoParam));

select 方法的具体实现如下,其实还是调用page hepler 的方法来查询分页,获取到数据之后通过 createPage 来创建我们内部流传的Page domain

    public static Page select(Pageable pageable, PagingSelector pagingSelector) {
        com.github.pagehelper.Page page = PageHelper.startPage(pageable.getPageNumber() + 1, pageable.getPageSize())
                .doSelectPage(new PageHelperSelect(pagingSelector));

        return createPage(page.getResult(), pageable, page.getTotal());
    }
 public static Page createPage(List content, Pageable pageable, long total) {
        return new PageImpl(content, pageable, total);
    }

实体类中枚举的更高实现在MyBatis 中的应用

配置

实现自定义枚举类的 TypeHandler,并将枚举转换的TypeHandler 放入mybatis 的config当中.

public class TypeCodeTypeHandler<E extends Enumerable> extends BaseTypeHandler<E> {

	private Class<E> type;
	private final Map<Integer, E> enums;

	public TypeCodeTypeHandler(Class<E> type) {
		if (type == null) {
			throw new IllegalArgumentException("type argument cannot be null");
		}
	/// 是一个 Java 反射 API 方法,用于获取指定枚举类的所有枚举常量。
		E[] enumArray = type.getEnumConstants();
		Map<Integer, E> enums = new HashMap<Integer, E>();
		for (E each : enumArray) {
			enums.put(each.getCode(), each);
		}
		this.type = type;
		this.enums = enums;
	}

	@Override
	public void setNonNullParameter(PreparedStatement ps, int i, E parameter,
			JdbcType jdbcType) throws SQLException {
		ps.setInt(i, parameter.getCode());
	}

	@Override
	public E getNullableResult(ResultSet rs, String columnName)
			throws SQLException {
		int code = rs.getInt(columnName);
		if (rs.wasNull()) {
			return null;
		} else {
			try {
				return enums.get(code);
			} catch (Exception ex) {
				throw new IllegalArgumentException("Cannot convert " + code
						+ " to " + type.getSimpleName() + " by code value.", ex);
			}
		}
	}

	@Override
	public E getNullableResult(ResultSet rs, int columnIndex)
			throws SQLException {
		int code = rs.getInt(columnIndex);
		if (rs.wasNull()) {
			return null;
		} else {
			try {
				return enums.get(code);
			} catch (Exception ex) {
				throw new IllegalArgumentException("Cannot convert " + code
						+ " to " + type.getSimpleName() + " by code value.", ex);
			}
		}
	}

	@Override
	public E getNullableResult(CallableStatement cs, int columnIndex)
			throws SQLException {
		int code = cs.getInt(columnIndex);
		if (cs.wasNull()) {
			return null;
		} else {
			try {
				return enums.get(code);
			} catch (Exception ex) {
				throw new IllegalArgumentException("Cannot convert " + code
						+ " to " + type.getSimpleName() + " by code value.", ex);
			}
		}
	}
}

public SqlSessionFactory sqlSessionFactory() {
       
        try {
            List<String> packages = Lists.newArrayList();
            packages.add("com.kaffatech.mocha.common.lang.type");
            //创建对应类型的 TypeHandler
            List<TypeHandler> typeHandlers = TypeHandlerUtils.getTypeHandlerByPackages(packages);
            bean.setTypeHandlers(typeHandlers.toArray(new TypeHandler[typeHandlers.size()]));
            return bean.getObject();
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }

使用

枚举类都需要继承自Enumerable 接口

public enum ConsumeTradeStatus implements Enumerable {

    WAIT(0, "待支付"), PROCESSING(2, "支付中"), SUCCESS(3, "支付成功"),FALSE(4,"支付失败"), CLOSE(5, "已失效");

    private Integer code;
    private String description;

    ConsumeTradeStatus(Integer code, String description) {
        this.code = code;
        this.description = description;
    }


    @Override
    public int getCode() {
        return code;
    }

    @Override
    public String getCodeString() {
        return name();
    }

    @Override
    public String getDescription() {
        return description;
    }
}
public interface Enumerable {

	/**
	 * 获取代码
	 * @return
	 */
	int getCode();


	/**
	 * 获取字符型代码(一般情况应为枚举name)
	 * @return
	 */
	String getCodeString();

	/**
	 * 获取描述
	 * @return
	 */
	String getDescription();

}


如果数据库中的字段使用枚举类型,声明为 tinyInt 类型,并在代码生成器那进行属性类型覆盖EntryManualType

 <table tableName="accounting_entry_apply">
            <columnOverride column="entry_manual_type"
                            javaType="com.flashfin.flp.ms.domain.accounting.type.EntryManualType"></columnOverride>
            <columnOverride column="audit_status"
                            javaType="com.flashfin.flp.ms.domain.accounting.type.AuditStatus"></columnOverride>
            <columnOverride column="deleted" javaType="java.lang.Boolean"></columnOverride>
            <columnOverride column="create_time" javaType="java.time.Instant"></columnOverride>
            <columnOverride column="update_time" javaType="java.time.Instant"></columnOverride>
            <columnOverride column="audit_time" javaType="java.time.Instant"></columnOverride>
        </table>

关于这样做的好处

记当前项目中国际化的做法_奋斗的小面包的博客-CSDN博客

扩展

分页原理的实现

常见的分页方式有如下两种

PageHelper.startPage(1, 10);
Page<User> users = (Page<User>) userMapper.selectList("user",null);//也可以是 List<Country> list = countryMapper.selectIf(1);

这种机制的实现原理是在调用PageHelper.start 的时候创建一个Page对象放入threadLocal 中,当mybatis的插件拦截到他时获取threadLocal中的page对象,进行sql的改写,之后讲查询到的结果放入 page 对象之中进行返回,Page是派生自List的,所以这也就是解释了为什么我们使用List 接收或使用Page 进行接收都不会报错的原理。

mybatis 在当前项目中的实际应用及自定义分页的实现

第二种分页方式如下

Page<Country> page = PageHelper.startPage(1, 10).doSelectPage(new ISelect() {
    @Override
    public void doSelect() {
        countryMapper.selectGroupBy();
    }
});

这又是什么机理呢,其实妙处就在于Page对象的提前暴露,当我们在调用调用start 的时候会创建一个page对象,并将该对象放入ThreadLocal当中,并将Page对象返回,这就给我我们机会提前引用,调用select 的时候会执行具体的dao操作,此时就算在plugin 中删除了ThreadLocal的Page的对象,我们外接依旧是有引用的

源码展示
//pagehelper 中
public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
        Page<E> page = new Page<E>(pageNum, pageSize, count);
        page.setReasonable(reasonable);
        page.setPageSizeZero(pageSizeZero);
        //当已经执行过orderBy的时候
        Page<E> oldPage = getLocalPage();
        if (oldPage != null && oldPage.isOrderByOnly()) {
            page.setOrderBy(oldPage.getOrderBy());
        }
        setLocalPage(page);
        return page;
    }
//Page 中

 public <E> Page<E> doSelectPage(ISelect select) {
        select.doSelect();
        return (Page<E>) this;
    }

说了这么久,分页插件的大致机理已经了解了,多说无益我们直接实现一下自定义的分页插件吧

实现自定义分页 插件

@Intercepts(
        {
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
        }
)

public class PageHelper implements Interceptor {
    public static ThreadLocal<Pageable> pageInfos = new ThreadLocal<>();

    public static void startPage(int pageNum,int pageSize) {
        pageInfos.set(PageRequest.of(pageNum,pageSize));
    }

    public static Pageable getPage() {
        return pageInfos.get();
    }

    public static void remove() {
        pageInfos.remove();
    }


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {

            MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
            SqlSource sqlSource = mappedStatement.getSqlSource();
            BoundSql boundSql;
            if (invocation.getArgs().length == 6) {
                boundSql = (BoundSql) invocation.getArgs()[5];
            } else {
                boundSql = mappedStatement.getBoundSql(invocation.getArgs()[1]);
            }

            Object parameterObject = invocation.getArgs()[1];
            //判读是否需要分页,判断的逻辑就是PageHelper 中是否有page信息
            Pageable page = PageHelper.getPage();
            if (page != null) {
                //查询总的total
                String sql = boundSql.getSql();
                String prefix = "Select count(0) ";
                int from = sql.indexOf("from");
                String countSql = prefix + sql.substring(from, sql.length());
                Executor executor = (Executor) invocation.getTarget();

                ResultMap resultMap = new ResultMap.Builder(mappedStatement.getConfiguration(), mappedStatement.getId() + "count", Integer.class, Collections.EMPTY_LIST, true).build();

                StaticSqlSource countBoundSql = new StaticSqlSource(mappedStatement.getConfiguration(), countSql, boundSql.getParameterMappings());

                MappedStatement countMappedStatement = new MappedStatement.Builder(mappedStatement.getConfiguration(), mappedStatement.getId() + "count", countBoundSql, mappedStatement.getSqlCommandType())
                        .cache(mappedStatement.getCache())
                        .databaseId(mappedStatement.getDatabaseId())
                        .fetchSize(mappedStatement.getFetchSize())
                        .statementType(mappedStatement.getStatementType())
                        .resultMaps(Arrays.asList(resultMap)).build();

                List<Object> query = executor.query(countMappedStatement,parameterObject,(RowBounds)invocation.getArgs()[2],(ResultHandler)invocation.getArgs()[3]);
                int total = (int) query.get(0);
                //分页插叙
                int pageSize = page.getPageSize();
                int pageNumber = page.getPageNumber();

                String selectSql = boundSql.getSql();
                selectSql = selectSql + " " + "limit " + (pageNumber - 1) * pageSize + "," + pageSize;

                BoundSql selectBoundSql = new BoundSql(mappedStatement.getConfiguration(), selectSql, boundSql.getParameterMappings(), boundSql.getParameterObject());
                //进行查询操作
                Object proceed = executor.query(mappedStatement,parameterObject,(RowBounds)invocation.getArgs()[2],(ResultHandler)invocation.getArgs()[3],(CacheKey) invocation.getArgs()[4],selectBoundSql);
                Page ret = new Page(pageNumber, pageSize);
                ret.setTotal(total);
                ret.addAll((Collection) proceed);
                return ret;
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            //清除分页信息
            PageHelper.remove();
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target,this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
@Configuration
public class PageHelperConfig implements InitializingBean {
    @Autowired
    private SqlSessionFactory sqlSessionFactory;


    public void afterPropertiesSet() throws Exception {
        PageHelper interceptor = new PageHelper();
        interceptor.setProperties(null);
        sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
    }
}

ok ok ok 目前先写到这里,本文档持续更新ing文章来源地址https://www.toymoban.com/news/detail-466812.html

到了这里,关于mybatis 在当前项目中的实际应用及自定义分页的实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java 双指针项目中的实际应用

    最近在做财务相关的系统,对账单核销预付款 从技术角度来看就是将两个数组进行合并 对账单核销预付款前提条件: 对账单总金额必须等于未核销金额 对账单数据 单号 金额 B0001 100 B0002 80 B0003 120 预付款数据 单号 未核销金额 PRE001 110 PRE002 190 结果数据 预付款单号 核销金额

    2024年02月02日
    浏览(104)
  • 策略模式在AIBOT项目中的实际应用

    原文链接 https://www.jylt.cc/#/detail?activityIndex=2id=8d1912358fa1c1d8db1b44e2d1042b70 AIBOT 你想 我来做 AIBOT https://chat.jylt.top/ 定义 策略模式(Strategy Pattern:Define a family of algorithms,encapsulate each one,and make them interchangeable.)中文解释为:定义一组算法,然后将这些算法封装起来,以便它们之间

    2024年01月21日
    浏览(55)
  • Java 双指针在实际项目中的应用

    最近在做财务相关的系统,对账单核销预付款 从技术角度来看就是将两个数组进行合并 对账单核销预付款前提条件: 对账单总金额必须等于未核销金额 对账单数据 单号 金额 B0001 100 B0002 80 B0003 120 预付款数据 单号 未核销金额 PRE001 110 PRE002 190 结果数据 预付款单号 核销金额

    2024年02月02日
    浏览(49)
  • Spring AOP:面向切面编程在实际项目中的应用

    🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页 ——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文并茂🦖生动形象🐅简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍专栏》 🐾 学会IDEA常用操作,工作效率翻倍~💐 🌊 《100天精通Golang(基础

    2024年02月09日
    浏览(43)
  • Mybatis-plus 分页 自定义count方法

    使用了mybatisplus 语句中有order by 语法 mybatisplus会使用 select count(*) from (子语句) TOTAL 算出total 但是会报错 查询条件QueryWrapper中还有order by排序条件,则生成的select count(*) from(sql) 就会报错 除非另外还指定了 TOP、OFFSET 或 FOR XML,否则,ORDER BY 子句在视图、内联函数、派生表、子查

    2024年04月23日
    浏览(49)
  • MyBatis-Plus自定义分页模型

    MyBatis-Plus自带的分页模型Page有些参数,我觉得不是很必要,因此自定义自己的分页模型。该类继承了 IPage 类,实现了简单分页模型如果你要实现自己的分页模型可以继承 Page 类或者实现 IPage 类。因为Java是单继承多实现的,所以我们使用实现IPage接口的方式实现我们自己的分

    2024年02月12日
    浏览(38)
  • Mybatis中的分页插件

    目录 一.为什么要使用分页插件? 二.分页常用标签 三.分页插件的使用         1.在mybatis的pom中添加分页插件依赖         2.在mybatis-config.xml中创建分页插件 3.在test文件中进行查询操作的时候,开启分页功能        PageInfo()         PageHelper.startPage(); 总结: 不必

    2023年04月13日
    浏览(37)
  • 【SpringBoot】MyBatis与MyBatis-Plus分页查询 & github中的PageHelper

            笔者写这篇博客是因为近期遇到的关于两者之间的分页代码差距,其实之前也遇见过但是没有去整理这篇博客,但由于还是被困扰了小一会儿时间,所以还是需要 加深记忆 。其实会看前后端传参解决这个问题很快、不麻烦。关于这两个框架的分页代码问题主要就

    2024年02月03日
    浏览(55)
  • 后端项目开发:分页功能的实现(Mybatis+pagehelper)

    分页查询是项目中的常用功能,此处我们基于Mybatis对分页查询进行处理。 引入分页依赖 在http目录下,新建PageResult类,我们用此类包装分页结果。

    2024年02月11日
    浏览(41)
  • 计算机视觉的实际应用:计算机视觉在实际应用中的成功案例

    计算机视觉(Computer Vision)是一种利用计算机解析、理解并从图像中抽取信息的技术。它是一种跨学科的研究领域,涉及到计算机科学、数学、物理、生物学、心理学等多个领域的知识和技术。计算机视觉的应用范围广泛,包括图像处理、图像识别、机器人视觉、自动驾驶等

    2024年01月23日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包