【定制轻商城源码】【IF导航系统源码】【源码下载站渗透】resultmap源码

时间:2024-12-28 22:55:20 编辑:qgis源码 添加ui 来源:云源码分享网

1.MyBatis 源码解析:映射文件的加载与解析(上)
2.2万多行MyBatis源码,你知道里面用了多少种设计模式吗?
3.重磅!MyBatis-Plus 可视化代码生成器来啦,开发效率提升2倍 !
4.MyBatis-Plus中如何使用ResultMap
5.MyBatis自定义TypeHandler
6.Mybatis TypeHandler(类型处理器)转换参数到SQL和转换SQL查询结果到Java类

resultmap源码

MyBatis 源码解析:映射文件的加载与解析(上)

       MyBatis 的映射文件是其核心组成部分,用于配置 SQL 语句、定制轻商城源码二级缓存及结果集映射等功能,是其区别于其他 ORM 框架的重要特色。

       在解析映射文件时,MyBatis 通过调用 XMLMapperBuilder#parse 方法实现加载与解析操作。此方法首先判断映射文件是否已解析,若未解析则调用 XMLMapperBuilder#configurationElement 方法解析所有配置,并注册当前映射文件关联的 Mapper 接口。对于处理异常的标签,MyBatis 会记录至 Configuration 对象并尝试二次解析。

       解析流程主要涉及以下几个关键步骤:

       缓存配置(cache 标签):MyBatis 采用缓存设计,分为一级缓存和二级缓存。解析 cache 标签时,首先获取相关属性配置,然后使用 CacheBuilder 创建缓存对象,并记录到 Configuration 对象。IF导航系统源码

       缓存引用(cache-ref 标签):标签默认限定在 namespace 范围内,用于引用其它命名空间中的缓存对象。解析过程中记录引用关系,然后从 Configuration 中获取引用的缓存对象。

       结果集映射(resultMap 标签):解析 resultMap 标签配置,构建 ResultMap 对象,并将其记录到 Configuration 中。

       SQL 语句(sql 标签):通过 sql 标签配置复用的 SQL 语句片段,解析后记录至 Configuration 的 sqlFragments 属性中。

       核心数据库操作(select / insert / update / delete 标签):解析这些标签时,构建 MappedStatement 对象并记录到 Configuration 中。

       每个标签解析实现由 MyBatis 提供的多个方法执行,如 XMLMapperBuilder 的 configurationElement 方法和解析具体标签的子方法,如 cacheElement、sqlElement 等。解析过程中,MyBatis 会调用不同的构造器和工厂方法来创建、初始化和配置相应的对象。

       在解析完成之后,MyBatis 源码下载站渗透将所有配置对象封装在 Configuration 对象中,该对象包含所有映射文件中定义的配置信息,供后续的 SQL 语句执行和映射操作使用。

2万多行MyBatis源码,你知道里面用了多少种设计模式吗?

       在MyBatis的两万多行的框架源码中,设计模式的巧妙使用是整个框架的精华。

       MyBatis中主要使用了以下设计模式:工厂模式、单例模式、建造者模式、适配器模式、代理模式、组合模式、装饰器模式、模板模式、策略模式和迭代器模式。

       具体来说,工厂模式用于SqlSessionFactory的创建,单例模式用于Configuration的管理,建造者模式用于ResultMap的构建,适配器模式用于统一日志接口,代理模式用于MapperProxy的tidb源码有多少行实现,组合模式用于SQL标签的组合,装饰器模式用于二级缓存操作,模板模式用于定义SQL执行流程,策略模式用于多类型处理器的实现,迭代器模式用于字段解析的实现。

       通过运用这些设计模式,MyBatis成功地实现了复杂场景的解耦,并将问题合理切割为若干子问题,以提高理解和解决的效率。

       总的来说,MyBatis大约运用了种左右的设计模式,这使得框架在处理复杂问题时能够更加高效和灵活。

       学习源码不仅可以帮助我们更好地理解设计模式和设计原则,更能够扩展我们的编码思维,积累实际应用的经验。

       希望本文的分享能够帮助到您,同时也推荐您阅读《手写MyBatis:渐进式源码实践》一书,了解更多关于MyBatis的知识。

