模拟Mybatis的实现方法
所需要用到的其他工具或技术:
项目管理工具 : maven
测试运行工具 : junit
数据库 : derby
xml操作工具:dom4j
继续不废话
maven dependencies:
<dependency> <groupid>junit</groupid> <artifactid>junit</artifactid> <version>4.9</version> <scope>test</scope> </dependency> <dependency> <groupid>org.apache.derby</groupid> <artifactid>derby</artifactid> <version>10.10.2.0</version> </dependency> <dependency> <groupid>org.apache.derby</groupid> <artifactid>derbyclient</artifactid> <version>10.10.2.0</version> </dependency> <dependency> <groupid>dom4j</groupid> <artifactid>dom4j</artifactid> <version>1.6.1</version> </dependency>
sql 建表及数据插入(如果在第一节中作过,可以跳过此步):
create table user_test_tb( id int primary key, username varchar(20) not null, password varchar(20) not null, nickname varchar(20) not null ); insert into user_test_tb values(1,'1st','111','jack'); insert into user_test_tb values(2,'2nd','222','rose'); insert into user_test_tb values(3,'3rd','333','will');
mybatis配置文件 src/main/resource源目录下
test-mybatis-configuration.xml <?xml version="1.0" encoding="utf-8" ?> <configuration> <properties> <property name="driver" value="org.apache.derby.jdbc.clientdriver" /> <property name="url" value="jdbc:derby://localhost:1527/bjpowernode;create=true" /> </properties> <environments default="development"> <environment id="development"> <transactionmanager type="jdbc" /> <datasource type="pooled"> <property name="driver" value="${driver}" /> <property name="url" value="${url}" /> </datasource> </environment> </environments> <mappers> <mapper class="com.bjpowernode.practice.annotation.usermapper" /> <mapper resource="com/bjpowernode/practice/xml/usermapper.xml" /> </mappers> </configuration>
user.java对象类(src/main/java/com/bjpowernode/practice目录下)
package com.bjpowernode.practice; /** * * user model * */ public class user { private string id; private string username; private string password; private string nickname; public string getid() { return id; } public void setid(string id) { this.id = id; } public string getusername() { return username; } public void setusername(string username) { this.username = username; } public string getpassword() { return password; } public void setpassword(string password) { this.password = password; } public string getnickname() { return nickname; } public void setnickname(string nickname) { this.nickname = nickname; } }
select.java 注解类(src/main/java/com/bjpowernode/practice/annotation目录下)
package com.bjpowernode.practice.annotation; import java.lang.annotation.elementtype; import java.lang.annotation.retention; import java.lang.annotation.retentionpolicy; import java.lang.annotation.target; /** 标注此注解只能用在方法上 */ @target(elementtype.method) /** 标注此注解生命周期是在runtime运行时 */ @retention(retentionpolicy.runtime) public @interface select { string value(); }
usermapper.java 基于annotation的配置类(src/main/java/com/bjpowernode/practice/annotation目录下)
package com.bjpowernode.practice.annotation; import com.bjpowernode.practice.user; import java.util.list; public interface usermapper { @select("select * from user_test_tb") public list<user> getuser(); }
mapper.java 对象类(src/main/java/com/bjpowernode/practice/simulation目录下)
package com.bjpowernode.practice.simulation; /** * * 存储查询结果对象 * */ public class mapper { /** * 返回类型 */ private string resulttype; /** * 查询sql */ private string querysql; public string getresulttype() { return resulttype; } public void setresulttype(string resulttype) { this.resulttype = resulttype; } public string getquerysql() { return querysql; } public void setquerysql(string querysql) { this.querysql = querysql; } }
sqlselectproxy.java aop动态代理类(src/main/java/com/bjpowernode/practice/simulation目录下)
package com.bjpowernode.practice.simulation; import com.bjpowernode.practice.annotation.select; import java.lang.reflect.invocationhandler; import java.lang.reflect.method; import java.sql.preparedstatement; import java.sql.resultset; import java.util.list; public class sqlselectproxy implements invocationhandler { @override public object invoke(object proxy, method method, object[] args) throws throwable { /** * 获得mapper方法上的select注解,以此来取得注解中的sql语句 */ select select = method.getannotation(select.class); if (!method.isannotationpresent(select.class)) { throw new runtimeexception("缺少@select注解!"); } preparedstatement pstmt = null; resultset rs = null; object obj = null; try { pstmt = sqlsessionimpl.connection.preparestatement(select.value()); rs = pstmt.executequery(); /** * 获得method的返回对象类型,此处应当作判断处理,当list的时候,当只返回一个对象的时候. * 为了简单实现功能并与第一节中测试文件不发生冲突起见,此处当作list处理 */ string returntype = method.getgenericreturntype().tostring();//java.util.list<com.bjpowernode.practice.user> if (returntype.startswith(list.class.getname())) { //去掉我们不需要的字符串,得到list中的类型 returntype = returntype.replace(list.class.getname(), "").replace("<", "").replace(">", ""); } else { // 返回其他对象应当作其他处理,此处为了简单起见,暂不处理 } obj = sqlsessionimpl.executequery(rs, returntype); } finally { if (rs != null && !rs.isclosed()) { rs.close(); } if (pstmt != null && !pstmt.isclosed()) { pstmt.close(); } } return obj; } }
sqlsession.java mybatis模拟接口(src/main/java/com/bjpowernode/practice/simulation目录下)
package com.bjpowernode.practice.simulation; import java.util.list; /** * * 模拟sqlsession * */ public interface sqlsession { public <t> t getmapper(class<t> clazz); public <e> list<e> selectlist(string query) throws exception; }
sqlsessionfactory.java mybatis模拟类(src/main/java/com/bjpowernode/practice/simulation目录下)
package com.bjpowernode.practice.simulation; import java.io.ioexception; import java.io.inputstream; import java.util.hashmap; import java.util.list; import java.util.map; import org.dom4j.document; import org.dom4j.documentexception; import org.dom4j.element; import org.dom4j.io.saxreader; /** * * 模拟sqlsessionfactory * */ public class sqlsessionfactory { private inputstream configuration; public sqlsession opensession() throws ioexception { sqlsessionimpl session = new sqlsessionimpl(); loadconfigurations(session); return session; } /** * * 通过dom4j读取配置文件信息 * * @param session * @throws ioexception */ private void loadconfigurations(final sqlsessionimpl session) throws ioexception { try { document document = new saxreader().read(configuration); element root = document.getrootelement(); list<element> mappers = root.element("mappers").elements("mapper"); for (element mapper : mappers) { if (mapper.attribute("resource") != null) { session.setxmlsqls(loadxmlconfiguration(mapper.attribute("resource").gettext())); } if (mapper.attribute("class") != null) { } } } catch (exception e) { system.out.println("读取配置文件错误!"); } finally { configuration.close(); } } /** * * 通过dom4j读取mapper.xml中的信息 * * @param resource * @return * @throws documentexception * @throws ioexception */ private map<string, mapper> loadxmlconfiguration(string resource) throws documentexception, ioexception { map<string, mapper> map = new hashmap<string, mapper>(); inputstream is = null; try { is = this.getclass().getclassloader().getresourceasstream(resource); document document = new saxreader().read(is); element root = document.getrootelement(); if (root.getname().equalsignorecase("mapper")) { string namespace = root.attribute("namespace").gettext(); for (element select : (list<element>) root.elements("select")) { mapper mappermodel = new mapper(); mappermodel.setresulttype(select.attribute("resulttype").gettext()); mappermodel.setquerysql(select.gettext().trim()); map.put(namespace + "." + select.attribute("id").gettext(), mappermodel); } } } finally { is.close(); } return map; } public inputstream getconfiguration() { return configuration; } public void setconfiguration(inputstream configuration) { this.configuration = configuration; } }
sqlsessionfactorybuilder.java mybatis模拟类(src/main/java/com/bjpowernode/practice/simulation目录下)
package com.bjpowernode.practice.simulation; import java.io.inputstream; /** * * 模拟sqlsessionfactorybuilder * */ public class sqlsessionfactorybuilder { public sqlsessionfactory build(inputstream is) { sqlsessionfactory sessionfactory = new sqlsessionfactory(); sessionfactory.setconfiguration(is); return sessionfactory; } }
sqlsessionimpl.java mybatis模拟类(src/main/java/com/bjpowernode/practice/simulation目录下)
package com.bjpowernode.practice.simulation; import java.lang.reflect.method; import java.lang.reflect.proxy; import java.sql.connection; import java.sql.drivermanager; import java.sql.preparedstatement; import java.sql.resultset; import java.util.arraylist; import java.util.list; import java.util.map; /** * * 模拟sqlsessionimpl * */ public class sqlsessionimpl implements sqlsession { /** db connection */ public static connection connection; private map<string, mapper> xmlsqls; private list<string> annotationclasses; public sqlsessionimpl() { /** * driverstring 和 connstring 应该是从配置文件读取,这里简化了 */ final string driverstring = "org.apache.derby.jdbc.clientdriver"; final string connstring = "jdbc:derby://localhost:1527/bjpowernode;create=true"; try { class.forname(driverstring); /** 获得db连接 */ connection = drivermanager.getconnection(connstring); } catch (exception e) { system.out.println("获取dbconnection出错!"); } } /** * 基于annotation的数据库操作 * */ @override public <t> t getmapper(class<t> clazz) { t clazzimpl = (t) proxy.newproxyinstance(this.getclass().getclassloader(), new class[] {clazz}, new sqlselectproxy()); return clazzimpl; } /** * * 基于xml的查询操作 */ @override public <e> list<e> selectlist(string query) throws exception { preparedstatement pstmt = null; resultset rs = null; try { /** 简单的preparedstateme jdbc实现 */ pstmt = connection.preparestatement(xmlsqls.get(query).getquerysql()); rs = pstmt.executequery(); /** 执行查询操作 */ return executequery(rs, xmlsqls.get(query).getresulttype()); } finally { if (!rs.isclosed()) { rs.close(); } if (!pstmt.isclosed()) { pstmt.close(); } } } /** * * 执行查询操作,并将查询到的结果与配置中的resulttype根据变量名一一对应,通过反射调用set方法注入各个变量的值 * * @param rs * @param type * @return * @throws exception */ public static <e> list<e> executequery(resultset rs, string type) throws exception { int count = rs.getmetadata().getcolumncount(); list<string> columnnames = new arraylist<string>(); for (int i = 1; i <= count; i++) { columnnames.add(rs.getmetadata().getcolumnname(i)); } final list list = new arraylist<object>(); while (rs.next()) { class modelclazz = class.forname(type); object obj = modelclazz.newinstance(); for (method setmethods : modelclazz.getmethods()) { for (string columnname : columnnames) { if (setmethods.getname().equalsignorecase("set" + columnname)) { setmethods.invoke(obj, rs.getstring(columnname)); } } } list.add(obj); } return list; } public map<string, mapper> getxmlsqls() { return xmlsqls; } public void setxmlsqls(map<string, mapper> xmlsqls) { this.xmlsqls = xmlsqls; } public list<string> getannotationclasses() { return annotationclasses; } public void setannotationclasses(list<string> annotationclasses) { this.annotationclasses = annotationclasses; } }
usermapper.xml 基于xml的mapper配置文件(src/main/java/com/bjpowernode/practice/xml目录下)
<?xml version="1.0" encoding="utf-8" ?> <!-- namespace 当基于xml进行配置的时候是根据namespace+id来拼接进行sql操作 --> <mapper namespace="com.bjpowernode.practice.usermapper"> <!-- select 查询 --> <select id="getuser" resulttype="com.bjpowernode.practice.user"> select * from user_test_tb </select> </mapper>
testmybatis.java 测试类(src/test/java/com/bjpowernode/practice目录下)
package com.bjpowernode.practice; import com.bjpowernode.practice.annotation.usermapper; import com.bjpowernode.practice.simulation.sqlsession; import com.bjpowernode.practice.simulation.sqlsessionfactory; import com.bjpowernode.practice.simulation.sqlsessionfactorybuilder; import com.bjpowernode.practice.simulation.sqlsessionimpl; import java.io.inputstream; import java.sql.sqlexception; import java.text.messageformat; import java.util.list; import org.junit.after; import org.junit.before; import org.junit.test; public class testmybatis { /** 配置置文件 */ private string source; private inputstream inputstream; private sqlsessionfactory sqlsessionfactory; @before public void setup() { source = "test-mybatis-configuration.xml"; } /** * * 基于xml格式配置的测试方法 * */ @test public void testxmlconfingure() { try { /** * 获得session */ inputstream = testmybatis.class.getclassloader().getresourceasstream(source); sqlsessionfactory = new sqlsessionfactorybuilder().build(inputstream); sqlsession session = sqlsessionfactory.opensession(); /** * 执行query操作 */ list<user> users = (list) session.selectlist("com.bjpowernode.practice.usermapper.getuser"); system.out.println("query by xml configuration..."); /** * 打印结果 */ this.printusers(users); } catch (exception e) { e.printstacktrace(); } } /** * * 基于annotation配置的测试方法 * */ @test public void testannotationconfingure() { try { inputstream = testmybatis.class.getclassloader().getresourceasstream(source); sqlsessionfactory = new sqlsessionfactorybuilder().build(inputstream); sqlsession session = sqlsessionfactory.opensession(); usermapper usermapper = session.getmapper(usermapper.class); system.out.println("\r\nquery by annotation configuration..."); this.printusers(usermapper.getuser()); } catch (exception e) { e.printstacktrace(); } } @after public void clearup() throws sqlexception { if (sqlsessionimpl.connection != null && !sqlsessionimpl.connection.isclosed()) { sqlsessionimpl.connection.close(); } } private void printusers(final list<user> users) { int count = 0; for (user user : users) { system.out.println(messageformat.format("==user[{0}]=================", ++count)); system.out.println("user id: " + user.getid()); system.out.println("user username: " + user.getusername()); system.out.println("user password: " + user.getpassword()); system.out.println("user nickname: " + user.getnickname()); } } }
以上就是基于xml以及annotation的方式对mybatis实现了一个简单的模拟。旨在理解mybatis的工作原理。
笔者一直觉得当学习一个工具类技术的时候,路线应该是
1.实现一个小例子
2.找材料理解其中原理
3.学习技术细节,并动手全部实现
4.在全部学完之后动手做一个小项目,尽可能的使用这个在技术中的各个环节。
总结
以上所述是小编给大家介绍的模拟mybatis的实现方法,希望对大家有所帮助