新闻资讯

新闻资讯 行业动态

mybatis入门案例自定义实现(一)

编辑:006     时间:2020-02-14

一、需要实现的类和接口

public static void main(String[] args) throws Exception{ //1.读取配置文件 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.创建SqlSessionFactory工厂 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(in); //3.使用工厂生产SqlSession对象 SqlSession session = factory.openSession(); //4.使用SqlSession创建Dao的代理对象 IUserDao userDao = session.getMapper(IUserDao.class); //5.使用代理对象执行方法 List<User> users = userDao.findAll(); for(User user : users) {
        System.out.println(user);
    } //6.释放资源 session.close();
    in.close();
}

根据测试类MybatisTest中的main函数,需要实现的类有:Resources、SqlSessionFactoryBuilder,需要实现的接口有:SqlSessionFactory、SqlSession。由于是自定义mybatis,我们将项目配置文件pom.xml中的mybatis的相关信息删除。

二、依据测试类创建缺少的接口和类
1.创建Resources类

在src/main/java目录下,创建mybatis包,在mybatis包下创建io包,在io包下新建类Resources,添加静态方法

//使用类加载器读取配置文件的类 public class Resources { /**
     * @description 根据传入的参数获取一个字节输入流
     * @param filePath
     * @return */ public static InputStream getResourceAsStream(String filePath){ return Resources.class.getClassLoader().getResourceAsStream(filePath);
    }

}

之所以这样子添加,我们可以查看原mybatis中,getResourcesAsStream()方法的调用层级,可以发现最终调用的就是ClassLoader类下的getResourceAsStream()方法,因此我们直接调用该方法即可。

//MybatisTest.class InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); //Resources.class public static InputStream getResourceAsStream(String resource) throws IOException { return getResourceAsStream((ClassLoader)null, resource);
 } public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
        InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader); if (in == null) { throw new IOException("Could not find resource " + resource);
        } else { return in;
        }
 } //ClassLoaderWrapper.class public InputStream getResourceAsStream(String resource, ClassLoader classLoader) { return this.getResourceAsStream(resource, this.getClassLoaders(classLoader));
 } InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
        ClassLoader[] var3 = classLoader; int var4 = classLoader.length; for(int var5 = 0; var5 < var4; ++var5) {
            ClassLoader cl = var3[var5]; if (null != cl) {
                InputStream returnValue = cl.getResourceAsStream(resource); if (null == returnValue) {
                    returnValue = cl.getResourceAsStream("/" + resource);
                } if (null != returnValue) { return returnValue;
                }
            }
        } return null;
  } //ClassLoader.class public InputStream getResourceAsStream(String name) {
        Objects.requireNonNull(name);
        URL url = getResource(name); try { return url != null ? url.openStream() : null;
        } catch (IOException e) { return null;
        }
  }
2.创建SqlSessionFactorty接口

在mybatis包下新建包sqlsession,在sqlsession包下新建接口SqlSessionFactorty,依据main()方法,该接口中需要声明方法openSession()。

public interface SqlSessionFactory { //用于打开一个新的SqlSession对象 SqlSession openSession();
}
3.创建SqlSession接口

在sqlsession包下新建接口SqlSessionFactorty,依据main()方法,该接口中需要声明方法getMapper()和close()。

//自定义mybatis中和数据库交互的核心类,可以创建dao接口的代理对象 public interface SqlSession { /**
     * @description 根据参数创建一个代理对象
     * @param daoInterfaceClass dao的接口字节码
     * @param <T>
     * @return */ <T> T getMapper(Class<T> daoInterfaceClass); //释放资源 void close();
}


4.创建SqlSessionFactoryBuilder类

在sqlsession包下创建SqlSessionFactoryBuilder类,添加一个build()方法,先返回空值,之后我们再来补全。

/* 用于创建一个SqlSessionFactory对象 */ public class SqlSessionFactoryBuilder { /**
     * @Description: 根据字节输入流来构建一个SqlSessionFactory工厂
     * @param config
     * @return */ public SqlSessionFactory build(InputStream config){ return null;
    }
}

在原mybatis中,build()方法调用层级为:

//MybatisTest.class SqlSessionFactory factory = builder.build(in); //SqlSessionFactoryBuilder.class public SqlSessionFactory build(InputStream inputStream) { return this.build((InputStream)inputStream, (String)null, (Properties)null);
 } public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5; try {
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            var5 = this.build(parser.parse());
        } catch (Exception var14) { throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
        } finally {
            ErrorContext.instance().reset(); try {
                inputStream.close();
            } catch (IOException var13) {
            }

        } return var5;
 } public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config);
  }

可以看到最后返回的是一个DefualtSqlSessionFactory对象,所以在自定义mybatis时,我们也需要返回一个DefuaultSqlSessionFactory对象。而且还用到了xml文件解析类XMLConfigBuilder,因此接下来我们需要定义xml解析类和DefuaultSqlSessionFactory类。

