jdbc源码分析
2023-01-05 09:33:30 6 举报
jdbc
作者其他创作
大纲/内容
适配器模式代理模式
一、驱动加载源码
Class.forName(\"com.mysql.jdbc.Driver\
里面总之会找每个驱动的这个目录,然后各种迭代加载,实例化
缺点 使用ServiceLoader类来从classpath下加载驱动会将META-INF/services/java.sql.Driver文件中的每一个驱动都加载进来(即使我们不使用
四、简单看看ResultSet
服务端编译(mysql数据库)
三、简单看看获取连接的源码
DriverManager的方法基本都是静态的不管怎么样,最终还是调用getConnection()方法
1、会预编译默认是客户端编译
为了通过SPI方式加载驱动,约定了路径 private static final String PREFIX = \"META-INF/services/\
从静态代码块说起
以下几种情况,经过3测试取平均值,情况如下:本地预编译:65769 ms本地预编译+缓存:63637 ms服务端预编译:100985 ms服务端预编译+缓存:57299 ms从中我们可以看出本地预编译加不加缓存其实差别不是太大,服务端预编译不加缓存性能明显会降低很多,但是服务端预编译加缓存的话性能还是会比本地好很多。主要原因是服务端预编译不加缓存的话本身prepare也是有开销的,另外多了大量的round-trip。
然后围绕这个自定义迭代器展开逻辑 while(driversIterator.hasNext()) { driversIterator.next(); }
客户端编译(Driver驱动类)
二、简单看看获取连接的源码
这是ServiceLoader的的全局变量
这个LazyIterator惰性迭代器实现了Iterator接口,自定义了hasNext和next方法,注意后面调用的时候,其实实现方法不一样
drivers = 获取这个属性System.getProperty(\"jdbc.drivers\");
其实这个类狠简单,主要是其父类做的很多事情public class Driver extends NonRegisteringDriver implements java.sql.Driver {
解决sql注入问题
如果不进行缓存,则MySQL服务端预编译也好,本地预编译也好,都会对同一种语句重复预编译。因此为了提升效率,往往我们需要启用缓存,通过设置连接中cachePrepStmts参数就可以控制是否启用缓存。此外通过prepStmtCacheSize参数可以控制缓存的条数,MySQL驱动默认是25,通常实践中都在250-500左右;通过prepStmtCacheSqlLimit可以控制长度多大的sql可以被缓存,MySQL驱动默认是256,通常实践中往往设置为2048这样。
(1)DBCP 是Apache提供的数据库连接池。tomcat服务器自带dbcp数据库连接池。速度相对c3p0较快,但因自身存在BUG,Hibernate3已不再提供支持。(2)C3P0 是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以,hibernate官方推荐使用。(3)Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点.(4)BoneCP 是一个开源组织提供的数据库连接池,速度快。(5)Druid 是阿里提供的数据库连接池,据说是集DBCP 、C3P0、Proxool优点于一身的数据库连接池,但是速度不确定是否有BoneCP快。
loadedDrivers.iterator();
五、连接池
所以分析一下这个私有的DriverManager.getConnection()源码
static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException(\"Can't register driver!\"); } }
内部再调用reload();
System.getProperty(\"jdbc.drivers\")
如果找到了,就实例化,实例化的时候,就会调用驱动的静态内部类的注册方法,
这个方法挺复杂,核心,各种迭代和判断,可能加载的驱动不是一个把,但是都给加载出来再说,然后再尝试的每个方法都调用连接,如果有一个连接通了,就结束了
开启方式
调用此方法后,后续对{@link . link的调用* #iterator() iterator}方法将会延迟查找并实例化*提供程序,就像新创建的加载器一样。
组装ClassLoader 参数。继续调用
Statement
ServiceLoader也实现了iterator接口,但是匿名内部类,这个迭代器里面再调用下面的惰性迭代器
1、假如maven引用了两个mysql依赖,最终只会保留一个版本低的2、如果主动加载了Driver驱动的实例,但是还是会在DriverManger类的静态方法初始化之后执行,也就是说,没再比这个SPI机制更提前探索性的加载driver驱动的时机了
ServiceLoader.load(Driver.class);
static { loadInitialDrivers(); println(\"JDBC DriverManager initialized\"); }
loadInitialDrivers()
加载驱动的4种方式
一、这个类是java自己开发的位于package java.sql; 下实现类:java.sql.DriverManagerDriverManager 会试着加载尽可能多的它可以找到的驱动程序,然后,对于任何给定连接请求,它会让每个驱动程序依次试着连接到目标 URL。二、接口:public interface Driver 每个驱动程序类必须实现的接口public interface Connection 与特定数据库的连接(会话)。在连接上下文中执行 SQL 语句并返回结果。public interface Statement 用于执行静态 SQL 语句并返回它所生成结果的对象public interface PreparedStatement 表示预编译的 SQL 语句的对象。SQL 语句被预编译并存储在 PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句。以上接口都是由JDK进行提供,由具体的数据库厂商进行实现
PreparedStatement
在实际生产环境中,如MyBatis等ORM框架大量使用了预编译语句,最终底层调用都会走到MySQL驱动里,从驱动中了解相关实现细节有助于更好地理解预编译语句。一些网上的文章称必须使用useServerPrepStmts才能开启预编译,这种说法是错误的。实际上JDBC规范里没有说过预编译语句这件事情由本地来做还是服务端来做。MySQL早期版本中由于不支持服务端预编译,因此当时主要是通过本地预编译。经过实际测试,对于频繁使用的语句,使用服务端预编译+缓存效率还是能够得到可观的提升的。但是对于不频繁使用的语句,服务端预编译本身会增加额外的round-trip,因此在实际开发中可以视情况定夺使用本地预编译还是服务端预编译以及哪些sql语句不需要开启预编译等。
jdbc:mysql://localhost/test?useServerPrepStmts=true&cachePrepStmts=true服务端预编译+缓存
下面这个方法其实是调用自己内部实现的迭代器
最后一个参数是Reflection.getCallerClass() 后面主要是用这个参数获取类加载器
从JDBC 2.0以后,即使我们不写Class.forName(“com.mysql.jdbc.Driver”);语句也仍然可以加载数据库驱动,建立数据库连接和正常使用数据库 //直接建立连接 ,里面会做判断,不存在再加载 Connection conn = DriverManager.getConnection(\"jdbc:mysql://localhost:3306/testdb\
在这里每个getConnection都是用CallerSensitive修饰的,调用getCallerClass应该是获取外面使用DriverManager.getConnection()的类的名称
特别注意的是:在实例化厂商提供的Driver实现类的时候里面有个静态的方法
0 条评论
回复 删除
下一页