MyBatis原理总结(代码实现流程)
我们在实际开发中,越简单越好,所以都是采用不写Dao实现类的方式。不管是使用xml还是直接配置。
但是MyBatis是支持写Dao实现类的
注意sqlSession是这里面的一个灵魂,有很多执行api
目录结构:
方法:
/** * 用户的持久层接口 */ public interface IUserDao { List<User> findAll(); }
实现类:
public class UserDaoImpl implements IUserDao { private SqlSessionFactory factory; //覆盖掉默认构造函数,这样就有了工厂,可以进一步创建对象 public UserDaoImpl(SqlSessionFactory factory){ this.factory = factory; } @Override public List<User> findAll() { //1.使用工厂创建SqlSession对象 SqlSession sqlSession = factory.openSession(); //2.使用sqlSession执行查询所有方法(此处需要的参数:(String statement)从配置文件中获取) namespace + id List<User> userList = sqlSession.selectList("com.toov5.dao.IUserDao.findAll"); //使用完后关闭掉 sqlSession.close(); return userList; } }
实体类:
public class User implements Serializable { private Integer id; private String username; private Date birthday; private String sex; private String address; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", birthday=" + birthday + ", sex='" + sex + '\'' + ", address='" + address + '\'' + '}'; } }
全局配置文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 配置环境 --> <environments default="mysql"> <!-- 配置mysql的环境--> <environment id="mysql"> <!-- 配置事务的类型--> <transactionManager type="JDBC"></transactionManager> <!-- 配置数据源(也叫连接池) --> <dataSource type="POOLED"> <!-- 配置连接数据库的4个基本信息 --> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 --> <mappers> <!--resource目录下 对应要创建相应的包 --> <mapper resource="com/toov5/dao/IUserDao.xml"/> </mappers> </configuration>
映射文件:
<?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.toov5.dao.IUserDao"> <!--查询所有--> <select id="findAll" resultType="com.toov5.entity.User"> select * from user; </select> </mapper>
测试类:
/** * MyBatis测试类 */ public class MyBatisTest { public static void main(String[] args) throws IOException { //1.读取配置文件 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2. 创建SqlSessonFactory工厂 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); //如何解析,如何封装已经底层帮助实现了。细节封装了。 SqlSessionFactory factory = builder.build(in); // SqlSessionFactory 是个接口,需要找实现。这个工厂是用来创建对象的,创建过程省略了 //3. 使用工厂生产SqlSession对象 SqlSession sqlSession = factory.openSession(); //不需要代理类了,SqlSession 是有查询方法的.使用工厂创建Dao对象,传入工厂 IUserDao userDao = new UserDaoImpl(factory); //5.使用代理对象执行方法 List<User> userList = userDao.findAll(); userList.stream().forEach( user ->{ System.out.println(user); } ); //6.释放资源 in.close(); } }
Mybatis在用动态代理dao方式时候,也是找到对应的sql语句的。
执行结果,一模一样。
引申
补充,对于绝对路径和相对路径,绝对路径不准确,采用相对路径。
相对路径有两种方案可以获得。 1. 类加载器,只能读取类路径的配置文件 2. 使用ServletContext对象的getRealPath()
创建工厂,采用了构建这模式。隐藏了创建细节。使用户直接调用方法就可以拿到对象了
使用工厂模式生产SqlSession。降低类间的依赖,解耦合。
创建dao接口实现类使用了代理模式。不修改源码基础上对已有的方法增强。
MyBatis原理分析:
MyBatis在使用代理Dao的方式实现增删改查时候,做的什么事呢?
两件事:
一: 创建对象
二: 在代理对象中调用selectList
从一开始就需要解析配置文件,
进而做如下操作:
1. 根据配置文件的信息创建Connection对象,注册驱动,获取连接
2. 获取预处理对象PreparedStatement,此时需要SQL语句。 com.prepareStatement(sql)
3. 执行查询 ResultSet resultSet = preparedStatement.executeQuery();
4.遍历结果用于封装
List<E> list = new ArrayList();
while(resultSet.next()){
E element = xxx; // 可以通过反射后去 class.forName("配置的全限定类名").newInstance(); 使用反射封装
// 进行封装。把每个rs的内容都添加到element中,把element加入到list中
list.add(element);
}
于是我们就可以把表的列名看成是实体类的属性名称,就可以使用反射的方式来根据名称获取每个属性。
5.返回结果
综上所述: 需要提供方法两个信息
1. 连接信息
2. 映射信息,包含两部分: 执行的sql语句; 封装结果的实体类全限定类名。这两个信息作为属性定义对象。
String key : nameSpace+id
Mapper value : String的sql语句 方法的全类名
通过方法:
session.getMapper(IUserDao.class) 实现了代理对象的创建
根据dao接口的字节码创建到的代理对象
public <T> getMapper(Class<T> daoInterfaceClass){ }
使用的的参数是 : 类加载器(与被代理对象使用相同的类加载器), Class数组:代理对象要实现接口的字节码数组, 如何代理(Handler)
Proxy.newProxyInstance()
此方法的封装:
s1.类加载器: 它使用的和被代理对象是相同的类加载器
s2.代理对象要实现的接口: 和被代理对象实现相同的接口
s3.如何代理: 它就是增强的方法,我们需要自己来提供
此处是一个InvocationHandler的接口,我们需要写一个该接口的实现类
调用该类的selectList方法
自定义MyBatis能通过入门案例看到的类:
class Resources
class SqlSessionFactoryBuilder
interface SqlSessionFactory
此时的pom:
<?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.test</groupId> <artifactId>mybatis</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--<dependency>--> <!--<groupId>org.mybatis</groupId>--> <!--<artifactId>mybatis</artifactId>--> <!--<version>3.4.5</version>--> <!--</dependency>--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.6.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
构建者构建工厂,工厂生产SqlSession。SqlSession可以做的事情很多。
总结:读取配置文件io流,解析出我们要的信息,交给构建者,构建者使用工具类,构建了工厂对象,工厂的openSession方法提供了sqlSession对象,sqlSession去干活。
下图信息量很大: