自定义mybatis
架构分析
-
-
得到数据源对象
-
加载其它的实体类映射文件:usermapper.xml,使用dom4j
-
-
mapper类只是一个实体类:pojo,用来封装数据
-
sqlsession类:
-
生成了usermapper接口的代理对象,jdk代理。
-
访问数据库:jdbc
-
封装查询的结果集,使用反射
-
添加所需的所有依赖,做好准备工作。
pom.xml文件
<properties> <!--dom4j版本--> <dom4j.vesrion>1.6.1</dom4j.vesrion> <!--dom4j依赖包版本--> <jaxen.version>1.1.6</jaxen.version> <!--mysql驱动版本--> <mysql.version>5.1.30</mysql.version> <!--c3p0版本--> <c3p0.version>0.9.2.1</c3p0.version> <!-- junit版本 --> <junit.version>4.12</junit.version> </properties> <dependencies> <!--dom4j依赖--> <dependency> <groupid>dom4j</groupid> <artifactid>dom4j</artifactid> <version>${dom4j.vesrion}</version> </dependency> <dependency> <groupid>jaxen</groupid> <artifactid>jaxen</artifactid> <version>${jaxen.version}</version> </dependency> <!-- mysql数据库依赖 --> <dependency> <groupid>mysql</groupid> <artifactid>mysql-connector-java</artifactid> <version>${mysql.version}</version> </dependency> <!--c3p0依赖--> <dependency> <groupid>com.mchange</groupid> <artifactid>c3p0</artifactid> <version>${c3p0.version}</version> </dependency> <!-- junit依赖 --> <dependency> <groupid>junit</groupid> <artifactid>junit</artifactid> <version>${junit.version}</version> </dependency> </dependencies>
步骤
-
创建包com.it.mybatis
-
创建实体类:mapper包含4个属性:namespace,id,resulttype,sql
-
重写tostring()方法,方便后期测试看到封装的结果
-
生成get和set方法
-
一个mapper对象代表一条要操作的查询语句对象
代码
package com.it.mybatis; /** 用来封装映射文件的实体类 */ public class mapper { private string namespace; //接口类全名 private string id; //接口中方法名 private string resulttype; //封装的数据类型 private string sql; //要执行的sql语句 @override public string tostring() { return "mapper{" + "namespace='" + namespace + '\'' + ", id='" + id + '\'' + ", resulttype='" + resulttype + '\'' + ", sql='" + sql + '\'' + '}'; } public string getnamespace() { return namespace; } public void setnamespace(string namespace) { this.namespace = namespace; } public string getid() { return id; } public void setid(string id) { this.id = id; } public string getresulttype() { return resulttype; } public void setresulttype(string resulttype) { this.resulttype = resulttype; } public string getsql() { return sql; } public void setsql(string sql) { this.sql = sql; } }
-
设计configuration的基本属性
-
产生get和set方法
-
产生tostring()方法
sqlmapconfig.xml文件
-
创建loadsqlmapconfig()方法,它的作用:
-
解析sqlmapconfig.xml配置文件,给configuration中的属性赋值
-
解析usermapper.xml配置文件,给mapper中的属性赋值
-
-
-
作用:使用dom4j解析sqlmapconfig.xml文件,给数据库有关的属性赋值
-
从类路径加载/sqlmapconfig.xml配置文件,创建输入流
-
使用dom4j得到文档对象
-
使用xpath读取所有property元素
-
遍历每个property元素,读取它的name和value属性值
-
判断name的字符串,如果与类中的属性名相同,则赋值到相应属性中
-
4.configuration解析实体类映射文件
解析usermapper.xml并且封装到mapper类中
-
创建新的方法loadmapper(document document),将当前的文档对象传递给方法
-
读取<mapper>中的resource属性值
-
通过resource读取它对应的xml文件
-
得到namespace,id,resulttype,sql的值,封装成mapper对象
-
在loadsqlmapconfig()中调用此方法
-
loadmapper(document document)方法开发步骤
作用:进一步解析其它的xml文件,给mappers属性赋值
-
读取mapper中的resource属性值
-
使用xpath读取所有mapper元素
-
遍历每个mapper元素
-
读取mapper的resource属性值
-
-
通过resource读取它对应的xml文件,得到namespace,id,resulttype,sql的值
-
使用类对象,读取输入流下面resource,注:要加上/
-
创建文档对象
-
读取根元素mapper
-
读取namespace属性
-
读取根元素下的一个select标签
-
得到id,resulttype,sql内容
-
-
封装成mapper对象
-
创建一个自定义的mapper对象,封装上面三个属性
-
再封装namespace属性
-
将封装好的mapper对象添加到this的mappers属性中,其中键是namespace+"."+id,值是自定义的mapper对象。
-
package com.it.mybatis; import com.mchange.v2.c3p0.combopooleddatasource; import org.dom4j.document; import org.dom4j.documentexception; import org.dom4j.element; import org.dom4j.io.saxreader; import javax.sql.datasource; import java.beans.propertyvetoexception; import java.io.inputstream; import java.util.hashmap; import java.util.list; import java.util.map; /** 1. 封装sqlmapconfig.xml配置信息 2. 得到数据源 3. 加载usermapper.xml配置信息 */ public class configuration { //数据源的四个属性 private string username; private string password; private string url; private string driver; //封装其它的映射文件中属性 private map<string, mapper> mappers = new hashmap<>(); private datasource datasource; //数据源 //在构造方法中调用 public configuration() { try { loadsqlmapconfig(); //加载配置文件 createdatasource(); //创建数据源 } catch (exception e) { e.printstacktrace(); } } /* 解析sqlmapconfig.xml文件,封装上面的数据源的四个属性 */ private void loadsqlmapconfig() throws exception { //得到输入流 inputstream inputstream = configuration.class.getresourceasstream("/sqlmapconfig.xml"); //得到文档对象 saxreader saxreader = new saxreader(); document document = saxreader.read(inputstream); //读取property list<element> list = document.selectnodes("//property"); for (element property : list) { //读取name属性 string name = property.attributevalue("name"); //读取value属性 string value = property.attributevalue("value"); //判断是哪个属性 switch (name) { case "username": this.username = value; break; case "password": this.password = value; break; case "driver": this.driver = value; break; case "url": this.url = value; break; } } //读取usermapper.xml文件 loadmapper(document); } /** 解析其它的实体类映射文件 @param document 上面已经得到的文档对象 */ private void loadmapper(document document) throws exception { //读取mapper中resource属性 list<element> list = document.selectnodes("//mapper"); for (element mapperelement : list) { //读取mapper中resource属性 string resource = mapperelement.attributevalue("resource"); //再次读取新的xml文件 inputstream in = configuration.class.getresourceasstream("/" + resource); //创建文档对象 document doc = new saxreader().read(in); //得到根元素 element rootelement = doc.getrootelement(); string namespace = rootelement.attributevalue("namespace"); //得到mapper下select元素 element select = rootelement.element("select"); //得到id属性 string id = select.attributevalue("id"); string resulttype = select.attributevalue("resulttype"); string sql = select.gettexttrim(); //创建mapper对象 mapper mapper = new mapper(); mapper.setid(id); mapper.setnamespace(namespace); mapper.setsql(sql); mapper.setresulttype(resulttype); //键=namespace + "." + "id"; string key = namespace + "." + id; //将创建好的mapper对象加到map集合中 mappers.put(key,mapper); } } /** 创建数据源 */ private void createdatasource() throws propertyvetoexception { //使用c3p0的数据源 combopooleddatasource ds = new combopooleddatasource(); //设置数据库访问属性 ds.setuser(username); ds.setpassword(password); ds.setjdbcurl(url); ds.setdriverclass(driver); this.datasource = ds; } @override public string tostring() { return "configuration{" + "username='" + username + '\'' + ", password='" + password + '\'' + ", url='" + url + '\'' + ", driver='" + driver + '\'' + ", mappers=" + mappers + ", datasource=" + datasource + '}'; } 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 geturl() { return url; } public void seturl(string url) { this.url = url; } public string getdriver() { return driver; } public void setdriver(string driver) { this.driver = driver; } public map<string, mapper> getmappers() { return mappers; } public void setmappers(map<string, mapper> mappers) { this.mappers = mappers; } public datasource getdatasource() { return datasource; } public void setdatasource(datasource datasource) { this.datasource = datasource; } }
步骤
得到sql语句和返回类型
-
得到configuration中map集合
-
实例化configuration对象
-
通过configuration得到mapper对象的集合
-
-
得到map中的键:类全名.方法名
-
通过方法对象->得到声明的接口->得到名称:即类全名 com.it.dao.usermapper
-
获取当前执行的方法名称:findallusers
-
通过类全名+方法名得到键
-
-
得到mapper中相应的属性
-
通过类全名+"."+方法名,从mappers中得到映射的mapper对象
-
从mapper中获取查询的sql语句
-
从mapper中获取返回值类型resulttype
-
通过反射将上面的resulttype字符串转成类对象,供后面的方法使用
-
得到connection对象访问数据库
-
通过configuration得到数据源,通过数据源得到连接对象
-
调用list queryforlist(connection connection, string sql, class clazz)方法
-
参数:连接对象,sql语句,结果集的类型。 直接创建一个list集合,添加3个user对象到集合中,暂时不访问数据库。
- 返回封装好的集合
-
package com.it.mybatis; import com.itheima.entity.user; import javax.sql.datasource; import java.lang.reflect.field; import java.lang.reflect.invocationhandler; import java.lang.reflect.method; import java.lang.reflect.proxy; import java.sql.*; import java.util.arraylist; import java.util.list; import java.util.map; /** 核心类 1. 生成了usermapper接口的代理对象,jdk代理。 2. 访问数据库:jdbc 3. 封装查询的结果集,使用反射。 */ public class sqlsession { public <t> t getmapper(class<t> clazz) { /* 参数1:类加载器 参数2:接口数组 参数3:每个方法调用一次 */ return (t) proxy.newproxyinstance( sqlsession.class.getclassloader(), new class[]{clazz}, new invocationhandler() { @override public object invoke(object proxy, method method, object[] args) throws throwable { //1. 通过键得到mapper对象 //创建configuration对象 configuration configuration = new configuration(); //得到集合 map<string, mapper> mappers = configuration.getmappers(); string id = method.getname(); //方法名 //getdeclaringclass得到method所在类全名 string namespace = method.getdeclaringclass().getname(); //如何得到键 string key = namespace + "." + id; //通过键得到值 mapper mapper = mappers.get(key); //2. 从mapper对象中得到sql语句执行,并且封装成对象返回 string sql = mapper.getsql(); //resulttype = com.itheima.entity.user string resulttype = mapper.getresulttype(); //将字符串转成class class type = class.forname(resulttype); //3.查询数据库,必须要连接对象 //连接对象从数据源中得到 datasource datasource = configuration.getdatasource(); connection connection = datasource.getconnection(); //使用jdbc访问数据库得到封装好的结果 list list = queryforlist(connection, sql, type); return list; } }); } /** 通过jdbc来访问数据库 @param connection 连接对象 @param sql 语句 @param type 返回类型 @return */ private list queryforlist(connection connection, string sql, class type) throws exception { list users = new arraylist<>(); //1. 通过connection创建语句对象:preparedstatement preparedstatement ps = connection.preparestatement(sql); //2. 通过语句对象执行sql语句 resultset rs = ps.executequery(); //3. 执行完毕以后得到结果集resultset while(rs.next()) { //4. 将resultset进行遍历,封装成实体类对象,添加到集合中 //创建对象 object user = type.getconstructor().newinstance(); //得到类中所有的属性 field[] fields = type.getdeclaredfields(); for (field field : fields) { //得到属性名 string name = field.getname(); //得到相应的值 object value = rs.getobject(name); //暴力反射 field.setaccessible(true); //给每个属性赋值 field.set(user, value); } users.add(user); } //5.释放资源 rs.close(); ps.close(); connection.close(); return users; } }
下一篇: c++ 【递归算法】梵塔问题