1前言 大家都知道MyBatis是我们常用的一个持久层框架,那么今天我们就来深入Mybatis底层,来探究MyBatis到底是怎么实现数据库操作的呢?2持久层的开发模式 1传统的开发模式 接口定义业务方法publicinterfaceUserService{publicUsergetUserById(intid);} 实现类实现接口方法(sql语句写在java代码里面)publicclassUserServiceImplimplementsUserService{OverridepublicUsergetUserById(intid){ConnectionconnJDBCTools。getConnection();Stringsqlselectfromuserwhereid?;PreparedStatementpstmtnull;ResultSetrsnull;try{pstmtconn。prepareStatement(sql);pstmt。setInt(1,id);rspstmt。executeQuery();if(rs。next()){intsidrs。getInt(1);Stringnamers。getString(2);UserusernewUser(sid,name);returnuser;}}catch(Exceptione){TODOAutogeneratedcatchblocke。printStackTrace();}finally{JDBCTools。release(conn,pstmt,rs);}returnnull;}} 测试Testpublicvoidf1()throwsException{UserServiceuserServicenewUserServiceImpl();UseruseruserService。getUserById(1);System。out。println(user);} 2Mybatis的开发模式 定义接口方法publicinterfaceUserMapper{publicUsergetUserById(intid);} 接口对应的映射xml文件(sql语句写在xml文件)lt;?xmlversion1。0encodingUTF8?!DOCTYPEmapperPUBLICmybatis。orgDTDMapper3。0ENhttp:mybatis。orgdtdmybatis3mapper。dtdmappernamespacecn。itsource。mapper。UserMapperselectidgetUserByIdparameterTypejava。lang。IntegerresultTypecn。itsource。domain。UserselectfromUserwhereid{id}selectmapper 测试Testpublicvoidf1()throwsException{UserMappermapper(UserMapper)newMyInvocationHandler()。getInstance(UserMapper。class);Userusermapper。getUserById(1);System。out。println(user);} 通过以上代码可以看到,MyBatis的方式省去了实现类的创建,改为用xml来定义业务方法的具体实现 为什么MyBatis可以不用通过实例化对象来操作我们的代码呢?这里就不得不说说jdk的动态代理 jdk动态代理运行时结合接口和mapper。xml来动态创建一个代理对象,程序调用该代理对象的方法来完成业务3jdk动态代理 创建一个类,实现InvocationHandler接口 1自定义getInstance方法:入参为目标对象,通过Proxy。newProxyInstance方法创建代理对象,并返回 2实现接口的invoke方法,通过反射机制完成业务逻辑代码 invoke方法是核心代码,在该方法中实现具体的业务需求 3使用invoke方法解析数据库信息配置xml,创建数据库连接对象读取数据源配置信息publicstaticMapString,StringgetProperties(){MapString,StringmapnewHashMapString,String();SAXReaderreadernewSAXReader();try{Documentdocumentreader。read(srcconfig。xml);获取根节点Elementrootdocument。getRootElement();Iteratoriterroot。elementIterator();while(iter。hasNext()){Elemente(Element)iter。next();解析environments节点if(environments。equals(e。getName())){Iteratoriter2e。elementIterator();while(iter2。hasNext()){解析environment节点Elemente2(Element)iter2。next();Iteratoriter3e2。elementIterator();while(iter3。hasNext()){Elemente3(Element)iter3。next();解析dataSource节点if(dataSource。equals(e3。getName())){if(POOLED。equals(e3。attributeValue(type))){Iteratoriter4e3。elementIterator();获取数据库连接信息while(iter4。hasNext()){Elemente4(Element)iter4。next();map。put(e4。attributeValue(name),e4。attributeValue(value));}}}}}}}}catch(Exceptione){TODOAutogeneratedcatchblocke。printStackTrace();}returnmap;}获取信息,创建数据源对象MapString,StringmapParseXML。getProperties();ComboPooledDataSourcedatasourcenewComboPooledDataSource();datasource。setDriverClass(map。get(driver));datasource。setJdbcUrl(map。get(url));datasource。setUser(map。get(username));datasource。setPassword(map。get(password));datasource。setInitialPoolSize(20);datasource。setMaxPoolSize(40);datasource。setMinPoolSize(2);datasource。setAcquireIncrement(5);Connectionconndatasource。getConnection();4反射 数据库连接,接下来就需要获取待执行的SQL语句,sql的定义全部写在UserMapper。xml中;解析xml执行sql语句,执行完毕,查询结果会保存在ResultSet中,还需要将ResultSet对象中的数据进行解析,封装到JavaBean中返回 上述步骤通过两步实现 第一步:反射机制创建User对象 第二步:通过反射动态执行类中所有属性的setter方法,完成赋值获取sql语句Stringsqlelement。getText();获取参数类型StringparameterTypeelement。attributeValue(parameterType);创建pstmtPreparedStatementpstmtcreatePstmt(sql,parameterType,conn,args);ResultSetrspstmt。executeQuery();if(rs。next()){读取返回数据类型StringresultTypeelement。attributeValue(resultType);反射创建对象ClassclazzClass。forName(resultType);objclazz。newInstance();获取ResultSet数据ResultSetMetaDatarsmdrs。getMetaData();遍历实体类属性集合,依次将结果集中的值赋给属性Field〔〕fieldsclazz。getDeclaredFields();for(inti0;ifields。length;i){ObjectvaluesetFieldValueByResultSet(fields〔i〕,rsmd,rs);通过属性名找到对应的setter方法Stringnamefields〔i〕。getName();namename。substring(0,1)。toUpperCase()name。substring(1);StringMethodNamesetname;MethodmethodObjclazz。getMethod(MethodName,fields〔i〕。getType());调用setter方法完成赋值methodObj。invoke(obj,value);}} 代码的实现大致思路如上所述,具体实现起来有很多细节需要处理5工具类 上述操作使用的两个工具类完整代码如下 ParseXMLpublicclassParseXML{读取数据源配置信息publicstaticMapString,StringgetProperties(){MapString,StringmapnewHashMapString,String();SAXReaderreadernewSAXReader();try{Documentdocumentreader。read(srcconfig。xml);获取根节点Elementrootdocument。getRootElement();Iteratoriterroot。elementIterator();while(iter。hasNext()){Elemente(Element)iter。next();解析environments节点if(environments。equals(e。getName())){Iteratoriter2e。elementIterator();while(iter2。hasNext()){解析environment节点Elemente2(Element)iter2。next();Iteratoriter3e2。elementIterator();while(iter3。hasNext()){Elemente3(Element)iter3。next();解析dataSource节点if(dataSource。equals(e3。getName())){if(POOLED。equals(e3。attributeValue(type))){Iteratoriter4e3。elementIterator();获取数据库连接信息while(iter4。hasNext()){Elemente4(Element)iter4。next();map。put(e4。attributeValue(name),e4。attributeValue(value));}}}}}}}}catch(Exceptione){TODOAutogeneratedcatchblocke。printStackTrace();}returnmap;}根据接口查找对应的mapper。xmlpublicstaticStringgetMapperXML(StringclassName){保存xml路径Stringxml;SAXReaderreadernewSAXReader();Documentdocument;try{documentreader。read(srcconfig。xml);Elementrootdocument。getRootElement();Iteratoriterroot。elementIterator();while(iter。hasNext()){ElementmappersElement(Element)iter。next();if(mappers。equals(mappersElement。getName())){Iteratoriter2mappersElement。elementIterator();while(iter2。hasNext()){ElementmapperElement(Element)iter2。next();com。cehnjie。dao。StudentDAO。替换classNameclassName。replace(。,);获取接口结尾名StringclassNameEndclassName。split()〔className。split()。length1〕;StringresourceNamemapperElement。attributeValue(resource);获取resource结尾名StringresourceName2resourceName。split()〔resourceName。split()。length1〕;StudentDAO。xml。替换resourceName2resourceName2。replace(。,);StringresourceNameEndresourceName2。split()〔0〕;if(classNameEnd。equals(resourceNameEnd)){xmlsrcresourceName;}}}}}catch(DocumentExceptione){TODOAutogeneratedcatchblocke。printStackTrace();}returnxml;}} MyInvocationHandlerpublicclassMyInvocationHandlerimplementsInvocationHandler{privateStringclassName;publicObjectgetInstance(Classcls){保存接口类型classNamecls。getName();ObjectnewProxyInstanceProxy。newProxyInstance(cls。getClassLoader(),newClass〔〕{cls},this);return(Object)newProxyInstance;}publicObjectinvoke(Objectproxy,Methodmethod,Object〔〕args)throwsThrowable{SAXReaderreadernewSAXReader();返回结果Objectobjnull;try{获取对应的mapper。xmlStringxmlParseXML。getMapperXML(className);Documentdocumentreader。read(xml);Elementrootdocument。getRootElement();Iteratoriterroot。elementIterator();while(iter。hasNext()){Elementelement(Element)iter。next();Stringidelement。attributeValue(id);if(method。getName()。equals(id)){获取信息,创建数据源对象MapString,StringmapParseXML。getProperties();ComboPooledDataSourcedatasourcenewComboPooledDataSource();datasource。setDriverClass(map。get(driver));datasource。setJdbcUrl(map。get(url));datasource。setUser(map。get(username));datasource。setPassword(map。get(password));datasource。setInitialPoolSize(20);datasource。setMaxPoolSize(40);datasource。setMinPoolSize(2);datasource。setAcquireIncrement(5);Connectionconndatasource。getConnection();获取sql语句Stringsqlelement。getText();获取参数类型StringparameterTypeelement。attributeValue(parameterType);创建pstmtPreparedStatementpstmtcreatePstmt(sql,parameterType,conn,args);ResultSetrspstmt。executeQuery();if(rs。next()){读取返回数据类型StringresultTypeelement。attributeValue(resultType);反射创建对象ClassclazzClass。forName(resultType);objclazz。newInstance();获取ResultSet数据ResultSetMetaDatarsmdrs。getMetaData();遍历实体类属性集合,依次将结果集中的值赋给属性Field〔〕fieldsclazz。getDeclaredFields();for(inti0;ifields。length;i){ObjectvaluesetFieldValueByResultSet(fields〔i〕,rsmd,rs);通过属性名找到对应的setter方法Stringnamefields〔i〕。getName();namename。substring(0,1)。toUpperCase()name。substring(1);StringMethodNamesetname;MethodmethodObjclazz。getMethod(MethodName,fields〔i〕。getType());调用setter方法完成赋值methodObj。invoke(obj,value);}}conn。close();}}}catch(Exceptione){TODOAutogeneratedcatchblocke。printStackTrace();}returnobj;}根据条件创建pstmtparamsqlparamparameterTypeparamconnparamargsreturnthrowsExceptionpublicPreparedStatementcreatePstmt(Stringsql,StringparameterType,Connectionconn,Object〔〕args)throwsException{PreparedStatementpstmtnull;try{switch(parameterType){caseint:intstartsql。indexOf({);intendsql。indexOf(});获取参数占位符{name}Stringtargetsql。substring(start,end1);将参数占位符替换为?sqlsql。replace(target,?);pstmtconn。prepareStatement(sql);intnumInteger。parseInt(args〔0〕。toString());pstmt。setInt(1,num);break;casejava。lang。String:intstart2sql。indexOf({);intend2sql。indexOf(});Stringtarget2sql。substring(start2,end21);sqlsql。replace(target2,?);pstmtconn。prepareStatement(sql);Stringstrargs〔0〕。toString();pstmt。setString(1,str);break;default:ClassclazzClass。forName(parameterType);Objectobjargs〔0〕;booleanflagtrue;存储参数ListObjectvaluesnewArrayListObject();保存带的sqlStringsql2;while(flag){intstart3sql。indexOf({);判断{}是否替换完成if(start30){flagfalse;break;}intend3sql。indexOf(});Stringtarget3sql。substring(start3,end31);获取{}的值如{name}拿到nameStringnamesql。substring(start32,end3);通过反射获取对应的getter方法namename。substring(0,1)。toUpperCase()name。substring(1);StringMethodNamegetname;MethodmethodObjclazz。getMethod(MethodName);调用getter方法完成赋值ObjectvaluemethodObj。invoke(obj);values。add(value);sqlsql。replace(target3,?);sql2sql。replace(?,);}截取sql2,替换参数String〔〕sqlssql2。split();pstmtconn。prepareStatement(sql);for(inti0;isqls。length1;i){Objectvaluevalues。get(i);if(java。lang。String。equals(value。getClass()。getName())){pstmt。setString(i1,(String)value);}if(java。lang。Integer。equals(value。getClass()。getName())){pstmt。setInt(i1,(Integer)value);}}break;}}catch(SQLExceptione){TODOAutogeneratedcatchblocke。printStackTrace();}returnpstmt;}根据将结果集中的值赋给对应的属性paramfieldparamrsmdparamrsreturnpublicObjectsetFieldValueByResultSet(Fieldfield,ResultSetMetaDatarsmd,ResultSetrs){Objectresultnull;try{intcountrsmd。getColumnCount();for(inti1;icount;i){if(field。getName()。equals(rsmd。getColumnName(i))){Stringtypefield。getType()。getName();switch(type){caseint:resultrs。getInt(field。getName());break;casejava。lang。String:resultrs。getString(field。getName());break;default:break;}}}}catch(SQLExceptione){TODOAutogeneratedcatchblocke。printStackTrace();}returnresult;}}6总结 MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,而不需要花费精力去处理。多学习研究优秀框架的实现思路,对提升自己的编码能力大有裨益。