重磅!MyBatis-Plus 可视化代码生成器来啦,区块链平台app源码开发效率提升2倍 !

       基于Mybatis-Plus的代码自助生成器

       在使用Mybatis-Plus进行开发时,可以提高开发效率。然而,常规的代码生成工具可能无法满足所有需求,尤其是对于多数据库的支持,以及高度定制化的要求。因此,一款支持图形用户界面的代码生成框架变得尤为重要。本文介绍了一款名为mybatis-plus-generator-ui的代码生成器,旨在为开发者提供更加高效、灵活的代码生成解决方案。

       mybatis-plus-generator-ui是基于Mybatis-Plus生成器进行封装,通过Web界面快速生成兼容Spring Boot和Mybatis-Plus框架的业务代码。它提供了交互式的Web界面,让开发者能够生成符合Mybatis-Plus框架的实体类(Entity)、映射类(Mapper)、Mapper.xml、服务类(Service)、控制器类(Controller)等。此外,该工具支持自定义模板和各类输出参数,甚至可以通过SQL查询语句直接生成代码。

       要使用mybatis-plus-generator-ui,首先可以通过Maven引入jar包,并创建一个程序入口,使用main函数运行。从1.4.0版本开始,mybatis-plus-generator-ui支持独立部署为Spring Boot项目,通过页面指定目标项目的根目录,为多个项目提供源码生成服务。配置示例包括数据库连接信息、运行端口以及默认的模板目录地址。

       使用mybatis-plus-generator-ui进行代码生成时,可以浏览和查询配置的数据源的数据表信息,选择生成模板代码。工具内置了多种代码模板配置,包括Entity、Mapper、Service、Controller等,并提供了模板替换和参数修改功能。此外,支持策略配置,如是否覆盖原有文件、生成文件的种类等。SQL配置生成功能允许用户通过输入SQL查询语句,自动生成对应的查询方法、DTO对象和ResultMap。

       为了满足不同团队的需求,mybatis-plus-generator-ui提供了代码模板调整和自定义扩展功能。开发者可以通过修改btl模板文件来自定义代码生成的各个方面,包括代码层级配置、服务层、控制器层、实体类命名等。mybatis-plus-generator-ui的前端界面也支持自定义修改,只需将代码clone下来,进入frontend目录进行扩展开发即可。

       总结来说,mybatis-plus-generator-ui为基于Mybatis-Plus的开发提供了高效、灵活的代码生成工具,简化了代码开发流程,提高了开发效率。通过实例集成和详细的扩展开发介绍,本文旨在为有需要的开发者提供实用的指导和帮助。

MyBatis-Plus中如何使用ResultMap

       MyBatis-Plus 对 MyBatis 基本零侵入,完全可以与 MyBatis 混合使用,这点很赞。

        在涉及到关系型数据库增删查改的业务时,我比较喜欢用 MyBatis-Plus ,开发效率极高。具体的使用可以参考官网,或者自己上手摸索感受一下。

        下面简单总结一下在 MyBatis-Plus 中如何使用 ResultMap 。

        先看个例子:

        有如下两张表:

        其中, tb_hero 中的 bid 关联 tb_book 表的 id 。

        下面先看 Hero 实体类的代码,如下:

        注意了,我特地把 tb_hero 表中的 bid 字段映射成实体类 Hero 中的 bookId 属性。

        MyBatis-Plus 打印出的 SQL 为:

        没毛病, MyBatis-Plus 会根据 @TableField 指定的映射关系,生成对应的 SQL 。

        MyBatis-Plus 打印出的 SQL 为:

        也没毛病,可以看到生成的 SELECT 中把 bid 做了别名 bookId 。

        比如现在我想连接 tb_hero 与 tb_book 这两张表,如下:

        查询 MyBatis-Plus 打印出的 SQL 为:

        SQL没啥问题,过滤与分页也都正常,但是此时你会发现 bookId 属性为 null ,如下:

        为什么呢?

        调用 BaseMapper 中内置的 selectById() 方法并没有出现这种情况啊???

        回过头来再对比一下在 HeroMapper 中自己定义的查询与 MyBatis-Plus 自带的 selectById() 有啥不同,还记得上面的刚刚的测试吗,生成的SQL有啥不同?

        原来, MyBatis-Plus 为 BaseMapper 中内置的方法生成SQL时,会把 SELECT 子句中 bid 做别名 bookId ,而自己写的查询 MyBatis-Plus 并不会帮你修改 SELECT 子句,也就导致 bookId 属性为 null 。

        在这里就是 tb_hero 表中的 bid 字段映射成实体类 Hero 中的 bid 属性。这样当然可以解决问题,但不是本篇讲的重点。

        在 @TableName 设置 autoResultMap = true

        然后在自定义查询中添加 @ResultMap 注解,如下:

        这样,也能解决问题。

        下面简单看下源码, @ResultMap("mybatis-plus_实体类名") 怎么来的。

        详情见: com.baomidou.mybatisplus.core.metadata.TableInfo#initResultMapIfNeed()

        注意看上面的字符串 id 的构成,你应该可以明白。

        思考: 这种方式的 ResultMap 默认是强绑在一个 @TableName 上的,如果是某个聚合查询或者查询的结果并非对应一个真实的表怎么办呢?有没有更优雅的方式?

        基于上面的思考,我做了下面简单的实现:

        关键代码其实没有几行,耐心看下应该不难懂。

        还是用例子来说明更直观。

        下面是一个聚合查询:

        其中 BookAgg 的定义如下,在实体类上使用了 @AutoResultMap 注解:

