皮皮网

【小猪 生活通 源码】【静默清粉源码】【连麦app源码】mybatis3 源码分析

2024-11-23 09:07:16 来源:tp资讯源码

1.github上的代码如何运行
2.MyBatis自定义TypeHandler
3.Mybatis 码分OGNL导致的并发安全问题
4.Mybatis中example[Criteria]使用

mybatis3 源码分析

github上的代码如何运行

       ä»Žgithub下的java代码怎么用eclipse运行

       é¦–先,下载github上的Java代码,然后打开eclipse,在左边的工程栏,鼠标右键选择import,然后选择General-ExistingProjectsintoWorkspace,然后选着您下来代码的路径下的文件,接下来一直next就可以了。

       é¦–先在github上搜索你要下载的源码仓库(或者你要是知道地址直接打开即可),这里以mybatis为例点击进入mybatis-3的页面,点击CloneorDownload复制黄色的路劲,这就是我们用Git检出的代码路劲。

       ä»Žgithub导入eclipse代码的方法:eclipse下安装git,点击Help-InstallNewSoftware-add。

       é¦–先:看README.md,好多项目都有,有没有说明。其次:看你下载的属于什么代码,对应到相应的开发环境上。然后:就是在对应的开发环境中编译(脚本语言直接放在应用中)。

       è°è¯´ä¸å¯ä»¥çš„。手把手教你!在eclipse里新建一个project,在project里new一个class,class取名为你源代码中publicclassxxx{ }中的xxx的名字。复制,粘贴,注意。要把程序原来给你的代码替换掉。然后在左侧。

       eclipse新建java程序的话,首先需要创建一个java项目工程,右键菜单,新建javaproject,会自动添加工程结构,然后右键src文件夹,选择class,写入你的代码,最主要有main函数,点击运行的话,就会执行你main函数的里面的语句。

如何运行github上的代码

       ä¸‹è½½ä»£ç ï¼šåœ¨GitHub上找到所需的项目,选择“Cloneordownload”按钮,可以选择“DownloadZIP”直接下载压缩包,或者使用Git工具进行下载。解压代码:将下载的ZIP压缩包解压到本地指定的目录中。

       å®‰è£…好之后搜索GitHub到达官网在上面搜索。登录账号之后点击这里,在点击zip下载压缩文件。解压这个文件之后,别放在C盘即可,之后按住shift+右键打开命令窗口。

       æŠŠmain中的图片、xml等等都复制到android-ui中对应的位置上,再导入对应v4或者v7包就好了。我也常常在github上下载项目,基本都会碰到这个问题,那上面感觉用eclipse的是少数。

       é¦–先:看README.md,好多项目都有,有没有说明。其次:看你下载的属于什么代码,对应到相应的开发环境上。然后:就是在对应的开发环境中编译(脚本语言直接放在应用中)。

       é¦–先,下载github上的Java代码,然后打开eclipse,在左边的工程栏,鼠标右键选择import,然后选择General-ExistingProjectsintoWorkspace,然后选着您下来代码的路径下的文件,接下来一直next就可以了。

       vscode本身是一个源代码编辑器,本身并不具备运行前端应用的功能。正确的做法是:首先用本地git客户端将vue项目从github克隆到本地,用vscode打开,完成编辑。

从github上面下载的项目怎么运行

       1、下载代码:在GitHub上找到所需的项目,选择“Cloneordownload”按钮,可以选择“DownloadZIP”直接下载压缩包,或者使用Git工具进行下载。解压代码:将下载的ZIP压缩包解压到本地指定的目录中。

       2、安装好之后搜索GitHub到达官网在上面搜索。登录账号之后点击这里,在点击zip下载压缩文件。解压这个文件之后,别放在C盘即可,之后按住shift+右键打开命令窗口。

       3、首先,当然是要下载和安装软件了(找不到软件在哪里下的,看下图有地址。),这个比较简单,这里就不再细说。我们着重说下怎么使用。安装好后运行软件,使用你的GitHub账号登陆。

       4、把main中的图片、xml等等都复制到android-ui中对应的位置上,再导入对应v4或者v7包就好了。我也常常在github上下载项目,基本都会碰到这个问题,那上面感觉用eclipse的是少数。

       5、要在本地运行GitHub下载的Lantern,需要进行以下步骤:从Lantern的GitHub仓库中下载最新版本的Lantern。解压缩下载的Lantern压缩包。在终端或命令行中进入Lantern的目录。

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 OGNL导致的并发安全问题

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

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

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

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

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

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

Mybatis中example[Criteria]使用

       æˆ‘们这个直接看源码看如何由example映射到sql语句

        Stock.java

        只有5个属性。

        StockMapper.java:

        我看只看countByExample接口,看名字就知道是返回给定example参数的结果个数。

        再看

        StockMapper.xml该方法实现:

        很简单给定参数类型StockExample 返回Long类型 select count(*) from stock 返回满足条件的stock表中数据数量

        再看<include refid="Example_Where_Clause" />

        foreach表示循环,分隔符是or(或者)重点在于数据集oredCriteria,我们去给定参数StockExample中寻找oredCriteria

        是一个List刚好和上面的集合对应了,看看单个Criteria是什么

        再看GeneratedCriteria

        我们发现Criteria里面居然还有一个list,我们翻上去看刚好与上面一个foreach里面还有一个foreach对应,那接下来研究Criterion

        这就是该类的声明,里面有这些值,这时候应该结合着之前xml第二层foreach属性看了,知道这些值有啥用

        一看我们根据when知道有四类情况,noValue,singleValue,betweenValue,listValue,对应着查询中四种情况

        1.只是单纯是否非空

        2.value=?多少查询

        3.两者之间查询

        4.在list中查询。

        所以我们构建的查询参数就是criterion,看一下上面四种咋构建吧

        以下均是lGeneratedCriterial类中方法,addCriterion是把生成的Criterion加到Ctriteria中

        1.是否非空:

        2.值查询

        public Criteria andIdEqualTo(Integer value) {

        addCriterion("id =", value, "id");

        return (Criteria) this;

        }

        3.两者之间

        public Criteria andSaleNotBetween(Integer value1, Integer value2) {

        addCriterion("sale not between", value1, value2, "sale");

        return (Criteria) this;

        }

        4.list

        public Criteria andVersionNotIn(List<Integer> values) {

        addCriterion("version not in", values, "version");

        return (Criteria) this;

        }

        完结,相信通过上面的源码剖析,大家都知道如何构建查询条件了把。

        总结

        StockExample.java 构造

        三个参数

        orderByClause:返回结果升降排序,字段+空格+acs/dcs(升序/降序)例子count acs

        setOrderByClause方法注入

        distinct:true/false 返回结果去不去重复,setDistinct注入

        oredCriteria:就是查询参数,之前狗讲了啥意思,构造方法

        构造Criteria

        收工。