Mybatis学习笔记--自定义Mybatis,涨姿势了!

return Resources.class.getClassLoader().getResourceAsStream(filePath);

}

}

用于创建SqlSessionFactory对象的类

public class 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

  • @return

*/

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

  •     以及执行所需的必要信息(value是一个Mapper对象,里面存放的是执行的SQL语句和要封装的实体类全限定类名)
    

*/

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 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

//判断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

  • @return

*/

public T getMapper(Class 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

*/

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

//1.获取方法名

String methodName =method.getName();

//2.获取方法所在类的名称

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

//3.组合key

String key = className+"."+methodName;

//4.获取mappers中的mapper对象

Mapper mapper =mappers.get(key);

//5.判断是否有mapper

if(mapper == null){

throw new IllegalArgumentException(“传入的参数有误”);

}

//6.调用工具类执行查询所有

return new Executor().selectList(mapper,conn);

}

}

编写工具类 Executor ,用于执行jdbc相关操作

/**

  • @Author: Ly

  • @Date: 2020-07-11 18:05

*/

public class Executor {

public List selectList(Mapper mapper, Connection conn){

PreparedStatement pstm = null;

ResultSet rs = null;

try{

//1.取出mapper中的数据

String queryString = mapper.getQueryString();//select * from user

String resultType = mapper.getResultType();//com.domain.User

Class domainClass =Class.forName(resultType);

//2.获取PrepareStatement(queryString);

pstm= conn.prepareStatement(queryString);

//3.执行SQL语句,获取结果集

rs = pstm.executeQuery();

//4.封装结果集

List list =new ArrayList();//定义返回值

while(rs.next()){

//实例化要封装的实体类对象

E obj =(E)domainClass.newInstance();

//取出结果集的元信息:ResultSetMetaData

ResultSetMetaData rsmd =rs.getMetaData();

//取出总列数

int columnCount = rsmd.getColumnCount();

//遍历总列数

for(int i =1;i<=columnCount;i++){

//获取每列的名称,列名的序号是从1开始的

String columnName = rsmd.getColumnName(i);

//根据得到列名,获取每列的值

Object columnValue = rs.getObject(columnName);

//给obj赋值,使用Java内省机制(借助PropertyDescriptor实现属性的封装)

PropertyDescriptor pd=new PropertyDescriptor(columnName,domainClass);

//获取它的写入方法

Method writeMethod = pd.getWriteMethod();

//把获取的列的值,给对象赋值

writeMethod.invoke(obj,columnValue);

}

//把赋好值的对象加入到集合中

list.add(obj);

}

return list;

}catch (Exception e){

throw new RuntimeException(e);

Java.png
Class domainClass =Class.forName(resultType);

//2.获取PrepareStatement(queryString);

pstm= conn.prepareStatement(queryString);

//3.执行SQL语句,获取结果集

rs = pstm.executeQuery();

//4.封装结果集

List list =new ArrayList();//定义返回值

while(rs.next()){

//实例化要封装的实体类对象

E obj =(E)domainClass.newInstance();

//取出结果集的元信息:ResultSetMetaData

ResultSetMetaData rsmd =rs.getMetaData();

//取出总列数

int columnCount = rsmd.getColumnCount();

//遍历总列数

for(int i =1;i<=columnCount;i++){

//获取每列的名称,列名的序号是从1开始的

String columnName = rsmd.getColumnName(i);

//根据得到列名,获取每列的值

Object columnValue = rs.getObject(columnName);

//给obj赋值,使用Java内省机制(借助PropertyDescriptor实现属性的封装)

PropertyDescriptor pd=new PropertyDescriptor(columnName,domainClass);

//获取它的写入方法

Method writeMethod = pd.getWriteMethod();

//把获取的列的值,给对象赋值

writeMethod.invoke(obj,columnValue);

}

//把赋好值的对象加入到集合中

list.add(obj);

}

return list;

}catch (Exception e){

throw new RuntimeException(e);

[外链图片转存中…(img-1OsWFU5A-1635178433056)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值