自定义Mybatis的全过程

思路:1.通过xml文件记录信息,
2.通过Resources类读取xml文件,
3.通过构建者(SqlSessionFactoryBuilder)调用XmlConifgBuilder解析xml文件,将获取的信息封装在Configuration中和Mapper中(Configuration中包含存储Mapper的容器mappers),然后创建工厂的实体类(DefauSqlSessionFactory),
4.工厂传递Configuration给创建的SqlSession的实体类(DefaSqlSesison)
5.SqlSession实体类通过方法getMapper(参数:dao接口类的字节码),返回匿名内部类,该匿名内部类包括MapperPrxoy由于该代理类需要Configuraiton中的mappers和connection,于是SqlSession实体类创建了Connection对象,并通过工具类DatasourceUtil获取连接,返回给代理类
6.MapperProxy是dao接口类的代理类,于是通过当前执行的方法的方法名,和该方法所在的类名,连接成key,从mappers中获取对应的Mapper,并用Mapper中的sql语句和返回值类型,调用Executor类的方法,实现查询。

以下先从环境入手:

  1. maven部分:
 < dependencies >
  < dependency >
    < groupId >log4j< /groupId>
    < artifactId >log4j< /artifactId >
    < version>1.2.12< /version>
  < /dependency>
    < dependency>
      < groupId>junit< /groupId>
      < artifactId>junit< /artifactId>
      < version>4.11</ version>
      < scope>test</ scope>
    </ dependency>
<!--    导入dom4j解析xml工具的依赖-->
    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>
    <dependency>
      <groupId>jaxen</groupId>
      <artifactId>jaxen</artifactId>
      <version>1.1.6</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql.version}</version>
    </dependency>
< !--     < dependency>-->
< !--      < groupId >org.mybatis</ groupId>-->
< !--      < artifactId>mybatis</ artifactId>-->
< !--      < version>${mybatis.version}</ version>-->
< !--    < /dependency>-->

  </ dependencies>

以上为pom.xml文件内容,注意:注释掉mybatis的依赖
2.以下为主配置文件部分:

< configuration>
<!--    配置环境-->
    <environments default="mysql">
	<!--        配置mysql-->
        <environment id="mysql">
        <!--            配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!--           配置数据源-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value=""/>
            </dataSource>
        </environment>
    </environments>
    <!--    配置映射路径-->
    <mappers>
    <!--&lt;!&ndash;        记得使用文件夹格式的路径&ndash;&gt;-->
        <mapper resource="huang/dao/IUserDao.xml"></mapper>
        <!--        直接指向接口类,使用注解进行配置-->
        <!--        <mapper class="huang.dao.IUserDao"/>-->
    </mappers>
</configuration>

3.映射文件部分:

< mapper namespace= "huang.dao.IUserDao" >
    
     < select id="findAll" resultType="huang.domain.User">
        select * from usertable
    </ select>
    </mapper>

4.Resources类:

import java.io.InputStream;

//1.提供获取xml的方法
public class Resources {
    public static InputStream getResourceAsStream(String mybatis){
        InputStream resource = Resources.class.getClassLoader().getResourceAsStream(mybatis);
        return resource;
    }
}

5.SqlSessionFactoryBuilder类


import huang.mybatis.config.Configuration;
import huang.mybatis.session.imp.DefaultSqlSessionFactory;
import huang.mybatis.utils.XMLConfigBuilder;

import java.io.InputStream;

public class SqlSessionFactoryBuilder {
    //1.提供建立工厂的方法
    public SqlSessionFactory build(InputStream inputStream) {
        Configuration configuration = XMLConfigBuilder.loadConfiguration(inputStream);
        return new DefaultSqlSessionFactory(configuration);
    }
}

6.SqlSessionFactory类和其实现类

public interface SqlSessionFactory {
    //1.提供给出session方法
    SqlSession openSession();
}


import huang.mybatis.config.Configuration;
import huang.mybatis.session.SqlSession;
import huang.mybatis.session.SqlSessionFactory;
//实现了工厂类,那么就要提供一个可以操作数据库的类
public class DefaultSqlSessionFactory implements SqlSessionFactory {
//1.提供可以操作数据库的类
    private Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    public DefaultSqlSessionFactory() {
    }

