写点什么

Mybatis 学习笔记 -- 自定义 Mybatis,java 程序员面试笔试宝典百度云

用户头像
极客good
关注
发布于: 刚刚

用于创建 SqlSessionFactory 对象的类


public cla


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


ss SqlSessionFactoryBuilder {


/**


  • 根据参数的字节输入流来构建一个 SqlSessionFactory 工厂

  • @param config

  • @return


*/


public SqlSessionFactory build(InputStream config){


Configuration cfg= XMLConfigBuilder.loadConfiguration(config);


return new DefaultSqlSessionFactory(cfg);


}


}


创建 SqlSessionFactory 接口


public interface SqlSessionFactory {


/**


  • 用于打开 SqlSession 对象

  • @return


*/


SqlSession openSession();


}


数据库交互的核心类 SqlSession,里面可以创建 dao 接口的代理对象


public interface SqlSession {


/**


  • 根据参数创建一个代理对象

  • @param daoInterfaceClass dao 的接口字节码

  • @param <T>

  • @return


*/


<T> T getMapper(Class<T> daoInterfaceClass);


/**


  • 释放资源


*/


void close();


}


此时,就会发现我们的 MybatisTest 测试类已经不再报错了,接下来开始完成 SqlSessionFactoryBuilder 中的代码:



配置工具类 XMLConfigBuilder,用于解析 XML 文件


/**


  • @Author: Ly

  • @Date: 2020-07-11 12:00

  • 用于解析配置文件


*/


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();


//把映射配置文件的内容获取出来,封装成一个 map


Map<String, Mapper> mappers=loadMapperConfiguration(mapperPath);


//给 configuration 中的 mappers 赋值


cfg.setMappers(mappers);


}else{


System.out.println("使用的是注解");


//表示没有 resource 属性,使用的是注解


//获取 class 属性的值


String daoClassPath =mapperElement.attributeValue("class");


//根据 daoClassPath 获取封装的必要信息


Map<String,Mapper> mappers=loadMapperAnnotation(daoClassPath);


//给 configuration 中的 mapper 赋值


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 的全限定类名和方法名组成)


*/


private static Map<String,Mapper> loadMapperConfiguration(String mapperPath) throws Exception{


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();


}


}


/**


  • 根据传入的参数,得到 dao 中所有被 select 注解标注的方法

  • 根据方法名称和类名,以及方法上注解 value 属性的值,组成 Mapper 的必要信息


*/


private static Map<String,Mapper> loadMapperAnnotation(String daoClassPath) throws Exception{


//定义返回值对象


Map<String,Mapper> mappers =new HashMap<String,Mapper>();


//1.得到 dao 接口的字节码对象


Class daoClass =Class.forName(daoClassPath);


//2.得到 dao 接口中的方法数组


Method[] methods=daoClass.getMethods();


//3.遍历 Method 数组


for(Method method :methods){


//取出每一个方法,判断是否有 select 注解


boolean isAnnotated =method.isAnnotationPresent(Select.class);


if(isAnnotated){


//创建 Mapper 对象


Mapper mapper =new Mapper();


//取出注解的 value 属性值


Select selectAnno = method.getAnnotation(Select.class);


String queryString = selectAnno.value();


mapper.setQueryString(queryString);


//获取当前方法的返回值,还要求必须带有泛型信息


Type type =method.getGenericReturnType();//List<User>


//判断 type 是不是参数化的类型


if(type instanceof ParameterizedType){


//强转


ParameterizedType ptype =(ParameterizedType)type;


//得到参数化类型中的实际类型参数


Type[] types =ptype.getActualTypeArguments();


//取出第一个


Class domainClass =(Class)types[0];


//获取 domainClass 的类名


String resultType =domainClass.getName();


//给 Mapper 赋值


mapper.setResultType(resultType);


}


//组装 key 的信息


//获取方法的名称


String methodName = method.getName();


String className =method.getDeclaringClass().getName();


String key =className+"."+methodName;


//给 map 赋值


mappers.put(key,mapper);


}


}


return mappers;


}


}


编写 Select 注解


@Retention(RetentionPolicy.RUNTIME)


@Target(ElementType.METHOD)


public @interface Select {


/**


  • 配置 sql 语句

  • @return


*/


String value();


}


配置类 Configuration


/**


  • @Author: Ly

  • @Date: 2020-07-11 11:37

  • 自定义 mybatis 的配置类


*/


public class Configuration {


private String driver;


private String url;


private String username;


private String password;


private Map<String,Mapper> mappers =new HashMap<String, Mapper>();


public Map<String, Mapper> getMappers() {


return mappers;


}


public void setMappers(Map<String, Mapper> 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;


}


}


编写 Mapper 类


/**


  • @Author: Ly

  • @Date: 2020-07-11 11:58

  • 用来封装执行的 SQL 语句和结果类型的全限定类名


*/


public class Mapper {


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;


}


}


DefaultSqlSessionFactory 类


/**


  • @Author: Ly

  • @Date: 2020-07-11 14:02

  • SqlSessionFactory 接口的实现类


*/


public class DefaultSqlSessionFactory implements SqlSessionFactory {


private Configuration cfg;


public DefaultSqlSessionFactory(Configuration cfg){


this.cfg=cfg;


}


/**


  • 用于创建一个新的操作数据对象

  • @return


*/


public SqlSession openSession() {


return new DefaultSqlSession(cfg);


}


}


编写 DefaultSqlSession 类


/**


  • @Author: Ly

  • @Date: 2020-07-11 14:17

  • SqlSession 接口的实现


*/


public class DefaultSqlSession implements SqlSession {


private Configuration cfg;


private Connection connection;


public DefaultSqlSession(Configuration cfg){


this.cfg=cfg;


connection = DataSourceUtil.getConnection(cfg);


}


/**


  • 用于创建代理对象

  • @param daoInterfaceClass dao 的接口字节码

  • @param <T>

  • @return


*/


public <T> T getMapper(Class<T> daoInterfaceClass) {


return (T) Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(),


new Class[]{daoInterfaceClass},new MapperProxy(cfg.getMappers(),connection));


}


/**


  • 用于释放资源


*/


public void close() {


if(connection!=null){


try {


connection.close();


} catch (SQLException e) {


e.printStackTrace();


}


}


}


}


编写数据库工具类 DataSourceUtil


public class DataSourceUtil {


/**


  • 用来获取一个连接

  • @param cfg

  • @return


*/


public static Connection getConnection(Configuration cfg){


try {


Class.forName(cfg.getDriver());


return DriverManager.getConnection(cfg.getUrl(),cfg.getUsername(),cfg.getPassword());


} catch (Exception e) {


throw new RuntimeException(e);


}


}


}


编写代理类 MapperProxy


/**


  • @Author: Ly

  • @Date: 2020-07-11 16:56


*/


public class MapperProxy implements InvocationHandler {


//map 的 key 是全限定类名+方法名


private Map<String, Mapper> mappers;


private Connection conn;


public MapperProxy(Map<String,Mapper> mappers,Connection conn){


this.mappers=mappers;


this.conn=conn;


}


/**


  • 用于对方法进行增强的,我们的增强其实就是调用 selectList 方法

  • @param proxy

  • @param method

  • @param args

  • @return

  • @throws Throwable

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
Mybatis学习笔记--自定义Mybatis,java程序员面试笔试宝典百度云