5.创建xml解析类XMLConfigBuilder

解析xml文件,我们采用的是dom4j技术,在查找信息时,用到了XPath。所以我们需要在项目文件pom.xml中添加上相关内容,导入jaxen是为了能够使用XPath:

<dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>jaxen</groupId> <artifactId>jaxen</artifactId> <version>1.1.6</version> </dependency>

在mybatis包下新建包utils,在utils包下新建类XMLConfigBuilder。解析xml不是本文重点,所以解析类的代码在这里直接给出:

/**
 *  用于解析配置文件
 */ public class XMLConfigBuilder { /**
     * 解析主配置文件,把里面的内容填充到DefaultSqlSession所需要的地方
     * 使用的技术:dom4j+xpath
     */ public static Configuration loadConfiguration(InputStream config){ try{ //定义封装连接信息的配置对象(mybatis的配置对象) Configuration cfg = new Configuration(); //1.获取SAXReader对象 SAXReader reader = new SAXReader(); //2.根据字节输入流获取Document对象 Document document = reader.read(config); //3.获取根节点 Element root = document.getRootElement(); //4.使用xpath中选择指定节点的方式,获取所有property节点 List<Element> propertyElements = root.selectNodes("//property"); //5.遍历节点 for(Element propertyElement : propertyElements){ //判断节点是连接数据库的哪部分信息 //取出name属性的值 String name = propertyElement.attributeValue("name"); if("driver".equals(name)){ //表示驱动 //获取property标签value属性的值 String driver = propertyElement.attributeValue("value");
                    cfg.setDriver(driver);
                } if("url".equals(name)){ //表示连接字符串 //获取property标签value属性的值 String url = propertyElement.attributeValue("value");
                    cfg.setUrl(url);
                } if("username".equals(name)){ //表示用户名 //获取property标签value属性的值 String username = propertyElement.attributeValue("value");
                    cfg.setUsername(username);
                } if("password".equals(name)){ //表示密码 //获取property标签value属性的值 String password = propertyElement.attributeValue("value");
                    cfg.setPassword(password);
                }
            } //取出mappers中的所有mapper标签,判断他们使用了resource还是class属性 List<Element> mapperElements = root.selectNodes("//mappers/mapper"); //遍历集合 for(Element mapperElement : mapperElements){ //判断mapperElement使用的是哪个属性 Attribute attribute = mapperElement.attribute("resource"); if(attribute != null){
                    System.out.println("使用的是XML"); //表示有resource属性,用的是XML //取出属性的值 String mapperPath = attribute.getValue();//获取属性的值"dao/IUserDao.xml" //把映射配置文件的内容获取出来,封装成一个map Map<String,Mapper> mappers = loadMapperConfiguration(mapperPath); //给configuration中的mappers赋值 cfg.setMappers(mappers);
                }
            } //返回Configuration return cfg;
        }catch(Exception e){ throw new RuntimeException(e);
        }finally{ try {
                config.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }

    } /**
     * 根据传入的参数,解析XML,并且封装到Map中
     * @param mapperPath    映射配置文件的位置
     * @return map中包含了获取的唯一标识(key是由dao的全限定类名和方法名组成)
     *          以及执行所需的必要信息(value是一个Mapper对象,里面存放的是执行的SQL语句和要封装的实体类全限定类名)
     */ private static Map<String,Mapper> loadMapperConfiguration(String mapperPath)throws IOException {
        InputStream in = null; try{ //定义返回值对象 Map<String,Mapper> mappers = new HashMap<String,Mapper>(); //1.根据路径获取字节输入流 in = Resources.getResourceAsStream(mapperPath); //2.根据字节输入流获取Document对象 SAXReader reader = new SAXReader();
            Document document = reader.read(in); //3.获取根节点 Element root = document.getRootElement(); //4.获取根节点的namespace属性取值 String namespace = root.attributeValue("namespace");//是组成map中key的部分 //5.获取所有的select节点 List<Element> selectElements = root.selectNodes("//select"); //6.遍历select节点集合 for(Element selectElement : selectElements){ //取出id属性的值      组成map中key的部分 String id = selectElement.attributeValue("id"); //取出resultType属性的值  组成map中value的部分 String resultType = selectElement.attributeValue("resultType"); //取出文本内容            组成map中value的部分 String queryString = selectElement.getText(); //创建Key String key = namespace+"."+id; //创建Value Mapper mapper = new Mapper();
                mapper.setQueryString(queryString);
                mapper.setResultType(resultType); //把key和value存入mappers中 mappers.put(key,mapper);
            } return mappers;
        }catch(Exception e){ throw new RuntimeException(e);
        }finally{
            in.close();
        }
    }

}
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

回复列表