Mybatis学习之代理

Mybatis学习之代理,第1张

Mybatis学习之代理

今天学到了mybatis的代理,感觉是一种很优秀的思路,来记录下
传统JDBC项目的数据库 *** 作是通过先建一个Dao接口,再在DaoImpl中实现相应方法进而完成 *** 作的(应该是吧…没接触过),我刚学习时在Mybatis中用到了类似的方式

#StudentDao.java
package com.example.springdemo.dao;

import com.example.springdemo.domain.Student;

import java.io.IOException;
import java.util.List;

public interface StudentDao {
    Student selectStudentById(int id) throws IOException;
    int insertStudent(Student student) throws IOException;
    List selectStudents();
}

#impl.StudentDaoImpl.java
package com.example.springdemo.dao.impl;

import com.example.springdemo.dao.StudentDao;
import com.example.springdemo.domain.Student;
import com.example.springdemo.utils.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class StudentDaoImpl implements StudentDao {
    @Override
    public Student selectStudentById(int id) {
        SqlSession session = MyBatisUtil.getSqlSession();
        String sqlId="com.example.springdemo.dao.StudentDao.selectStudentById";
        Student student=session.selectOne(sqlId,id);
        System.out.println("out:"+student);
        session.close();
        return student;
    }

    @Override
    public int insertStudent(Student student) {
        SqlSession session = MyBatisUtil.getSqlSession();
        String sqlId="com.example.springdemo.dao.StudentDao.insertStudent";
        int row=session.insert(sqlId,student);
        System.out.println("out:"+row);
        session.commit();
        session.close();
        return row;
    }

    @Override
    public List selectStudents() {
        SqlSession session = MyBatisUtil.getSqlSession();
        String sqlId="com.example.springdemo.dao.StudentDao.selectStudents";
        List students=session.selectList(sqlId);
        for (Student stu:students){
            System.out.println("out:"+stu);
        }
        session.close();
        return students;
    }
}

然后在我的代码中调用DaoImpl

package com.example.springdemo;
//
//import com.example.springdemo.dao.StudentDao;
//import com.example.springdemo.dao.impl.StudentDaoImpl;
//import com.example.springdemo.domain.Student;
//import com.example.springdemo.utils.MyBatisUtil;
//import org.apache.ibatis.io.Resources;
//import org.apache.ibatis.session.SqlSession;
//import org.apache.ibatis.session.SqlSessionFactory;
//import org.apache.ibatis.session.SqlSessionFactoryBuilder;
//import org.junit.jupiter.api.Test;
//
//import java.io.IOException;
//import java.io.InputStream;
//import java.util.List;
//
public class DaoImplTest {
//    @Test
//    public void testSelectStudentById() throws IOException {
//        StudentDao dao =new StudentDaoImpl();
//        dao.selectStudentById(1);
//    }
//    @Test
//    public void testInsertStudent() throws IOException {
//       StudentDao dao = new StudentDaoImpl();
//       Student student1 = new Student();
//       student1.setAge(18);
//       student1.setName("quin");
//       student1.setId(3);
//       student1.setEmail("114514");
//       dao.insertStudent(student1);
//    }
//    @Test
//    public void testSelectStudents() throws IOException {
//        StudentDao dao =new StudentDaoImpl();
//        dao.selectStudents();
//    }
}

