Mybatis接口式编程的原理
mybatis 有两种实现方式
其一:通过xml配置文件实现
其二:面向接口编程的实现
前者原理在mybatis运行套路里面大致说了一下,此节说的是后者,面向接口的编程,可以解决掉 namespace / 传入参数 / 返回值 / 与sql关联id 等四处风险。
意思就是,mybatis配置文件dao.xml找了一个接口作为自己的代言人,并告诉其他的java对象,以后访问数据库不要再骚扰我这个dao.xml文件了,你去找我的代言人助理它会全权负责的。
如果接口助理要全权负责dao.xml文件的所有工作,那么,dao.xml文件肯定要和代言接口交接清楚工作任务,不能然接口乱搞。
仍旧以访问数据库信息列表为例:
首先要定义一个接口imessage和dao.xml文件里面的各种配置项一一对应:
package hdu.terence.dao; import java.util.list; import hdu.terence.bean.message; public interface imessage { publiclist<message> querymessagelist(message message); }
dao.xml文件配置:
<?xml version="1.0"encoding="utf-8"?> <!doctype mapper public "-//mybatis.org//dtd mapper 3.0//en" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.terence.dao.imessage"> <resultmap type="hdu.terence.bean.message" id="messageresult"> <id column="id" jdbctype="integer"property="id"/> <!--主键标签--> <result column="command" jdbctype="varchar"property="command"/> <result column="description" jdbctype="varchar" property="description"/> <result column="content" jdbctype="varchar"property="content"/> </resultmap> <select id="querymessagelist" parametertype="hdu.terence.bean.message" resultmap="messageresult"> selectid,command,description,content from message where 1=1 <if test="command!=null and!"".equals(command.trim())"> andcommand=#{command} </if> <if test="description!=null and!"".equals(description.trim())"> anddescription like '%' #{description} '%' </if> </select><span style="color: teal; font-family: arial, helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
测试:
public list<message> querymessagelistbymybatis(stringcommand,string description) { dbaccess dbaccess =new dbaccess(); sqlsession sqlsession=null; list<message>messagelist=new arraylist<message>(); try { sqlsession=dbaccess.getsqlsession(); message message=new message(); message.setcommand(command); message.setdescription(description); //方式二:通过接口调用配置文件里面的sql语句 imessageimessage=sqlsession.getmapper(imessage.class); messagelist=imessage.querymessagelist(message); } catch (ioexception e) { e.printstacktrace(); } finally{ if(sqlsession!=null) { sqlsession.close(); //要关闭数据库会话 } } return messagelist; }
第一,dao.xml文件命名空间要和接口的全名保持一致:写接口的全名(包括报名com.terence.dao.imessage)方便接口找到配置文件的命名空间。
第二,接口定义代言的sql语句对应的方法:querymessagelist(),方法名要和代言的sql语句配置项的id:querymessagelist相同,方便接口根据自己声明的方法映射到对应的配置项id。
第三,接口声明的方法带入的形参message要和dao.xml文件对应配置项需要的参数保持一致。
第四,接口声明的方法的返回值类型list<message>要和dao.xml文件id配置项resultmap类型一致。
这样就完成了接口的代理工作,配置文件会告诉其他的java代码,以后通过这个接口就可以完成我本来要完成的工作,执行sql语句对数据库完成交互工作;很明显,这种接口式编程比以前的直接调用配置文件方便多了,以前直接调用配置文件,每次使用配置文件,都需要写配置文件的命名空间、id、参数和返回值,这些地方有时候会手滑写不一致,如果出错,编译器不会出现提示,开发者只能根据执行结果错误提示推敲错误的地方慢慢调试。如果使用接口式编程,通过将配置文件dao.xml和定义的接口一一匹配对应,通过接口代理配置文件,以后不管谁使用都可以直接调用接口下,不用管配置文件里面的命名空间和sql配置id,调用接口时如果出错,会自动提示,更有利于错误的查找。
但是,如果仅仅在mybatis中使用接口式编程,并没有什么显著的效果,但是当mybatis遇到spring的时候,效果就显著了。
当mybatis遇到spring,mybatis的核心配置文件configuration.xml中连接数据库的配置,就会取代了spring中的db层,mybatis中的sqlsession会话将会托管给spring,上述的messagedao.xml部分带入参数调用接口的部分都会托管给spring的service来完成。此时我们定义的imessage接口将会替代原来的dao层,此时的dao层只剩下接口文件和javadao.xml配置文件.
mybatis接口式编程的原理
第一个问题,明天为什么接口imssage.querymessagelist()没有实现类,但是却可以调用对应的方法?
首先要有一个创建代理实例的类,类里面有个方法invoke();
mapperproxy implements invocationhandler { mapperproxy.invoke(); }
当我们调用接口的时候,走的是invoke()方法,会通过proxy.newproxyinstance()加载一个代理实例,实际上也就是通过sqlsession.getmapper()来获取代理实例,即
sqlsession.getmapper()==proxy.newproxyinstance(); imessage imessage=sqlsession.getmapper(imessage.class); messagelist=imessage.querymessagelist(message);
这样,即使imessage自身没有实现类,但是通过sqlsession的getmapper方法带入接口类imessage.class,就可以获取一个imessage类型的代理实例,很明显,这里是泛型在起作用,带入什么样的类型,就得到一个什么类型的接口,原因是mybatis已经利用泛型做了强转了;
第二个问题,既然是通过invoke()方法,它是怎么知道我们要调用sqlsession.selectlist()方法?
因为刚初始化sqlsession的时候,加载了configuration.xml文件,并在改文件中加载了各个javadao.xml文件,而这个configuration.xml文件对应了mybatis中相关的类:configuration,接口的全名称在invoke()方法里面都可以拿到, 接口全名称.方法名==namespace.id,所以可以拿到配置文件中的查询方法。
sqlsession的获取
public sqlsession getsqlsession() throws ioexception { //1、通过配置文件获取数据库连接相关信息 readerreader=resources.getresourceasreader("hdu/terence/config/configuration.xml"); //2、通过配置信息构建sqlsessionfactory sqlsessionfactoryssf=newsqlsessionfactorybuilder().build(reader); //3、通过sqlsessionfactory打开数据库会话 sqlsessionsqlsession=ssf.opensession(); return sqlsession; }
sqlsession通过上述配置实现,首先通过resources.getresourceasreader(“配置文件路径”)方法加载配置文件包装一个reader对象,然后通过sqlsessionfactory这个接口带入reader对象,获取一个动态代理实例,即sqlsessionfactory会话工厂,通过会话工厂得到一个会话sqlsession().
其中,在获取会话工厂获取实例的时候,底层实现源码是将带入的参数read作为key,找到map中对应的value值,即mapperproxyfactory。
以上所述是小编给大家介绍的mybatis接口式编程的原理,希望对大家有所帮助
上一篇: java 中堆内存和栈内存理解