皮皮网
皮皮网

【vc 定位特征码源码】【android家庭记账app源码】【php社区源码免费下载】mybatis 3.2.2 源码

来源:rocketmq源码环境搭建 发表时间:2024-11-26 19:49:47

1.MyBatis自定义TypeHandler
2.MybatisPlusMP的分页查询、多条件查询以及查询过程中解决null的空值判定
3.MybatisPlus条件构造器Wrapper、分页查询、自定义SQL、Service层接口、代码生成器
4.java17升级21及sbt3.0升级3.2备忘录
5.Mybatis vc 定位特征码源码OGNL导致的并发安全问题

mybatis 3.2.2 源码

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博客

MybatisPlusMP的分页查询、多条件查询以及查询过程中解决null的空值判定

       MybatisPlus这款强大且高效的持久层框架在处理复杂SQL语句时表现出色,极大地提升了效率。本文将带您深入了解MybatisPlus的分页查询、多条件查询以及查询过程中如何解决null值的判定问题。

       ### 分页处理

       在MybatisPlus中进行分页处理,首先通过创建IPage对象并设置分页参数,如当前页码和每页显示记录数,执行分页查询并获取结果。此外,可以配置分页拦截器,将其作为Spring管理的bean对象,以优化查询性能。

       ### 条件查询

       #### 2.1 字符串形式的查询条件

       在条件查询时,通过QueryWrapper对象执行查询,利用字符串形式输出条件可能引发拼写错误。android家庭记账app源码为解决此问题,引入lambda表达式,实现实体与属性对应查询,大幅提升了查询的准确性。

       构建LambdaQueryWrapper时需使用泛型,否则会提示默认的Object类不是函数接口。不使用泛型时,查询条件与实体关联性减弱,导致拼写错误。使用lambda表达式后,避免了此类错误,但增加了lambda()调用层,使得查询逻辑更为清晰。

       #### 2.2 直接通过LambdaQueryWrapper对象

       这种方式是LambdaQueryWrapper方法的直接应用,利用其强大的功能简化多条件查询,支持链式编程。

       ### 多条件查询

       在MybatisPlus中,处理多条件查询问题简单且灵活,支持链式编程。例如,查询年龄在3岁到8岁之间的php社区源码免费下载用户信息或年龄小于3岁或大于8岁的数据。

       #### 3.1 且条件示例

       查询年龄在3岁到8岁之间的用户信息时,使用gt(大于)和lt(小于)方法构建SQL语句,迅速获取结果。

       #### 3.2 或条件示例

       查询年龄小于3岁或大于8岁的数据时,利用or()方法构建SQL语句,同样能够快速查询到目标数据。

       ### 处理null判定

       在使用MybatisPlus进行条件筛选时,特别是当用户输入筛选条件时,需要解决null值判定问题。例如,用户可能只输入价格范围的上界或下界,或者同时输入上下界。针对这种情况,通过新建模型类继承现有实体类,并添加额外属性,如price2,实现对null值的正确处理。在查询条件构建时,利用lt()、gt()方法结合boolean条件判断,灵活处理null值,两小无猜源码确保查询逻辑的准确性。

       最终,通过上述方法,MybatisPlus不仅实现了高效的分页、多条件和null值处理功能,还提供了灵活的查询方式,极大地提升了开发效率和用户体验。

MybatisPlus条件构造器Wrapper、分页查询、自定义SQL、Service层接口、代码生成器

       MybatisPlus提供了强大的条件构造器Wrapper,包括QueryWrapper、UpdateWrapper和LambdaQueryWrapper,用于构造Where、Select和SET条件,极大地简化了数据库操作。

       1. 条件构造器

       Wrapper的子类QueryWrapper用于设置查询列,UpdateWrapper用于设置更新列,LambdaQueryWrapper则支持更灵活的列引用。

       常用方法包括:eq(等于)、eclipse导入纯源码工程gt(大于)、like(模糊匹配)等,例如:

       示例:SQL条件 eq(age, ) 对应Wrapper写法。

       2. 自定义SQL

       虽然MP提供了许多内置方法,但有时仍需自定义SQL。支持Mybatis原有的自定义方法,同时允许使用Wrapper构建条件。

       2.1 Mybatis方式

       1. 定义Mapper接口,如:selectById(User user),并配置XML映射文件。

       2. 使用<select>标签编写自定义SQL。

       2.2 结合条件构造器

       在自定义方法中,使用Wrapper作为参数,拼接条件SQL。

       3. 分页查询

       MP提供分页查询拦截器,如基本分页查询和多表分页查询。

       3.1 基本分页

       配置分页拦截器,使用Page对象进行查询。

       3.2 多表分页

       在接口方法中接收Page对象,处理多表查询。

       4. Service层接口

       Service层支持批量操作,简化接口定义。

       4.1 基本使用

       从手动编写到MP提供的模板,简化接口实现。

       4.2 自定义方法

       Service接口和实现类支持自定义功能。

       5. 代码生成器

       使用代码生成器快速生成PO类、DAO、Mapper、Service接口及实现类,提升开发效率。

       步骤:添加依赖,配置后执行生成代码。

       6. MybatisX插件

       MybatisX是IDEA插件,简化快速开发,一键安装并支持多种功能。

java升级及sbt3.0升级3.2备忘录

       Java与Spring Boot 3.2升级备忘录

       Java于年9月日正式发布,与此同时,Spring Boot在同年月也推出了3.2版本,引入了对Java的关键特性——虚拟线程的支持。为了充分利用这些新特性,我最近将基于Spring Boot的Java开发平台从Java + SBT 3.0.5升级到了Java + SBT 3.2.1。

       Java引入了多项新特性,具体详情可查阅OpenJDK官网或阅读我的另一篇文章。至于Spring Boot 3.2的变化,则可参考官方文档进行深入了解。

       若你的项目还未升级至Java + SBT 3.0,请先阅读我之前的升级指南。

       本次升级主要集中在依赖包版本更新和一些针对Java的适配调整。并非所有升级都是由于Java或SBT 3.2的变动,而是对之前版本的例行更新。

       依赖包版本升级的策略遵循了最新推荐的版本。升级后的依赖版本如下,但请留意可能存在的底层依赖冲突。

       在升级过程中,特别关注了maven-compiler-plugin配置的更新。引入了该插件的配置,确保了编译过程的兼容性与效率。

       mybatis-generator-maven-plugin的配置中,将mybatis-generator使用的MySQL驱动包更新为mysql-connector-j,以适应新环境。

       考虑到虚拟线程的特性,将代码中synchronized关键字替换为ReentrantLock,以避免IO阻塞导致的线程固定问题。在确保代码逻辑正确的情况下,可选择保持原synchronized结构。

       为了最大化虚拟线程的性能优势,尽量将代码中使用的平台线程替换为虚拟线程,包括优化现有代码结构以适应虚拟线程环境。

       在使用try-with-resources时,应谨慎处理与计划器结合的情况。若计划器执行周期性任务,使用try-with-resources可能会导致计划器在预计之外的时间关闭,从而影响任务执行。对于一次性计划任务,尝试使用try-with-resources可能会导致任务意外中断,需根据具体情况判断是否应用此策略。

       IDEA版本的更新(至.3.2)全面支持Java,界面变化较大。建议根据个人习惯调整界面风格,新界面设计美观,值得适应。

       除了代码框架的升级,也同步升级了IDEA插件版本,并更新了本地Maven至3.9.6版本。在高版本Maven中,settings.xml配置文件的某些参数,如connectTimeout与requestTimeout的配置方式发生了变化。

       在升级过程中,注意到了一些小问题,但并未遇到Java或SBT本身的兼容性问题。确保了代码的稳定运行,并对可能出现的依赖冲突和配置调整保持警惕。

Mybatis OGNL导致的并发安全问题

       Mybatis是一个轻量级半自动化ORM框架,通过xml描述符或注解将对象与SQL语句结合,实现面向对象与数据库映射的简化。其最大优势在于将应用程序与Sql语句解耦,Sql语句在xml文件中定义。

       OGNL(Object-Graph Navigation Language)是Mybatis中广泛使用的表达式语言,用于设置和获取Java对象属性,执行列表投影和lambda表达式。灵活的OGNL表达式在Struts2等框架中应用时,也会引入可执行漏洞风险。

       某公司使用Mybatis 3.2.3版本作为数据访问层。在线业务系统运行期间,出现并发安全问题。异常表现为随机出现,构造特定OGNL表达式时不会重现,具体异常堆栈信息显示List的size()方法不可访问。此问题在测试环境未重现,占总调用次数的0.%。

       编写模拟多线程并发环境下的测试代码以验证问题。并发测试代码执行后,异常在预期的并发环境下重现。异常堆栈信息指向OgnlRuntime类无法访问java.util.Collections私有成员SingletonList。

       问题关键在于method作为共享变量,即java.util.Collections$SingletonList.size()方法。在第一个线程允许调用method方法,第二个线程将其设为不可访问后,第一个线程再次调用时引发MethodFailedException异常。这是典型的并发同步问题。

       OGNL 2.7版本已修复此问题,Mybatis在3.3.0版本中进行了修复升级。源码已直接嵌入mybatis包中,解决了并发访问共享资源导致的异常问题。

相关栏目:热点

.重点关注