新闻资讯
mybatis入门案例自定义实现(一)
一、需要实现的类和接口
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();
}
}
}
回复列表