Mybatis源码系列0-从JDBC到Mybatis
说Mybatis之前得先讲讲JDBC
public class JDBCTest { public static void main(String[] args) throws Exception { String url = "jdbc:mysql://XXXXX/test"; String user = "XXXX"; String password = "XXXX"; try { // 1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); // 2.获取连接 Connection connection = DriverManager.getConnection(url, user, password); // 3.获取statement,preparedStatement String sql = "select * from user where id=?"; PreparedStatement prepareStatement = connection.prepareStatement(sql); // 设置参数 prepareStatement.setLong(1, 1l); // 4.执行查询,获取结果, ResultSet rs = prepareStatement.executeQuery(); while (rs.next()) { System.out.println(rs.getString("userName")); System.out.println(rs.getString("name")); System.out.println(rs.getInt("age")); } } finally { // 5.关闭连接,释放资源 if (rs != null) { rs.close(); } if (prepareStatement != null) { prepareStatement.close(); } if (connection != null) { connection.close(); } } } } 复制代码
如果我们在一个项目中所有与数据库打交道的地方都写一堆这样这个东西,肯定是不友好的。
我们分析存在的问题:
DriverManager.getConnection(url, user, password);
写一遍吧。好的写法是对其封装一个类工厂,只在工厂类中写一遍,直接从工厂类中获取Connection 针对这5个步骤的问题,我们自己也可以写出几个工具类出来,其实就是在造轮子了。
Mybatis 就是这么一个框架,帮助我们解决JDBC在使用上的不方便。
配置
对于硬编码的信息,将其维护到XML中,进行集中配置管理,。
mybatis-config.xml: 数据库连接等全局配置信息的集中管理
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!-- 根标签 --> <configuration> <!-- 1.环境配置,可以配置多个,default:指定采用哪个环境 --> <environments default="test"> <!-- id:标识环境--> <environment id="test"> <!-- 事务管理器,JDBC类型的事务管理器 --> <transactionManager type="JDBC" /> <!-- 数据源,池类型的数据源 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/test" /> <property name="username" value="root" /> <property name="password" value="123456" /> </dataSource> </environment> </environments> <!-- 2.sql模板文件的位置的配置 --> <mappers> <mapper resource="mappers/UserMapper.xml" /> </mappers> </configuration> 复制代码
UserMapper.xml: SQL也维护到XML中,使其从JAVA代码中脱离出来,进行集中管理。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- mapper:根标签,namespace:命名空间,命名空间唯一 --> <mapper namespace="UserMapper"> <!-- id:SQL语句的唯一标识,同命名空间下唯一。 resultType:sql语句查询结果集的封装类型 --> <select id="selectUser" resultType="com.wqd.model.User"> select * from user where id= #{id} </select> </mapper> 复制代码
// 读取配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); //根据配置文件,构建sqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取sqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); try{ //执行SQL,获取结果集 //(SQL通过命名空间+SQLID 的格式定位) User user = sqlSession.selectOne("MyMapper.selectUser", 1); }finally { sqlSession .close(); } 复制代码
相对于JDBC:
将大量的编码工作,改为了配置工作。
仍然存在的可优化的点:
为了解决SQL定位的问题,Mybatis还提供了Mapper功能。
通常,我们习惯用一个Dao来表示对数据库的访问操作。
package com.wqd.mapper public interface UserMapper { public User selectUser(); } 复制代码
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace 定义为接口的 全限定名 --> <mapper namespace="com.wqd.mapper.UserMapper"> <!-- id:定义为方法名。 resultType:sql语句查询结果集的封装类型 --> <select id="selectUser" resultType="com.wqd.model.User"> select * from user where id= #{id} </select> </mapper> 复制代码
编码使用
// 读取配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); //根据配置文件,构建sqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取sqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); try{ //获取Mapper ,执行操作 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.selectUser(1); }finally{ sqlSession.close(); } 复制代码
可以看出SQL的定位,通过方法调用的方式来解决了。 这样是比较符合 开发人员的开发习惯 的。
可优化点:
对于session的关闭,往往是开发人员经常遗忘的东西。有时候程序是比人靠谱的。在一些不要控制session.commit的场合,能自动关闭最好。
解决这个问题,Mybitis还提供了一个类 SqlsessionManager 。
从上图我们可以看出, SqlsessionManager 是Sqlsession的实现类,也就是 SqlsessionManager 与 DefaultSqlSession 是平级的,两个都可以表示会话。
但是
// 读取配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); //根据配置文件,构建sqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSessionManager sqlSessionManager = SqlSessionManager.newInstance(sqlSessionFactory); UserDao userMapper =sqlSessionManager.getMapper(UserDao.class); 复制代码
我们看到SqlsessionManager 不需要我们手动关闭session了。是不是又轻松了许多?
其实,SqlsessionManager通过AOP技术,在执行逻辑后进行了增强,使用的开发人员不必关心connection/session的关闭问题。
当然SqlsessionManager 还解决了DefaultSqlSeesion线程不安全问题。具体SqlsessionManager 是如何实现的,在后续文章中再说。
大框架spring,也我们准备了一个JDBC的轮子 Spring-JDBC
Spring-JDBC其实就是我们常说的:JDBCTemplate
不同于mybatis的是
Mybatis 通过对JDBC的封装,使我们可以更加灵活的操作数据库。
接下来,我将通过源码去探索Mybatis内部一些优秀的设计,来解开Mybatis设计的秘密。