Location>code7788 >text

Take MySQL as an example to see how maven-shade-plugin can solve the problem of multiple driver versions coexisting?

Popularity:382 ℃/2024-09-02 08:53:02

Happy Moment

On the day of Ching Ming Festival, I saw a kid burning paper on the roadside.
Sneaking a few exam papers into the fire every now and then
While burning, I read: Grandpa, you are old, do more problems over there, it's good for your brain, if you don't understand something, take my teacher away and let him teach you!

开心一刻

premise sth.

suppose that...MySQL 5.7.36 librariesqsl_datax

mysql5

show that one is capable of doing sth.qsl_datax_source and data

CREATE TABLE `qsl_datax_source` (
  `id` bigint(20) NOT NULL COMMENT 'self-incrementing primary key', `username` varchar(255) NOT NULL COMMENT 'name', `qsl_datax_source'
  `username` varchar(255) NOT NULL COMMENT 'name', `password` varchar(255) NOT NULL COMMENT
  `password` varchar(255) NOT NULL COMMENT 'password', `birth_day` date NOT NULL COMMENT 'birth_day' date not NULL COMMENT
  `birth_day` date NOT NULL COMMENT 'date of birth', `remark` text, `remark` text, `remark` text, `remark` text, `remark` text
  `remark` text, `remark` text, `remark` text, `remark` text
  PRIMARY KEY (`id`)
) ENGINE = InnoDB ;
INSERT INTO `qsl_datax_source` VALUES (1, 'zhang san', 'z123456', '1991-01-01', 'zhang san') ;
INSERT INTO `qsl_datax_source` VALUES (2, 'li4', 'l123456', '1992-01-01', 'li4');;
INSERT INTO `qsl_datax_source` VALUES (3, 'wang wu', 'w123456', '1993-01-01', 'wang wu');;
INSERT INTO `qsl_datax_source` VALUES (4, 'hempzi', 'm123456', '1994-01-01', 'hempzi');

You need to synchronize the data in the table to theMySQL 8.0.30

mysql8

sql_db librariesqsl_datax_source table and only use theJDBC of the way, how to realize it? You may find it very simple to just introduce themysql-connector-j dependencies

<dependency>
    <groupId></groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.0.33</version>
</dependency>

Then write the synchronization code directly

public static void main(String[] args) throws Exception {
    String url5 = "jdbc:mysql://192.168.2.118:3307/qsl_datax?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
    String url8 = "jdbc:mysql://192.168.2.118:3311/sql_db?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
    Properties pro = new Properties();
    ("user", "root");
    ("password", "123456");
    // Load Driver Class
    ("");
    // establish a connection
    Connection conn5 = (url5, pro);
    // data search
    Statement statement = ();
    ResultSet resultSet = ("SELECT * FROM qsl_datax_source");
    StringBuilder insertSql = new StringBuilder("INSERT INTO qsl_datax_source(id,username,password,birth_day,remark) VALUES ");
    while (()) {
        // put togethersql
        ("(")
                .append(("id")).append(",")
                .append("'").append(("username")).append("',")
                .append("'").append(("password")).append("',")
                .append("'").append(("birth_day")).append("',")
                .append("'").append(("remark")).append("'")
                .append("),");
    }
    // on account ofmysql5cap (a poem)mysql8The passwords are the same.,So it's the same one. pro
    Connection conn8 = (url8, pro);
    Statement stmt = ();
    int count = ((0, () - 1));
    ("Number of new records inserted:" + count);

    ();
    ();
    ();
    ();
    ();
}

Post-execution output

Number of new records inserted: 4

existMySQL 8.0.30 librariessql_db View Tableqsl_datax_source data

同驱动同步成功

Synchronized completion. Isn't that just a matter of having hands?

行不行

Generally speaking, a higher version of the driver will be compatible with a lower version of the database, but it is not absolute or incomplete; MySQL version, driver version, JDK version corresponds to the following relationship

mysql版本驱动版本jdk版本对应关系

The mysql-connector-j 8.0.33 driver is compatible with MySQL 5.7.36, so the above synchronization is fine, but if the MySQL version is very low (e.g., 5.), for example, from theMySQL 5.1.8 Synchronize toMySQL 8.0.30 Can the synchronization code as above still synchronize successfully (I'm not going to try, and neither should you, since the purpose of the derivation has already been achieved), so the insurance approach would be

mysql-connector-j 8.0.33 Operation MySQL 8.0.30

mysql-connector-java 5.1.49 Operation MySQL 5.7.36

mysql-connector-java 5. Operating MySQL 5.

So here's the problem.

How to insert data into MySQL 8.0.30 after checking data from MySQL 5.7.36 with mysql-connector-java 5.1.49 and then inserting it into MySQL 8.0.30 with mysql-connector-j 8.0.33

multi-driver operation

I'm sure it's easy for you, too. Go ahead and introduce it.mysql-connector-java 5.1.49 dependencies

<dependency>
    <groupId></groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.0.33</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
</dependency>

Then adjust the code

public static void main(String[] args) throws Exception {
    String url5 = "jdbc:mysql://192.168.2.118:3307/qsl_datax?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
    String url8 = "jdbc:mysql://192.168.2.118:3311/sql_db?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
    Properties pro = new Properties();
    ("user", "root");
    ("password", "123456");
    // Load Driver Class
    ("");
    // establish a connection
    Connection conn5 = (url5, pro);
    // data search
    Statement statement = ();
    ResultSet resultSet = ("SELECT * FROM qsl_datax_source");
    StringBuilder insertSql = new StringBuilder("INSERT INTO qsl_datax_source(id,username,password,birth_day,remark) VALUES ");
    while (()) {
        // put togethersql
        ("(")
                .append(("id")).append(",")
                .append("'").append(("username")).append("',")
                .append("'").append(("password")).append("',")
                .append("'").append(("birth_day")).append("',")
                .append("'").append(("remark")).append("'")
                .append("),");
    }
    ("");
    // on account ofmysql5cap (a poem)mysql8The passwords are the same.,So it's the same one. pro
    Connection conn8 = (url8, pro);
    Statement stmt = ();
    int count = ((0, () - 1));
    ("Number of new records inserted:" + count);

    ();
    ();
    ();
    ();
    ();
}

Compare this with the previous code

多驱动使用前后代码比较

Very little adjustment; the output after execution is as follows

Loading class `'. This is deprecated. The new driver class is `'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
Number of new records inserted:4

If you look at the results only, the synchronization did succeed, but the first line of thewarnings It's worth thinking about.

Class Loading. This class has been deprecated. The new driver class is , which is automatically registered through the SPI mechanism and does not need to be loaded manually.

There are 2 questions that arise from this

  1. It shouldn't be.mysql-connector-java 5.1.49 How did they get dumped?
  2. What is the SPI mechanism. When was it loaded?

Let's start with question 2. For more information on the SPI mechanism see

Remember a JDK SPI configuration does not take effect the problem → so simple can not, or go home to raise pigs!

DriverManager There are static code blocks

static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}