    @Override
    public SqlSession openSession() {
        return new DefaultSqlSession(configuration);
    }
}

7.SqlSession类和其实现类:

public interface SqlSession {
    //1.提供返回Mapper方法和关闭方法
    <t> t getMapper(Class<t> daoClass);

    //关闭方法
    void close();
}
import huang.mybatis.config.Configuration;
import huang.mybatis.proxy.MapperProxy;
import huang.mybatis.session.SqlSession;
import huang.mybatis.utils.DatasourceUtil;

import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;

public class DefaultSqlSession implements SqlSession {
    //1.用于创建代理对象
    private Configuration config;
    private Connection connection;
    public DefaultSqlSession(Configuration config) {
        this.config = config;
        connection = DatasourceUtil.getConnection(config);
    }

    @Override
    public <t> t getMapper(Class<t> daoClass) {
        //1.创建代理对象
//       Proxy.newProxyInstance(daoClass.getClassLoader(), new Class[]{daoClass}, new MapperProxy(config.getMappers(),connection));
        return (t) Proxy.newProxyInstance(daoClass.getClassLoader(),
                new Class[]{daoClass},new MapperProxy(config.getMappers(),connection));
    }
    //2.用于释放资源
    @Override
    public void close() {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

8.Configuration类,注意:要new一个HashMap,否则直接使用会报空指针异常,其次,要使用putAll方法,否则在XmlConfigBuilder中:

            for(Element mapperElement : mapperElements){
                //判断mapperElement使用的是哪个属性
                Attribute attribute = mapperElement.attribute("resource");
                if(attribute != null){
                    System.out.println("使用的是XML");
                    //表示有resource属性,用的是XML
                    //取出属性的值
                    String mapperPath = attribute.getValue();//获取属性的值"com/itheima/dao/IUserDao.xml"
                    //把映射配置文件的内容获取出来,封装成一个map
                    Map<String, Mapper> mappers = loadMapperConfiguration(mapperPath);
                    //给configuration中的mappers赋值
                    cfg.setMappers(mappers);
                }

中,就会出现setMappers调用多次,覆盖上次的情况出现,所以采用追加的方式加入

import java.util.HashMap;
import java.util.Map;

public class Configuration {
    private String username;
    private String url;
    private String password;
    private String driver;
    private Map<String, Mapper> mappers = new HashMap<String, Mapper>();//如果不适用new出来的方式,则会空指针异常

    public Map<String, Mapper> getMappers() {
        return mappers;
    }

    public void setMappers(Map<String, Mapper> mappers) {
//        this.mappers = mappers;
        //由于xmlConfigBuilder类中,通过for循环的方式,多次调用setMappers方法
    //若直接等于,那么就是每次set都用新的代替旧的,旧的就消失了
        this.mappers.putAll(mappers);//使用追加的方式
    }


    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }
}

9.Mapper类:

public class Mapper {
    //1.封装了sql语句和全限定类名的类
    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;
    }

    @Override
    public String toString() {
        return "Mapper{" +
                "queryString='" + queryString + '\'' +
                ", resultType='" + resultType + '\'' +
                '}';
    }
}

10.MapperProxy类:

import huang.mybatis.config.Mapper;
import huang.mybatis.utils.Executor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Map;

public class MapperProxy implements InvocationHandler {
    private Map<String, Mapper> mapppers;
    private Connection con;

