今天学到了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; ListselectStudents(); }
#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 ListselectStudents() { 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文件
像这样,我没用去看底层代码,猜测是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"; Liststudents=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); Liststudents = 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进行反射调用吧…
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)