loadInitialDrivers() There is a code in

loadInitialDrivers

The driver is automatically loaded, and the driver class often has code similar to the following

驱动类注册驱动实例

Register the driver instance with theDriverManagerSo you don't need to manually load the driver class anymore!

Starting with JDBC 4.0, JDBC drivers support autoloading and no longer require a call to load the driver.

Let's go back to question 1, the synchronized alarm message.

Loading class `'. This is deprecated. The new driver class is `'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.

I'm sure it is.mysql-connector-j 8.0.33 Alarmed out becausemysql-connector-java 5.1.49 No class

Is that right? Do a global search.

This is deprecated. The new driver class is `'

同步告警信息出处

Tap in and we'll findmysql-connector-j 8.0.33 There are also classes

Look carefully, this Driver is not registering its instance into the (used form a nominal expression)

mysql8驱动com_mysql_jdbc_Driver类

What does that mean? It means it's frommysql-connector-j 8.0.33 Loaded class:Instead of getting the information from themysql-connector-java 5.1.49 (of cargo etc) load

Let's run through the whole synchronization process

  1. Through the SPI mechanism, files are loadedMETA-INF/services/ Classes configured in the

    Contents of the file mysql-connector-j 8.0.33

    mysql8驱动java_sql_Driver

    Contents of file mysql-connector-java 5.1.49

    mysql5驱动_java_sql_Driver

    Class loader loading is undoubtedly found in the mysql-connector-j 8.0.33 jar package, and loading the The class loader found the one in the mysql-connector-j 8.0.33 jar package instead of the one in the mysql-connector-java 5.1.49 jar package, so it alerted the

  2. Called manually in code(""); Perform class loading, based on theParental Delegation ModelIf the class has already been loaded, it will not be loaded again, so it is equivalent to not doing anything.

    The previous warning message was not triggered here! If you don't believe me, you can comment out that line of code and run it, and you'll see that you still get the same warning message!

  3. To look up data from MySQL5, the driver used is actually the

    连接mysql5的实际驱动

    Because that's the only driver that fits in the DriverManager.

  4. Called manually in code(""); Perform class loading, based on theParental Delegation ModelIf the class has already been loaded, it will not be loaded again, so it is equivalent to not doing anything.

  5. To check data from MySQL8, the only driver you can use is no doubt the

So the whole synchronization, using the driver under mysql-connector-j 8.0.33, mysql-connector-java 5.1.49 is not used at all, is not in your expectation?

小孩 震惊

So how do we realize our original idea?

How to insert data into MySQL 8.0.30 after checking data from MySQL 5.7.36 with mysql-connector-java 5.1.49 and then inserting it into MySQL 8.0.30 with mysql-connector-j 8.0.33

maven-shade-plugin

Party A throws to the existence of two Jar packages with the same package name and class name, to be used in the project at the same time how to do? There are a number of solutions, but maven-shade-plugin is the optimal solution.

maven plugin maven-shade-plugin, to solve the problem of class coexistence in the same package with the same name of the gods!

Then how should come to the current case, in fact, very simple, just need to use maven-shade-plugin'sRetargeting class It's just a function. Watch me.

  1. Class relocation for mysql-connector-j 8.0.33

    Create a new projectmysql-jdbcThere is no code and no configuration file.

    mysql-jdbc8

    There's only one.

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="/POM/4.0.0"
             xmlns:xsi="http:///2001/XMLSchema-instance"
             xsi:schemaLocation="/POM/4.0.0 /xsd/maven-4.0.">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId></groupId>
        <artifactId>mysql-jdbc8</artifactId>
        <version>8.0.33</version>
    
        <properties>
            <>8</>
            <>8</>
            <>UTF-8</>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId></groupId>
                <artifactId>mysql-connector-j</artifactId>
                <version>8.0.33</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId></groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <version>3.6.0</version>
                    <executions>
                        <execution>
                            <!-- cap (a poem) package Stage Binding -->
                            <phase>package</phase>
                            <goals>
                                <goal>shade</goal>
                            </goals>
                            <configuration>
                                <relocations>
                                    <relocation>
                                        <pattern></pattern>
                                        <shadedPattern>.jdbc8</shadedPattern>
                                    </relocation>
                                </relocations>
                                <filters>
                                    <filter>
                                        <artifact>:mysql-jdbc8</artifact>
                                        <excludes>
                                            <exclude>META-INF/*.*</exclude>
                                        </excludes>
                                    </filter>
                                </filters>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>
    

    mvn install Deploy the repackaged jar to the local repository in one go

    mysql-jdbc8_8_0_30
  2. Adjusting the maven dependencies of the sample code

    mysql-connector-j 8.0.33 to mysql-jdbc8 8.0.33, mysql-connector-java 5.1.49 as is.

    <dependencies>
        <dependency>
            <groupId></groupId>
            <artifactId>mysql-jdbc8</artifactId>
            <version>8.0.33</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
    </dependencies>
    
  3. Adjustment of synchronization code

    Remove manual driver loading, add connection driver information version output

    public static void main(String[] args) throws Exception {
        String url5 = "jdbc:mysql://192.168.2.118:3307/qsl_datax?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
        String url8 = "jdbc:mysql://192.168.2.118:3311/sql_db?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
        Properties pro = new Properties();
        ("user", "root");
        ("password", "123456");
        // establish a connection
        Connection conn5 = (url5, pro);
        // data search
        Statement statement = ();
        ("conn5 driver version: " + ().getDriverVersion());
        ResultSet resultSet = ("SELECT * FROM qsl_datax_source");
        StringBuilder insertSql = new StringBuilder("INSERT INTO qsl_datax_source(id,username,password,birth_day,remark) VALUES ");
        while (()) {
            // put togethersql
            ("(")
                    .append(("id")).append(",")
                    .append("'").append(("username")).append("',")
                    .append("'").append(("password")).append("',")
                    .append("'").append(("birth_day")).append("',")
                    .append("'").append(("remark")).append("'")
                    .append("),");
        }
        // on account ofmysql5cap (a poem)mysql8The passwords are the same.,So it's the same one. pro
        Connection conn8 = (url8, pro);
        ("conn8 driver version: " + ().getDriverVersion());
        Statement stmt = ();
        int count = ((0, () - 1));
        ("Number of new records inserted:" + count);
    
        ();
        ();
        ();
        ();
        ();
    }
    

Processing is complete. Let's run it and see what happens.

maven-shade-plugin改造后执行结果

The previous warning is indeed gone, but a new question arises: why is the driver using the same one, and why isn't the driver in mysql-connector-java 5.1.49 being used?

一个bug改一天

The one in mysql-connector-java 5.1.49 must have been loaded properly and registered with DriverManager, don't you agree? (It doesn't matter if you don't, we'll prove it later.) So why isn't it being used anymore? source code; source code to follow the more simple, I'll take you step by step with the final back to the following methods

#getConnection(, , <?>)

Inside this method is this code

for(DriverInfo aDriver : registeredDrivers) {
    // If the caller does not have permission to load the driver then
    // skip it.
    if(isDriverAllowed(, callerCL)) {
        try {
            println("    trying " + ().getName());
            Connection con = (url, info);
            if (con != null) {
                // Success!
                println("getConnection returning " + ().getName());
                return (con);
            }
        } catch (SQLException ex) {
            if (reason == null) {
                reason = ex;
            }
        }

    } else {
        println("    skipping: " + ().getName());
    }

}

Let's make a break to follow (in the front!!!)

debug_驱动列表

isDriverAllowed The effect is to check that a givenDriverWhether the object is allowed to pass the specifiedClassLoaderloading, we don't need to be concerned with that, but rather we need to be concerned with the

Connection con = (url, info);

Follow me in.#connect

debug_connect

Follow up if you're interested(url)But I don't think it's necessary, it's obvious to match the url against the regular expression to see if it fits, since the MySQL5 url is in the same format as the MySQL8 one!

String url5 = "jdbc:mysql://192.168.2.118:3307/qsl_datax?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
String url8 = "jdbc:mysql://192.168.2.118:3311/sql_db?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";

I used it to connect to MySQL5 and MySQL8 because it was in the front of the queue.

Why is the driver using the same one, and why isn't the driver in mysql-connector-java 5.1.49 being used?

Isn't that clear? You may have another question: why isn't it in the first place? This has to do with the order in which classes are loaded, and is beyond the scope of this article. Does that still fulfill the original purpose?

Checking data from MySQL 5.7.36 with mysql-connector-java 5.1.49 and inserting data into MySQL 8.0.30 with mysql-connector-j 8.0.33

It's definitely possible. Watch me tweak the code.

public static void main(String[] args) throws Exception {
    String url5 = "jdbc:mysql://192.168.2.118:3307/qsl_datax?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
    String url8 = "jdbc:mysql://192.168.2.118:3311/sql_db?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
    Properties pro = new Properties();
    ("user", "root");
    ("password", "123456");

    // establish a connection
    Driver driver5 = getDriver("");
    Connection conn5 = (url5, pro);
    // data search
    Statement statement = ();
    ("conn5 driver version: " + ().getDriverVersion());
    ResultSet resultSet = ("SELECT * FROM qsl_datax_source");
    StringBuilder insertSql = new StringBuilder("INSERT INTO qsl_datax_source(id,username,password,birth_day,remark) VALUES ");
    while (()) {
        // put togethersql
        ("(")
                .append(("id")).append(",")
                .append("'").append(("username")).append("',")
                .append("'").append(("password")).append("',")
                .append("'").append(("birth_day")).append("',")
                .append("'").append(("remark")).append("'")
                .append("),");
    }
    // on account ofmysql5cap (a poem)mysql8The passwords are the same.,So it's the same one. pro
    Driver driver8 = getDriver("");
    Connection conn8 = (url8, pro);
    ("conn8 driver version: " + ().getDriverVersion());
    Statement stmt = ();
    int count = ((0, () - 1));
    ("Number of new records inserted:" + count);

    ();
    ();
    ();
    ();
    ();
}

private static Driver getDriver(String driverClassName) {
    Enumeration<Driver> drivers = ();
    while (()) {
        Driver driver = ();
        if (().getName().equals(driverClassName)) {
            return driver;
        }
    }
    throw new RuntimeException("Driver not found:" + driverClassName);
}

Run it and see what happens.

改造成功_执行结果

At this point I'm just going to say: who else?

还有谁

summarize

  1. Sample code:mysql-driver-demo

    Excluding code for mysql-jdbc8

  2. As far as MySQL is concerned, the mysql-connector-j 8 driver is compatible with MySQL 5.5, 5.6, and 5.7, and it is possible to use mysql-connector-j 8 to connect to MySQL 5.7 in practice.

  3. SQL Server has driver incompatibility.

    Microsoft JDBC Driver for SQL Server Support Matrix

    SQLServer驱动兼容情况
  4. maven-shade-plugin to achieve the coexistence of multiple versions of the driver, simple and efficient, worth mastering!

    maven plugin maven-shade-plugin, to solve the problem of class coexistence in the same package with the same name of the gods!