jdbc源码分析
今天看了jdbc的源码,感觉特别有意思,下面是jdbc的传统步骤:
Connection connection=null; PreparedStatement preparedStatement=null; ResultSet resultSet=null; try { Class.forName("com.mysql.cj.jdbc.Driver"); connection=DriverManager.getConnection("jdbc:mysql://localhost:3306/myshool?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong","root","root"); String sql="select * from student where name= ? "; preparedStatement=connection.prepareStatement(sql); preparedStatement.setString(1,"小明"); resultSet=preparedStatement.executeQuery(); while (resultSet.next()){ System.out.println(resultSet.getString("id")+" " +resultSet.getString("name")); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }
这里我用的是最新的mysql驱动包
第一点:加载驱动。
Class.forName("com.mysql.cj.jdbc.Driver");
在最开始用的时候很不明白,首先为什么要加载一个驱动类,之后就可以取得connection,也非常好奇DriverManager是怎么来拿到驱动类的信息,另外还有为什么要用Class.forName加载。
后来点进Driver里,看到:
所以也就是说,在Class.forName加载完驱动类后,开始执行静态代码块时,会new一个Driver,并调用DriverManager的registerDriver把Driver给注册到自己的驱动程序管理器(DriverManager)中去。
在这里说一下ClassLoader.loadClass和Class.forName的区别:
- Classloder.loaderClass(String name)
其实该方法内部调用的是:Classloder. loadClass(name, false)
方法:Classloder. loadClass(String name, boolean resolve)
1:参数name代表类的全限定类名
2:参数resolve代表是否解析,resolve为true是解析该类
- Class.forName(String name)
其实该方法内部调用的是:Class.forName(className, true, ClassLoader.getClassLoader(caller))
方法:Class.forName0(String name, boolean initialize, ClassLoader loader)
参数name代表全限定类名
参数initialize表示是否初始化该类,为true是初始化该类
参数loader 对应的类加载器
- 两者最大的区别
Class.forName得到的class是已经初始化完成的
Classloder.loaderClass得到的class是没有初始化的
这里Driver类也继承NonRegisteringDriver并实现java.sql.Driver接口
我们点进NonRegisteringDriver类会发现,它也是实现java.sql.Driver接口:
最主要看一下Driver接口:
public interface Driver { //获取Connection 方法。数据库的url,及info至少得包含user,password key Connection connect(String url, java.util.Properties info) throws SQLException; //判断是否是一个正确的url字符串。 boolean acceptsURL(String url) throws SQLException; //得到驱动的属性(user,password,port等)。 DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info) throws SQLException; //得到主要版本 int getMajorVersion(); //得到次要版本 int getMinorVersion(); //判断是否是一个正确的driver boolean jdbcCompliant(); //------------------------- JDBC 4.1 ----------------------------------- //返回父日志 public Logger getParentLogger() throws SQLFeatureNotSupportedException; }
其实Driver接口是每个数据库驱动都必须继承的接口。
言归正传,我们可以看到在Driver类中的静态代码块有DriverManager.registerDriver(这是把Driver给注册到自己的驱动程序管理器(DriverManager)中)的方法,我们点进去可以看到DriverManager类,此类没有继承和实现任何接口,它是管理一组 JDBC 驱动程序的基本服务。
这里显示的是一些重要的方法
public class DriverManager { //已经注册的驱动列表 private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>(); private static volatile int loginTimeout = 0; private static volatile java.io.PrintWriter logWriter = null; private static volatile java.io.PrintStream logStream = null; // Used in println() to synchronize logWriter private final static Object logSync = new Object(); //阻止被初始化,DriverManager里面都是静态的方法。 private DriverManager(){} //初始化加载驱动,其中用到了ServiceLoader机制 static { //初始化加载驱动 loadInitialDrivers(); //打印日志 println("JDBC DriverManager initialized"); } /*因为源代码过长,这里我们只讲重要的一些方法*/ //初始化加载驱动 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; } //这里涉及到一个ServiceLoader概念。上面我推荐了一篇文章,想了解的可以去看看。通过ServiceLoader去加载所有的driver。 AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { //通过ServiceLoader.load()方法加载所有驱动 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); Iterator<Driver> driversIterator = loadedDrivers.iterator(); try{ while(driversIterator.hasNext()) { driversIterator.next(); } } catch(Throwable t) { } return null; } }); println("DriverManager.initialize: jdbc.drivers = " + drivers); if (drivers == null || drivers.equals("")) { return; } String[] driversList = drivers.split(":"); println("number of Drivers:" + driversList.length); for (String aDriver : driversList) { try { println("DriverManager.Initialize: loading " + aDriver); Class.forName(aDriver, true, ClassLoader.getSystemClassLoader()); } catch (Exception ex) { println("DriverManager.Initialize: load failed: " + ex); } } } //3个获取connection方法,对外提供的方法。 @CallerSensitive public static Connection getConnection(String url, java.util.Properties info) throws SQLException { return (getConnection(url, info, Reflection.getCallerClass())); } //这个方法中可以看到,properties中至少配置参数其实就是user和password @CallerSensitive public static Connection getConnection(String url, String user, String password) throws SQLException { java.util.Properties info = new java.util.Properties(); if (user != null) { info.put("user", user); } if (password != null) { info.put("password", password); } return (getConnection(url, info, Reflection.getCallerClass())); } @CallerSensitive public static Connection getConnection(String url) throws SQLException { java.util.Properties info = new java.util.Properties(); return (getConnection(url, info, Reflection.getCallerClass())); } // 内部真正工作的方法(私有) private static Connection getConnection( String url, java.util.Properties info, Class<?> caller) throws SQLException { //检查callerCL是否为空,如果为空则通过Thread.currentThread().getContextClassLoader()去加载 ClassLoader callerCL = caller != null ? caller.getClassLoader() : null; synchronized(DriverManager.class) { // synchronize loading of the correct classloader. if (callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } } //这里判断url if(url == null) { throw new SQLException("The url cannot be null", "08001"); } println("DriverManager.getConnection(\"" + url + "\")"); SQLException reason = null; //遍历registeredDrivers去获得正确的connection for(DriverInfo aDriver : registeredDrivers) { // 如果callerCL不允许读取驱动,就会跳过。 if(isDriverAllowed(aDriver.driver, callerCL)) { try { println(" trying " + aDriver.driver.getClass().getName()); //真正的获取connection的方法,其实还是通过driver接口中的connect方法。 Connection con = aDriver.driver.connect(url, info); if (con != null) { println("getConnection returning " + aDriver.driver.getClass().getName()); return (con); } } catch (SQLException ex) { if (reason == null) { reason = ex; } } } else { println(" skipping: " + aDriver.getClass().getName()); } } if (reason != null) { println("getConnection failed: " + reason); throw reason; } println("getConnection: no suitable driver found for "+ url); throw new SQLException("No suitable driver found for "+ url, "08001"); } //通过url获取driver。 @CallerSensitive public static Driver getDriver(String url) throws SQLException { println("DriverManager.getDriver(\"" + url + "\")"); Class<?> callerClass = Reflection.getCallerClass(); // 通过遍历registeredDrivers中每个驱动 for (DriverInfo aDriver : registeredDrivers) { // acceptsURL()方法判断url是否符合driver if(isDriverAllowed(aDriver.driver, callerClass)) { try { if(aDriver.driver.acceptsURL(url)) { println("getDriver returning " + aDriver.driver.getClass().getName()); return (aDriver.driver); } } catch(SQLException sqe) { } } else { println(" skipping: " + aDriver.driver.getClass().getName()); } } println("getDriver: no suitable driver"); throw new SQLException("No suitable driver", "08001"); } //注册驱动的方法 public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException { //调用下面的方法 registerDriver(driver, null); } // public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException { //判断driver是否已经被加载到registeredDrivers,没有就加进去 if(driver != null) { registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); } else { throw new NullPointerException(); } println("registerDriver: " + driver); } //注销driver方法。 @CallerSensitive public static synchronized void deregisterDriver(Driver driver) throws SQLException { if (driver == null) { return; } SecurityManager sec = System.getSecurityManager(); if (sec != null) { sec.checkPermission(DEREGISTER_DRIVER_PERMISSION); } println("DriverManager.deregisterDriver: " + driver); DriverInfo aDriver = new DriverInfo(driver, null); if(registeredDrivers.contains(aDriver)) { if (isDriverAllowed(driver, Reflection.getCallerClass())) { DriverInfo di = registeredDrivers.get(registeredDrivers.indexOf(aDriver)); if(di.action() != null) { di.action().deregister(); } //通过remove()注销。 registeredDrivers.remove(aDriver); } else { throw new SecurityException(); } } else { println(" couldn't find driver to unload"); } } }
getConnection这个方法中的properties类注意一下,它是以后能用到的以.properties为后缀名的属性文件
第二点:获取数据库链接Connection
代表与数据库的链接,并拥有创建SQL语句的方法,以完成基本的SQL操作,同时为数据库事务提供提交和回滚方法
这里说一下Connection接口的重要方法:
public interface Connection extends Wrapper, AutoCloseable { //创建statement Statement createStatement() throws SQLException; //创建prepareStatement PreparedStatement prepareStatement(String sql) throws SQLException; //创建CallableStatement CallableStatement prepareCall(String sql) throws SQLException; //转换sql为本机执行sql String nativeSQL(String sql) throws SQLException; //设置是否自动提交 状态 void setAutoCommit(boolean autoCommit) throws SQLException; //判断是否是自动提交 boolean getAutoCommit() throws SQLException; //提交 void commit() throws SQLException; //回滚 void rollback() throws SQLException; //关闭连接 void close() throws SQLException; //判断是否关闭 boolean isClosed() throws SQLException; ...//都是一些规范的接口,太多就不一一列举了。 }
第三点:获取statement、preparedstatement
statement和preparedStatement的作用是一样的,都是用来执行sql。区别:
1.代码的可读性和可维护性.
虽然用PreparedStatement来代替Statement会使代码多出几行,但这样的代码无论从可读性还是可维护性上来说.都比直接用Statement的代码高很多档次:
stmt.executeUpdate(“insert into tb_name (col1,col2,col2,col4) values (‘”+var1+”’,’”+var2+”’,”+var3+”,’”+var4+”’)”);//stmt是Statement对象实例
perstmt = con.prepareStatement(“insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)”);
perstmt.setString(1,var1);
perstmt.setString(2,var2);
perstmt.setString(3,var3);
perstmt.setString(4,var4);
perstmt.executeUpdate(); //prestmt是 PreparedStatement 对象实例
2.PreparedStatement尽最大可能提高性能.
3.最重要的一点是极大地提高了安全性.
由此可见:开发的时候尽量用preparedStatement,少用statement。
preparedStatement中一些重要的方法:
public interface PreparedStatement extends Statement { //用于产生单个结果集的语句,例如 SELECT 语句 ResultSet executeQuery() throws SQLException; //用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL(数据定义语言)语句 int executeUpdate() throws SQLException; //设置空值,必须穿入type,不然可能报空指针异常 void setNull(int parameterIndex, int sqlType) throws SQLException; ...(同理有很多set的方法) //清空属性 void clearParameters() throws SQLException; //用于执行返回多个结果集、多个更新计数或二者组合的语句 boolean execute() throws SQLException; ...//都是一些规范的接口,太多就不一一列举了。 }
还有一个接口CallableStatement 提供了一种以标准形式调用已储存过程的方法。
第四点:ResultSet
结果集(ResultSet)是数据中查询结果返回的一种对象,可以说结果集是一个存储查询结果的对象,但是结果集并不仅仅具有存储的功能,他同时还具有操纵数据的功能,可能完成对数据的更新等
ResultSet中一些重要的方法:
public interface ResultSet extends Wrapper, AutoCloseable { //是否有下一个值 boolean next() throws SQLException; //关闭 void close() throws SQLException; //是否为空 boolean wasNull() throws SQLException; //得到第几列的String类型数据 String getString(int columnIndex) throws SQLException; boolean getBoolean(int columnIndex) throws SQLException; ...(太多get方法不一一列举) //得到列名为columnLabel的值 String getString(String columnLabel) throws SQLException; //更新第几列为空(各种update方法) void updateNull(int columnIndex) throws SQLException; //插入(updateRow、deleteRow、refreshRow 等) void insertRow() throws SQLException; }
以上就是jdbc中所注意的类,接口源码。