引言 最近想分析一下mybatis源码,但是在这之前呢,先分析一下JDBC操作mysql。
先写个小例子 1.在mysql中建表 1 2 3 4 CREATE TABLE `people` ( `id` int (11 ) DEFAULT NULL , `name` varchar (255 ) DEFAULT NULL ) ENGINE= InnoDB DEFAULT CHARSET= utf8mb4;
2.获取连接工具方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static Connection getConnection () { Connection conn = null ; try { Class.forName("com.mysql.cj.jdbc.Driver" ); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8" , "root" , "123456" ); } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return conn; }
3.写增删该查操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 public static void select () { String sql = "select * from people order by id" ; Connection conn = null ; PreparedStatement pstmt = null ; ResultSet rs; try { conn = getConnection(); pstmt = conn.prepareStatement(sql); rs = pstmt.executeQuery(); while (rs.next()) { int id = rs.getInt("id" ); String name = rs.getString("name" ); System.out.println("id:" + id + ",name:" + name); } } catch (SQLException e) { e.printStackTrace(); } finally { close(pstmt); close(conn); } } public static void update (int id, String name) { String sql = "update people set name =? where id=?" ; Connection conn = null ; PreparedStatement pstmt = null ; try { conn = getConnection(); pstmt = conn.prepareStatement(sql); pstmt.setString(1 , name); pstmt.setInt(2 , id); pstmt.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { close(pstmt); close(conn); } } public static void del (int id) { String sql = "delete from people where id = ?" ; Connection conn = null ; PreparedStatement pstmt = null ; try { conn = getConnection(); pstmt = conn.prepareStatement(sql); pstmt.setInt(1 , id); pstmt.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { close(pstmt); close(conn); } } public static void add (int id, String name) { Connection conn = null ; PreparedStatement pstmt = null ; try { conn = getConnection(); pstmt = conn.prepareStatement("insert into people(id,name) values(?,?)" ); pstmt.setInt(1 , id); pstmt.setString(2 , name); pstmt.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { close(pstmt); close(conn); } }
4.各种关闭: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public static void close (PreparedStatement pstmt) { if (pstmt != null ) { try { pstmt.close(); } catch (SQLException e) { e.printStackTrace(); } } } public static void close (Connection conn) { if (conn != null ) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } public static void close (ResultSet rs) { if (rs != null ) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } }
5.测试方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void main (String[] args) { System.out.println("------start---------" ); select(); System.out.println("------add---------" ); add(1 , "张三" ); select(); System.out.println("------update---------" ); update(1 , "李四" ); select(); System.out.println("------del---------" ); del(1 ); select(); System.out.println("------end---------" ); }
一套下来,看起来没啥难点,但是其内部是如何操作的呢?
DriverManager初始化分析 首先,去掉初始化驱动类com.mysql.cj.jdbc.Driver这一步骤,也能够运行成功,原因就在DriverManager中:
1 2 3 4 5 6 7 8 static { loadInitialDrivers(); println("JDBC DriverManager initialized" ); }
加载初始化驱动:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 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 ; } }); 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); } } }
这里有两处重要的地方,一个是ServiceLoader.load(Driver.class),另一处是 Class.forName(aDriver, true,ClassLoader.getSystemClassLoader()); 着重看一下ServiceLoader:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public static <S> ServiceLoader<S> load (Class<S> service) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); } public static <S> ServiceLoader<S> load (Class<S> service, ClassLoader loader) { return new ServiceLoader <>(service, loader); } private ServiceLoader (Class<S> svc, ClassLoader cl) { service = Objects.requireNonNull(svc, "Service interface cannot be null" ); loader = (cl == null ) ? ClassLoader.getSystemClassLoader() : cl; acc = (System.getSecurityManager() != null ) ? AccessController.getContext() : null ; reload(); } public void reload () { providers.clear(); lookupIterator = new LazyIterator (service, loader); } private LazyIterator (Class<S> service, ClassLoader loader) { this .service = service; this .loader = loader; }
紧接着迭代LazyIterator的时候:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private boolean hasNextService() { if (nextName != null) { return true; } if (configs == null) { try { String fullName = PREFIX + service.getName(); if (loader == null) configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName); } catch (IOException x) { fail(service, "Error locating configuration files", x); } } while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } pending = parse(service, configs.nextElement()); } nextName = pending.next(); return true; }
这里的PREFIX为:”META-INF/services/“
service.getName()为:java.sql.Driver
读取jar包下面的META-INF/services/java.sql.Driver文件, mysql-connector-java-8.0.17中的内容为:com.mysql.cj.jdbc.Driver 最后通过反射加载com.mysql.cj.jdbc.Driver类。 之后就可以使用了。
DriverManager获取Connection 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 // Worker method called by the public getConnection() methods. private static Connection getConnection( String url, java.util.Properties info, Class<?> caller) throws SQLException { /* * 当callerCl为空时,我们应该检查应用程序的类加载器(它正在间接调用这个类), * 这样就可以从这里加载rt.jar外部的JDBC驱动程序类。 */ ClassLoader callerCL = caller != null ? caller.getClassLoader() : null; synchronized(DriverManager.class) { // synchronize loading of the correct classloader. if (callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } } if(url == null) { throw new SQLException("The url cannot be null", "08001"); } println("DriverManager.getConnection(\"" + url + "\")"); // 遍历试图建立连接的已加载的registeredDrivers。 // Remember the first exception that gets raised so we can reraise it. SQLException reason = null; for(java.sql.DriverInfo aDriver : registeredDrivers) { // 如果调用者没有加载驱动程序的权限,那么跳过它。 if(isDriverAllowed(aDriver.driver, callerCL)) { try { println(" trying " + aDriver.driver.getClass().getName()); //调用驱动程序的connect方法 Connection con = aDriver.driver.connect(url, info); if (con != null) { // Success! println("getConnection returning " + aDriver.driver.getClass().getName()); return (con); } } catch (SQLException ex) { if (reason == null) { reason = ex; } } } else { println(" skipping: " + aDriver.getClass().getName()); } } // if we got here nobody could connect. 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"); }
在这里就交给驱动程序获取连接了: 如com.mysql.cj.jdbc.Driver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public Connection connect (String url, Properties info) throws SQLException { try { try { if (!ConnectionUrl.acceptsUrl(url)) { return null ; } else { ConnectionUrl conStr = ConnectionUrl.getConnectionUrlInstance(url, info); switch (conStr.getType()) { case SINGLE_CONNECTION: return ConnectionImpl.getInstance(conStr.getMainHost()); case LOADBALANCE_CONNECTION: return LoadBalancedConnectionProxy.createProxyInstance((LoadbalanceConnectionUrl)conStr); case FAILOVER_CONNECTION: return FailoverConnectionProxy.createProxyInstance(conStr); case REPLICATION_CONNECTION: return ReplicationConnectionProxy.createProxyInstance((ReplicationConnectionUrl)conStr); default : return null ; } } } catch (UnsupportedConnectionStringException var5) { return null ; } catch (CJException var6) { throw (UnableToConnectException)ExceptionFactory.createException(UnableToConnectException.class, Messages.getString("NonRegisteringDriver.17" , new Object []{var6.toString()}), var6); } } catch (CJException var7) { throw SQLExceptionsMapping.translateException(var7); } }
这边就复杂了,不深入了驱动程序源码了,大概知道java最终将连接交给驱动程序实现。
PreparedStatement Connection.prepareStatement(sql)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 PreparedStatement prepareStatement (String sql) throws SQLException;