这里注释掉了…取消注释太麻烦了就凑合看吧
这种方式的确能按照我们想要的方式查询数据库,但mybatis提供了一种可以不需要Impl来实现具体方法,就可以直接调用Dao中的方法
已知Mybatis每个mapper都需要一个xml文件




    
        select id,name,email,age from user
    
    
        insert into user values(#{id},#{name},#{email},#{age})
    

像这样,我没用去看底层代码,猜测是Mybatis只需要读取这个xml文件,就可以把namespace下的sql语句和该namespace(也就是我们的Dao)联系起来
这也是为什么Mybatis在调用的时候一定需要这样的代码的原因吧

package com.example.springdemo.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MyBatisUtil {
    private static SqlSessionFactory factory = null;
    static {
        String config = "mybatis.xml";
        try {
            InputStream inputStream = Resources.getResourceAsStream(config);
            factory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static SqlSession getSqlSession() {
        SqlSession session = null;
        if (factory != null) {
            session = factory.openSession();
        }
        return session;
    }

}

一定要传入一个xml文件,然后用InputStream去读取
当然这不是唯一的一种方式,官网也给出了其他方式的实例

这里先不去考虑这种,专注于利用xml文件的方式
我看的是动力节点的课程(不得不说动力节点yyds),它一开始教的是这样利用Mybatis读数据

package com.example.springdemo;

import com.example.springdemo.domain.Student;
import com.example.springdemo.utils.MyBatisUtil;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyTest {
    @Test
    public void testSelectStudentById() throws IOException {
        String config="mybatis.xml";
        InputStream inputStream=Resources.getResourceAsStream(config);
        SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session=factory.openSession();
        String sqlId="com.example.springdemo.dao.StudentDao.selectStudentById";
        Student student=session.selectOne(sqlId,2);
        System.out.println("out:"+student);
        session.close();
    }
    @Test
    public void testInsertStudent() throws IOException {
        String config="mybatis.xml";
        InputStream inputStream=Resources.getResourceAsStream(config);
        SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session=factory.openSession();
        String sqlId="com.example.springdemo.dao.StudentDao.insertStudent";
        Student student1 = new Student();
        student1.setAge(18);
        student1.setName("quin");
        student1.setId(3);
        student1.setEmail("114514");
        int row=session.insert(sqlId,student1);
        System.out.println("out:"+row);
        session.commit();
        session.close();
    }
    @Test
    public void testSelectStudents() throws IOException {
        SqlSession session = MyBatisUtil.getSqlSession();
        String sqlId="com.example.springdemo.dao.StudentDao.selectStudents";
        List students=session.selectList(sqlId);
        for (Student stu:students){
            System.out.println("out:"+stu);
        }
        session.close();
    }
}

重点在

 String sqlId="com.example.springdemo.dao.StudentDao.selectStudentById";
 Student student=session.selectOne(sqlId,2);

sqlId传入的是不是很眼熟,正是我们Dao中的方法名
这里稍微跟进了一下selectOne,它的本质是一种特殊的selectList方法的实现
我们直接看selectList的代码

statement是我们传入的sqlId,底层对它进行了一个getMapper的 *** 作,而这个getMapper就是今天的重点,但我们暂且搁置,看后来我学到的另一种 *** 作数据的方法

package com.example.springdemo;

import com.example.springdemo.dao.StudentDao;
import com.example.springdemo.domain.Student;
import com.example.springdemo.utils.MyBatisUtil;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class NewMyTest {
    @Test
    public void testSelectStudentById() throws IOException {
        SqlSession session = MyBatisUtil.getSqlSession();
        StudentDao dao = session.getMapper(StudentDao.class);
        Student student=dao.selectStudentById(1);
        System.out.println("out:"+student);
        session.close();
    }
    @Test
    public void testInsertStudent() throws IOException {
        SqlSession session = MyBatisUtil.getSqlSession();
        StudentDao dao = session.getMapper(StudentDao.class);
        Student student1 = new Student();
        student1.setAge(18);
        student1.setName("quin");
        student1.setId(3);
        student1.setEmail("114514");
        int row=dao.insertStudent(student1);
        System.out.println("out:"+row);
        session.commit();
        session.close();
    }
    @Test
    public void testSelectStudents() throws IOException {
        SqlSession session = MyBatisUtil.getSqlSession();
        StudentDao dao = session.getMapper(StudentDao.class);
        List students = dao.selectStudents();
        for (Student stu:students){
            System.out.println("out:"+stu);
        }
        session.close();
    }
}

重点区别在

 StudentDao dao = session.getMapper(StudentDao.class);
 Student student=dao.selectStudentById(1);

这里我并没有写Impl文件,就直接调用了Dao里的方法,可Dao是接口啊,里面是空的什么都没写,那它是怎么执行我的代码的呢
可以看到这里我们的SqlSession(Mybatis中的重要对象)使用了getMapper方法,作为一个学过不短时间安全的人,第一时间想到的就是去追溯这个方法的源码…

这里就出现了Proxy,也就是我们标题所提到的代理,也是Mybatis的主打卖点,这里调用一篇别人的博客
https://blog.csdn.net/qq_42046105/article/details/112914184

追溯到newInstance方法

这里从mapperInterface中的得到了Dao这个接口
把断点打在可以得到mapperInterface的地方

发现构造方法中传入的,那么就需要找到哪里第一次调用了这个构造方法传了值,这里再逆推就没有什么收获了,但感觉既然是从xml中获得的,而且应该在一开始,那就该是SqlSessionFactory或者SqlSessionFactoryBuilder了,发现的确,之后借鉴了博客里的思路,找到
org.apache.ibatis.builder.xml.XMLMapperBuilder.bindMapperForNamespace

这里addMapper了

addmapper后调用knownMappers.puts,触发了构造方法,成功初始化
正所谓java一切框架皆基于反射,在这里套用上面博客里的一句话总结就是

Mybatis 会通过 Class#forname 得到 Mapper 接口 Class 对象,生成对应的动态代理对象,核心业务处理都会在
InvocationHandler#invoke 进行处理,Mapper 没有实现类,所有调用 JDBC 等 *** 作都是在 Mybatis
InvocationHandler 实现的。

的确我们上面好像也没有看到它具体实现sql语句的部分…应该都在某个InvocationHandler中,然后通过invoke进行反射调用吧…

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5709971.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存