自定义mybatis
架构分析
Configuration类:
得到数据源对象
加载其它的实体类映射文件: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>
pox.xml
自定义mybatis:Mapper封装数据类
分析映射配置:UserMapper.xml
步骤
创建包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; } }
Mapper
自定义mybatis:设计Configuration的基本属性
设计Configuration的基本属性
产生get和set方法
产生toString()方法
分析
sqlMapConfig.xml文件
创建loadSqlMapConfig()方法,它的作用:
解析sqlMapConfig.xml配置文件,给Configuration中的属性赋值
解析UserMapper.xml配置文件,给Mapper中的属性赋值
在构造方法中调用方法: loadSqlMapConfig()
作用:使用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; } }
Configuration
核心组件SqlSession:编写getMapper方法
步骤
得到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; } }
SqlSession
相关文章