我们已经通过案例体验到了mybatis的魅力。现在来梳理一下MyBatis运行时的几个对象,我们需要搞清楚他们的作用,进而需要理解mybatis的整个工作流程和执行原理。
-
Resources
加载配置文件,有一种是使用类加载进行加载,我们通过这个类的类加载器进行资源的加载。
-
SqlSessionFactoryBuilder
构建SqlSessionFactory工厂对象需要的对象。采用了构建者模式,屏蔽了对象构建的细节。
-
SqlSessionFactory
创建SqlSession对象所用。使用工厂模式创建,目的就是解耦合。
-
SqlSession
创建代理对象,使用了代理模式。
-
Executor
操作数据库
-
MappedStatement
存储SQL语句、参数、输出结果类型
-
## 3.2.pom.xml
```xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.by</groupId> <artifactId>02_mybatis_DIY</artifactId> <version>1.0-SNAPSHOT</version> <properties> <!-- 项目源码及编译输出的编码 --> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <!-- 项目编译JDK版本 --> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- 解析xml的dom4j --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <!-- dom4j的依赖包jaxen --> <dependency> <groupId>jaxen</groupId> <artifactId>jaxen</artifactId> <version>1.1.6</version> </dependency> </dependencies> <build> <!-- 如果不添加此节点src/main/java目录下的所有配置文件都会被漏掉。 --> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources> </build> </project>
## 3.3.utils
我们需要三个工具类:
- XMLConfigBuilder:解析XMl配置文件。
- DataSourceUtil:获取数据库连接对象。
- Executor:执行SQL,封装我们想要的数据。参考:资料/utils
## 3.4.Resources
l编写资源加载类。使用类加载器加载配置文件```java
package com.by.io; import java.io.InputStream; public class Resources { //根据文件名称,加载类路径下面的配置文件 public static InputStream getResourceAsStream(String filePath){ return Resources.class.getClassLoader().getResourceAsStream(filePath); } } ``` ## 3.5.SqlSessionFactoryBuilder 将配置资源封装成Configuration对象,并且将该资源对象传到工厂对象中 ```java package com.by.builder; import com.by.cfg.Configuration; import com.by.factory.DefaultSqlSessionFactory; import com.by.factory.SqlSessionFactory; import com.by.utils.XMLConfigBuilder; import java.io.InputStream; public class SqlSessionFactoryBuilder { /** * 构建SqlSessionFactory对象 * @param in * @return */ public SqlSessionFactory build(InputStream in){ Configuration configuration = XMLConfigBuilder.loadConfiguration(in); return new DefaultSqlSessionFactory(configuration); } }
```## 3.6.Configuration
配置类存储所有的配置信息```java
package com.by.cfg; import com.by.mapping.MappedStatement; import java.util.HashMap; import java.util.Map; public class Configuration { private String driver; private String url; private String username; private String password; //Map<namespace+id> private Map<String, MappedStatement> mappers = new HashMap<String,MappedStatement>(); public Map<String, MappedStatement> getMappers() { return mappers; } public void setMappers(Map<String, MappedStatement> mappers) { this.mappers.putAll(mappers);//此处需要使用追加的方式 } public String getDriver() { return driver; } public void setDriver(String driver) { this.driver = driver; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } 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; } }
```## 3.7.MappedStatement
MappedStatement是用来封装sql语句和查询结果集```java
package com.by.mapping; public class MappedStatement { private String queryString;//SQL private String resultType;//实体类的全限定类名 public String getQueryString() { return queryString; } public void setQueryString(String queryString) { this.queryString = queryString; } public String getResultType() { return resultType; } public void setResultType(String resultType) { this.resultType = resultType; } }
```## 3.8.SqlSessionFactory```java
package com.by.factory; public interface SqlSessionFactory { //获取SQLSession对象 public SqlSession openSession(); } ``` ```java package com.by.factory; import com.by.cfg.Configuration; import com.by.session.DefaultSqlSession; import com.by.session.SqlSession; public class DefaultSqlSessionFactory implements SqlSessionFactory { private Configuration cfg; public DefaultSqlSessionFactory(Configuration cfg) { this.cfg = cfg; } /** * 获取一个SqlSession对象 * @return */ @Override public SqlSession openSession() { return new DefaultSqlSession(cfg); } } ``` ## 3.9.SqlSession ```java public interface SqlSession { //获取代理对象 public <T> T getMapper(Class<T> tClass); //释放资源 void close(); }
``````javapackage com.by.session; import com.by.utils.DataSourceUtil; import com.by.cfg.Configuration; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.SQLException; public class DefaultSqlSession implements SqlSession { private Configuration cfg; private Connection conn; public DefaultSqlSession(Configuration cgf){ this.cfg = cgf; this.conn = DataSourceUtil.getConnection(cfg); } /* * 创建代理对象 */ @Override public <T> T getMapper(Class<T> tClass) { /** * tClass.getClassLoader():类加载器 * new Class[]{tClass}:Class数组,让代理对象和被代理对象有相同的行为 * new ProxyFactory:调用真是角色,附加自己的操作 */ return (T) Proxy.newProxyInstance(tClass.getClassLoader(), new Class[]{tClass}, new ProxyFactory(cfg.getMappers(),conn)); } @Override public void close() { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
```## 3.10.ProxyFactory```java
package com.by.session; import com.by.mapping.MappedStatement; import com.by.utils.Executor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.sql.Connection; import java.util.Map; public class ProxyFactory implements InvocationHandler { private Map<String, MappedStatement> mappers; private Connection conn; public ProxyFactory(Map<String, MappedStatement> mappers, Connection conn){ this.mappers = mappers; this.conn = conn; } //调用代理对象的任何方法,都会在这执行 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //1.获取方法名 String methodName = method.getName(); //findAll //2.获取方法所在类的名称 String className = method.getDeclaringClass().getName();//com.by.mapper.UserMapper //3.组合key String key = className+"."+methodName; //4.获取mappers中的Mapper对象 MappedStatement mappedStatement = mappers.get(key); //5.判断是否有mapper if(mappedStatement == null){ throw new IllegalArgumentException("传入的参数有误"); } //6.调用工具类执行查询所有 return new Executor().selectList(mappedStatement,conn); } }
```## 3.11.测试
```java
//1.读取配置文件 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.创建SqlSessionFactory工厂 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(in); System.out.println("-----" + factory); //3.使用工厂生产SqlSession对象 SqlSession session = factory.openSession(); //4.使用SqlSession创建Dao接口的代理对象 UserDao userDao = session.getMapper(UserDao.class); //5.使用代理对象执行方法 List<User> users = userDao.findAll(); for(User user : users){ System.out.println(user); } //6.释放资源 session.close(); in.close();