MyBatis自定义TypeHandler

       MyBatis自定义TypeHandler1什么是TypeHandler

       TypeHandler根据字面意思即为类型处理器

       引用官方文档的描述:MyBatis在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时,都会用类型处理器将获取到的值以合适的方式转换成Java类型

       MyBatis存在一些默认的类型处理器,可参考官方文档

2为什么要使用TypeHandler

       在开发过程中,当默认的TypeHandler无法满足需求时,例如遇到MyBatis不支持的数据类型或需要特殊处理的类型转换,便需要自己定制对应的TypeHandler

       笔者会在下面的代码实现中完成如下几种情况的TypeHandler:

       逗号分隔保存在数据库中的数据,在对应的Java类中为数组

       自定义枚举

3如何自定义TypeHandler

       MyBatis提供了接口org.apache.ibatis.type.TypeHandler和类org.apache.ibatis.type.BaseTypeHandler

       官方文档给出的示例为继承BaseTypeHandler,笔者在这里也使用这种方式

       先来观察一下官方的StringTypeHandler:

publicclassStringTypeHandlerextendsBaseTypeHandler<String>{ @OverridepublicvoidsetNonNullParameter(PreparedStatementps,inti,Stringparameter,JdbcTypejdbcType)throwsSQLException{ ps.setString(i,parameter);}@OverridepublicStringgetNullableResult(ResultSetrs,StringcolumnName)throwsSQLException{ returnrs.getString(columnName);}@OverridepublicStringgetNullableResult(ResultSetrs,intcolumnIndex)throwsSQLException{ returnrs.getString(columnIndex);}@OverridepublicStringgetNullableResult(CallableStatementcs,intcolumnIndex)throwsSQLException{ returncs.getString(columnIndex);}}

       方法名称和代码都简洁明了,观察可知,只需要完成四个方法的覆盖,即可实现自定义TypeHandler

3.1逗号分隔字符串转数组

       假设用户表t_user设计如下:

idusernametags1adminadmin,user

       对应的Java类为:

@Data@NoArgsConstructor@SuperBuilder(toBuilder=true)publicclassUser{ privateStringid;privateStringusername;privateString[]tags;}

       tags属性在数据库中用逗号分隔的字符串保存,但User类对应的属性为String数组

       可以创建StringArrayTypeHandler来解决类型转换的问题:

publicclassStringArrayTypeHandlerextendsBaseTypeHandler<String[]>{ @OverridepublicvoidsetNonNullParameter(PreparedStatementpreparedStatement,inti,String[]strings,JdbcTypejdbcType)throwsSQLException{ preparedStatement.setString(i,StringUtils.join(strings,","));}@OverridepublicString[]getNullableResult(ResultSetresultSet,Strings)throwsSQLException{ returnconvert(resultSet.getString(s));}@OverridepublicString[]getNullableResult(ResultSetresultSet,inti)throwsSQLException{ returnconvert(resultSet.getString(i));}@OverridepublicString[]getNullableResult(CallableStatementcallableStatement,inti)throwsSQLException{ returnconvert(callableStatement.getString(i));}/***将查询值转换为数组**@paramvalue查询值,String*@return转换结果,String[]*/privateString[]convert(Stringvalue){ returnStringUtils.isEmpty(value)?newString[0]:value.split(",");}}3.2自定义枚举

       如何创建包含中文名称的枚举,可以参考MyBatis中使用Java类与枚举

       先创建工具类用于根据code获取枚举实体:

publicclassValueNameEnumUtils{ privateValueNameEnumUtils(){ }publicstatic<EextendsValueNameEnum>EvalueOf(Class<E>enumClass,intvalue){ E[]enumConstants=enumClass.getEnumConstants();for(Ee:enumConstants){ if(e.getValue()==value){ returne;}}returnnull;}}

       和3.1中的情况不同,枚举的具体类型是不确定,所以我们要使用泛型的方式处理TypeHandler

       创建ValueNameEnumTypeHandler:

publicclassValueNameEnumTypeHandler<EextendsValueNameEnum>extendsBaseTypeHandler<ValueNameEnum>{ privatefinalClass<E>type;publicValueNameEnumTypeHandler(Class<E>type){ if(type==null){ thrownewIllegalArgumentException("Typeargumentcannotbenull");}this.type=type;}}

       泛型虽然名之为泛,但在编译过程中实际会发生类型擦除

       总之,对于泛型TypeHandler,我们需要声明一个用来标识具体类型的属性privatefinalClass<E>type和创建对应的构造函数publicValueNameEnumTypeHandler(Class<E>type)

       接下来和3.1中的一致,重写四个方法:

publicclassValueNameEnumTypeHandler<EextendsValueNameEnum>extendsBaseTypeHandler<ValueNameEnum>{ privatefinalClass<E>type;publicValueNameEnumTypeHandler(Class<E>type){ if(type==null){ thrownewIllegalArgumentException("Typeargumentcannotbenull");}this.type=type;}@OverridepublicvoidsetNonNullParameter(PreparedStatementps,inti,ValueNameEnumparameter,JdbcTypejdbcType)throwsSQLException{ ps.setInt(i,parameter.getValue());}@OverridepublicEgetNullableResult(ResultSetrs,StringcolumnName)throwsSQLException{ intcode=rs.getInt(columnName);returnrs.wasNull()?null:valueOf(code);}@OverridepublicEgetNullableResult(ResultSetrs,intcolumnIndex)throwsSQLException{ intcode=rs.getInt(columnIndex);returnrs.wasNull()?null:valueOf(code);}@OverridepublicEgetNullableResult(CallableStatementcs,intcolumnIndex)throwsSQLException{ intcode=cs.getInt(columnIndex);returncs.wasNull()?null:valueOf(code);}/***根据枚举值返回枚举示例**@paramcode枚举值*@return枚举实例*/privateEvalueOf(intcode){ try{ returnValueNameEnumUtils.valueOf(type,code);}catch(Exceptionex){ thrownewIllegalArgumentException("Cannotconvert"+code+"to"+type.getSimpleName()+"bycodevalue.",ex);}}}

       完成上述代码直接启动,会抛出异常:Unabletofindausableconstructorforclasscn.houtaroy.springboot.common.MyBatis.handler.ValueNameEnumTypeHandler

       产生异常的源码如下:

public<T>TypeHandler<T>getInstance(Class<?>javaTypeClass,Class<?>typeHandlerClass){ //未指定JavaType,此处为falseif(javaTypeClass!=null){ try{ Constructor<?>c=typeHandlerClass.getConstructor(Class.class);return(TypeHandler<T>)c.newInstance(javaTypeClass);}catch(NoSuchMethodExceptionignored){ //ignored}catch(Exceptione){ thrownewTypeException("Failedinvokingconstructorforhandler"+typeHandlerClass,e);}}try{ //此处抛出异常Constructor<?>c=typeHandlerClass.getConstructor();return(TypeHandler<T>)c.newInstance();}catch(Exceptione){ thrownewTypeException("Unabletofindausableconstructorfor"+typeHandlerClass,e);}}

       报错的原因直白,没有找到ValueNameEnumTypeHandler的构造函数

       首先我们要了解下Java类构造函数的机制:如果定义了构造函数,则使用定义,否则默认生成空构造函数

       在3.1中的StringArrayTypeHandler,我们没有定义构造函数,自动生成空构造函数,typeHandlerClass.getConstructor()不会抛出异常

       但ValueNameEnumTypeHandler定义了一个构造函数ValueNameEnumTypeHandler(Class<E>type),且没有指定JavaType,typeHandlerClass.getConstructor()自然抛出异常

       解决方法有两种:

       创造空的构造函数

       指定JavaType

       笔者推荐第二种,因为第一种方式枚举类属性type会产生NPE(空指针异常),MyBatis官方也我们提供了注解@MappedTypes用于指定JavaType:

@MappedTypes(ValueNameEnum.class)publicclassValueNameEnumTypeHandler<EextendsValueNameEnum>extendsBaseTypeHandler<ValueNameEnum>{ //...}4如何使用TypeHandler

       在上一章节中,我们完成了编码实现自定义TypeHandler,但完成的TypeHandler还没办法进行使用,需要手动进行配置

       有两种配置方式:局部使用和全局使用

       以StringArrayTypeHandler举例:

4.1局部使用

       在ResultMap中使用:

<resultMapid="UserResultMap"type="cn.houtaroy.springboot.common.system.model.User"><idcolumn="id"property="id"/><resultcolumn="tags"property="tags"typeHandler="cn.houtaroy.springboot.extension.mybatis.handler.StringArrayTypeHandler"/></resultMap>

       在语句中使用:

updatet_usersettags=#{ tags,typeHandler=cn.houtaroy.springboot.extension.mybatis.handler.StringArrayTypeHandler}4.2全局使用

       使用配置文件指定handler包名:

@Data@NoArgsConstructor@SuperBuilder(toBuilder=true)publicclassUser{ privateStringid;privateStringusername;privateString[]tags;}0

       注意,此配置类型为String,只能配置一个包,推荐使用下面的方式

       手写配置类:

@Data@NoArgsConstructor@SuperBuilder(toBuilder=true)publicclassUser{ privateStringid;privateStringusername;privateString[]tags;}1

       StringArrayTypeHandler不适合全局配置,它会在全部JavaType为String[]的属性上使用

5拓展阅读

       MyBatis3官方文档中TypeHandler内容:mybatis–MyBatis3|配置

       网上搜索的在SpringBean声明周期中进行全局配置:Mybatis自定义全局TypeHander_chuobenggu的博客-CSDN博客

Mybatis TypeHandler(类型处理器)转换参数到SQL和转换SQL查询结果到Java类

       TypeHandler在Mybatis中的作用

       在预处理语句(PreparedStatement)中设置参数或从结果集中取出值时,TypeHandler负责将获取的值以合适的方式设置到PreparedStatement,或转换成Java类型。通过重写TypeHandler或创建自己的TypeHandler,可以处理不支持的或非标准类型。

       在项目中,对于只有有限个值的字段,常使用数字类型表示,如考试状态字段exam_status定义为tinyint。在代码中直接使用Integer表示时,读性和维护性较差,故定义枚举类来表示。需要实现TypeHandler以自动完成从数据库数字类型转换成枚举类的过程。

       实现TypeHandler的方案如下:定义枚举类实现EnumBase接口,包含codeOf静态方法进行数字转换。然后创建TypeHandler类实现org.apache.ibatis.type.TypeHandler接口,重写相应方法。避免在xml配置文件为每个枚举单独声明TypeHandler,使用Java代码动态注册,关键在于配置类。

       Mybatis注册工厂TypeHandlerRegistry初始化常见类型转换器,如String类型。执行转换时,通过TypeHandlerRegistry获取TypeHandler。自定义枚举类能起作用,依据TypeHandlerRegistry获取TypeHandler的顺序。Mybatis使用TypeHandler实现参数设置到SQL和转换SQL查询结果到Java类的过程。

       在mapper文件中,所有parameter入参的Java参数关联TypeHandler,解析成ParameterMapping对象,其中每条SQL的parameter入参关联UnknownTypeHandler,因为字段未指定JavaType和jdbcType属性,只有指定parameterType属性,Mybatis才能获取实际Java类型。resultMap的Java参数也关联TypeHandler,通过type属性获取实际Java类型并关联TypeHandler。

       执行代码时,SQL参数设置和查询结果转换到Java类的过程由TypeHandler完成。对于参数设置,调用TypeHandler设置SQL参数,获取真正TypeHandler是通过TypeHandlerRegistry.getTypeHandler方法实现。转换查询结果到Java类的过程复杂,由DefaultResultSetHandler处理。

       了解Mybatis执行流程有助于深入理解TypeHandler的作用和实现细节。建议实际项目中实践TypeHandler的使用,提高代码的可读性和可维护性。

       TypeHandler源码地址:[提供源码GitHub链接]