1、JDBC连接数据库的四部曲
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url,user,password);
String sql = " select id,name from user ";
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery(sql);
while(rs.next()){
int id = rs.getInt(1);
String name = rs.getString(2);
System.out.println("id:"+id+",name:"+name);
}
rs.close();
statement.close();
connection.close();
}
2、mybatis连接数据库源码分析
mybatis基于jdbc,本质上不过是对jdbc进行封装,这里分析一下源码是如何连接到数据库的,即对应jdbc的加载驱动和获取连接;
public static void main(String[] args) throws InterruptedException {
Reader reader = Resources.getResourceAsReader("config/mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
}
这里简单测试获取到sqlSession ,拿到它就可以进行数据库操作。
调试时注意在第2行打上断点,一路debug下去发现build 方法主要是在解析mybatis的xml文件,当解析执行到到org.apache.ibatis.builder.xml.XMLConfigBuilder#environmentsElement 要注意,下图284 行是解析数据源配置,
跟踪进入org.apache.ibatis.builder.xml.XMLConfigBuilder#dataSourceElement ,注意330行
此处明显是用了反射来实例化对象,这里直接在idea调试工具中看不出来resolveClass后得到的是什么类型,此处可以用idea自带的调试工具打印出来看看就知道了,如下图:
打印结果:class org.apache.ibatis.datasource.pooled.PooledDataSourceFactory ,此处的PooledDataSourceFactory 对应配置文件中的type=“pooled”
<dataSource type="pooled">
<property name="driver" value="${oracle.driver}"/>
<property name="url" value="${oracle.url}"/>
<property name="username" value="${oracle.username}"/>
<property name="password" value="${oracle.password}"/>
</dataSource>
知道反射结果的类型后,直接找到这个类,在他的构造函数打个断点,注意public class PooledDataSourceFactory extends UnpooledDataSourceFactory ,构造函数执行时需要先调用其父类的构造方法,所以我们要在其父类构造方法也打断点观察,
其父类构造函数
public UnpooledDataSourceFactory() {
this.dataSource = new UnpooledDataSource();
}
调用org.apache.ibatis.datasource.unpooled.UnpooledDataSource 的构造方法,所以进入UnpooledDataSource 类,此类就比较重点了,它有个静态代码块:
static {
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
registeredDrivers.put(driver.getClass().getName(), driver);
}
}
静态代码快有个DriverManager.getDrivers(); ,这里就是加载驱动,此函数中的操作就类似我们在jdbc中的Class.forName(xxxx),跟踪进入java.sql.DriverManager 看看,此类也有个静态代码块:
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
重点来了,有个loadInitialDrivers() ,这里就是经典的java的spi实现,第20行会读取mysql驱动包下的META-INF/services/java.sql.Driver 文件,此文件中只有一行com.mysql.cj.jdbc.Driver ,代表java.sql.Driver 接口的实现类是com.mysql.cj.jdbc.Driver ,此处需要了解java的SPI机制
private static void loadInitialDrivers() {
String drivers;
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
}
return null;
}
});
继续跟踪进入driversIterator.next(); 会到达java.util.Iterator#next ,最后肯定会到java.util.ServiceLoader.LazyIterator#nextService ,如下代码,第8行调用了反射来加载之前读取到的com.mysql.cj.jdbc.Driver ,这里就完成了SPI,就加载了驱动
private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service,
"Provider " + cn + " not found");
}
if (!service.isAssignableFrom(c)) {
fail(service,
"Provider " + cn + " not a subtype");
}
try {
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error();
}
加载驱动后,需要注册驱动,加载com.mysql.cj.jdbc.Driver 同样会执行静态代码块,同时完成驱动注册
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
后面不再分析了。。。。
|