    public MapperProxy(Map<String, Mapper> mapppers, Connection con) {
        this.mapppers = mapppers;
        this.con = con;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //1.获取当前方法名
        String name = method.getName();
        //2.获取方法所在类全限定类名
        String className = method.getDeclaringClass().getName();
        //3.组合成KEY
        String key = className + "." + name;
        //4.判断是否又mapper
        Mapper mapper = mapppers.get(key);
        if (mapper == null) {
            throw new IllegalArgumentException("传入参数错误");
        }
        //5.利用工具类执行查询
        return new Executor().selectList(mapper,con);//使用匿名内部类
    }
}

11.DatasourceUtil类:

public class DatasourceUtil {
    //1.用于获取一个连接
    public static Connection getConnection(Configuration configuration) {
            Connection connection=null;
        try {
            Class.forName(configuration.getDriver());
            connection = DriverManager.getConnection(configuration.getUrl(), configuration.getUsername(), configuration.getPassword());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }
}

12.关于各个类的功能:
1.配置文件xml:填写连接信息和映射文件所在位置
2.dao接口的映射:提供接口的实现方法,全类名,返回值类型
3.Resouces类:简单封装读取文件的方式。
4.xmlConifgBuilder类:用于解析xml文件,获取其中的信息
5.SqlSessionFactoryBuilder类:构建者类,调用xmlConfigBuilder类获取信息创建Configuration对象,返回工厂的实现类(将configuration传递给工厂)
6.Configuration类:封装连接信息,和Mapper类,
7.Mapper类:一个mapper对应一个dao类,封装sql信息,和返回值的全限定类名
8.Executory类:进行数据库查询,返回list集合,需要传参(mapper,connection)
9.DefaSqlSessionFactory类:将传过来的configuration传递给工厂内创建的DefaultSqlSession对象
10.DefaultSqlSession类:提供getMapper方法,返回匿名内部代理类,在此代理类中,创建了MapperProxy类;创建了一个连接
11.MapperProxy类:是代理类;包含map<String,Mapper>成员和connection成员,从session中获取;通过当前方法Method获取当前执行的方法名,和该方法所在类的全类名;然后从map中获取对应的value(mapper对象),
12.DatasourceUtil类:通过configuration对象,创建对象,返回对象

13.从信息流向的角度来看
主要有:连接信息,查询和返回信息,含查询方法的类信息。
1.连接信息,记录在xml中,通过Resoureces方法,获取文件流,通过XmlCoinfigBuilder解析文件,封装在Configuration类中,最终在sqlSession对象中调用datasourceUitl类中解析连接信息,创建连接
2.查询和返回信息,记录在映射路径下的xml文件中,通过XmlConfigBuilder解析该信息,封装在Configuraion中的mappers容器,作为一个Mapper类对象中,最终传递给MapperProxy代理类,调用Executor中的方法,提供查询和返回信息类。
3.含查询方法的类信息,记录在映射路径下的xml文件中,XmlConfigBuilder不解析该信息,而是在session创建mapper对象的过程中,传入类.class文件,(session.getMapper(IUserDao.class));最后,在MapperProxy代理类中,通过现在执行的方法,进行获取类信息.

14.从类的流向角度来看:
Configuration承载着大部分信息,所以从构建者(SqlSessionFactoryBuilder)到MapperProxy,一直传递。Connection只创建了一个,由SqlSession创建,传递给MapperProxy。

15.最后测试部分如下:

public class MybatisTest {
    public static void main(String[] args) throws IOException {
        //1.读取xml文件
        InputStream in = Resources.getResourceAsStream("mybatisConfig.xml");/*Resources中调用类.class.getClassLoader.
        getResourcesAsStream(String mybatis)方法读取xml文件*/
        //2.通过构建者模式创建工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();/*构建者通过文件流利用XMLConfigBuilder创建配置类的对象;
        配置类封装了连接信息,和mapper对象;mapper对象封装了查询语句和返回值类型*/
        SqlSessionFactory factory = builder.build(in);/*构建者通过配置类的对象,创建工厂的实现类。*/
        //3.通过工厂获取session
        SqlSession session = factory.openSession();/*将配置类传递给实现了SqlSession接口的实现类*/
        //4.通过session获取mapper
        IUserDao mapper = session.getMapper(IUserDao.class);/*session创建了dao接口的代理类,创建连接对象,传递给代理类mapper和
        连接对象;*/
        //5.执行dao方法
        List<User> all = mapper.findAll();/*利用mapper中的成员变量从mappers中读取sql和返回值类,利用Executor().selectList(mapper,con)
        执行方法,返回list集合*/
        for (User user : all) {
            System.out.println(user);
        }
        //6.释放资源
        in.close();
        session.close();
    }
}

发布了12 篇原创文章 · 获赞 0 · 访问量 140

猜你喜欢

转载自blog.csdn.net/weixin_44065691/article/details/105353281