动态加载hive-jdbc 本地可以 服务器有问题——2018年(含)之前整理一

591 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

动态加载hive-jdbc

背景

由于hive-jdbc的版本一般不具有向下兼容性,所以当我们需要从不同hadoop集群取hive数据的时候,考虑动态去加载不同版本的hive-jdbc驱动。

问题

同事遇到一个奇葩问题 本地项目可以动态加载hive-jdbc 而服务器有问题将代码放入其他项目中,本地还是还是有问题 回报空指针异常问题

Exception in thread "main" n" java.lang.NullPointerExerException

at at sun.net.util.URLUtil.urlNoFragStriString(ng(URLUtil.java:50)

a)

at at sun.misc.URLClassPath.getLoader(URL(er(URLClassPath.java:485)

a)

at at sun.misc.URLClassPath.getNextLoaderader(er(URLClassPath.java:457)

a)

at at sun.misc.URLClassPath.getResource(Uce(ce(URLClassPath.java:211)

a)

at at java.net.URLClassLoadeoader$er$1.run(URL(un(URLClassLoader.java:365)

a)

at at java.net.URLClassLoadeoader$er$1.run(URL(un(URLClassLoader.java:362)

a)

at at java.security.AccessController.doPrivileged(ged(Native Method)

at at java.net.URLClassLoader.findClass(URL(ss(URLClassLoader.java:361)

a)

at at java.lang.ClassLoader.loadClass(Cla(ss(ClassLoader.java:424)

a)

at at java.lang.ClassLoader.loadClass(Cla(ss(ClassLoader.java:357)

a)

at at org.apache.hive.jdbc.HiveDriver.connect(Hiv(ct(HiveDriver.java:105)

a)

at at com.example.demo.SwitchJDBCUtil.getCon(Swi(on(SwitchJDBCUtil.java:59)

a)

at at com.example.demo.SwitchJDBCUtil.main(Swi(in(SwitchJDBCUtil.java:78)

a)

at at sun.reflect.NativeMethodAccessorImpl.invoke0(Na0(Native Method)

at at sun.reflect.NativeMethodAccessorImpl.invoke(Nat(ke(NativeMethodAccessorImpl.java:62)

a)

at at sun.reflect.DelegatingMethodAccessorImpl.invoke(Del(ke(DelegatingMethodAccessorImpl.java:43)

a)

at at java.lang.reflect.Method.invoke(Met(ke(Method.java:498)

a)

at at com.intellij.rt.execution.application.AppMain.main(App(in(AppMain.java:147))

原驱动包

image.png

源代码

package com.zcah.utils;

\


import java.io.File;

import java.net.MalformedURLException;

import java.net.URL;

import java.net.URLClassLoader;

import java.sql.Connection;

import java.sql.Driver;

import java.sql.SQLException;

import java.util.HashSet;

import java.util.Properties;

import java.util.Set;

\


public class JarLoadUtil {

\


private static URLClassLoader loader = null;

private static String url = "jdbc:hive2://192.168.101.26:10000";

private static String driverName = "org.apache.hive.jdbc.HiveDriver";

private static String userName = "hive";

private static String password = "hive";

// private static String dictory = "/usr/local/static/hive-jdbc-1.1.0";

private static String dictory = "D:\\root\\hive-jdbc-1.1.0";

\


public static void main(String[] args) throws Exception {

URLClassLoader loader = JarLoadUtil.getURLClassLoader();

Class<?> clazz = loader.loadClass(driverName);

Driver driver = (Driver)clazz.newInstance();  

Properties info = new Properties();

info.setProperty("user", userName);

        info.setProperty("password",password);  

   

Connection con = driver.connect(url, info);

String schema = con.getSchema();

System.out.println("schema---->"+schema);

}

/*

 * 加载目录下所有jar文件,并返回相应的的URLClassLoader

 */

public static URLClassLoader getURLClassLoader() {

if (loader == null) {

Set<URL> set = new HashSet<URL>();

String fileNames[] = listFileNames();

if (fileNames != null && fileNames.length > 0) {

for (int i = 0; i < fileNames.length; i++) {

try {

File fileTemp=new File(dictory,fileNames[i]);

set.add(fileTemp.toURI().toURL());

} catch (MalformedURLException e) {

System.out.println("加载目录下jar文件出错!");

throw new RuntimeException("加载目录下jar文件出错!", e);

}

}

URL[] urls = set.toArray(new URL[20]);

loader = new URLClassLoader(urls);

}

}

return loader;

}

\


/*

 * 查询lib目录下的所有文件名称

 */

private static String[] listFileNames() {

File file_directory = new File(dictory);

return file_directory.list();

}

}

\

解决

解决方法,猜测是依赖包的问题,于是修改pom文件添加以下依赖

<dependency>

   <groupId>org.apache.hadoop</groupId>

   <artifactId>hadoop-client</artifactId>

   <version>2.5.2</version>

   <!-- added by zhangh for conflict in log4j-over-slf4j and slf4j-log4j12 -->

   <exclusions>

      <exclusion>

         <groupId>org.slf4j</groupId>

         <artifactId>slf4j-log4j12</artifactId>

      </exclusion>

      <exclusion>

         <groupId>log4j</groupId>

         <artifactId>log4j</artifactId>

      </exclusion>

   </exclusions>

   <!-- conflict end -->

</dependency>

本地其他程序可以正常运行 
于是推测 还缺少 hadoop-client jar包,但是 hadoop-client依赖 hadoop-commons包
所以应该加2个jar文件在驱动文件夹中于是全部的jar文件应该是

image.png 注意:我这里没有用同一版本,最好用集群中最高版本(如果向下皆兼容的话),不行就用最低版本,再不行只能一个个试了,我这个亲测可用,兼容CHD中hadoop2.0-2.6版本,动态加载的原因是我要管理多个集群。
但是放在服务器上依然报错:

image.png 跟入代码发现是ClassLoader类加载器的问题:

*多个jar文件加载 本地IDEA环境可用 部署到服务器上不可用

URLClassLoader loader = new URLClassLoader(urls);//服务器上不可用

*服务器上 要获取当前线程 调用 上下文类加载器 亲测 可行

URLClassLoader loader = new URLClassLoader(urls,Thread.currentThread().getContextClassLoader());

修改后代码

public Connection getCon(String url,String driverName,String userName,String password,String dictory) throws Exception{

                //动态加载jar包

                //File file = new File(dictory);

                File file = new File("/usr/local"+dictory);

                System.out.println("hive-jdbc的jar文件在服务器的存储路径:"+file.getPath());

                //得到该目录下的所有的jar文件

                File[] listFiles = file.listFiles();

                //创建一个set集合用于存储驱动文件的url,使得url不能重复

                Set<URL> set = new HashSet<URL>();

                //遍历所有的文件

                for (File file2 : listFiles) {

                File fileTemp=new File(file,file2.getName());

                System.out.println("文件名:"+file2.getName());

                set.add(fileTemp.toURI().toURL());

                System.out.println(fileTemp.toURI().toURL());

        }

        //将集合给转换为数组

        URL[] urls = set.toArray(new URL[20]);

        //单个jar文件加载

        //URLClassLoader loader = new URLClassLoader(new URL[] { file.toURI().toURL() });

\


        //多个jar文件加载 本地IDEA环境可用 部署到服务器上不可用

        //URLClassLoader loader = new URLClassLoader(urls);

\


        //服务器上 获取当前线程 调用 上下文类加载器 亲测 可行

        URLClassLoader loader = new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());

\


        System.out.println(loader);

        System.out.println(file.toURI().toURL());

\


        Class cls = loader.loadClass(driverName);

         //实例化driver对象

        Driver driver = (Driver) cls.newInstance();

        Properties info = new Properties();

        info.setProperty("user", userName);

        info.setProperty("password",password);

\


        //获取连接对象

        Connection connect = null;

                try {

                        connect = driver.connect(url, info);

                } catch (SQLException e) {

                        e.printStackTrace();

                        System.out.println("连接hive失败:"+e.getMessage());

                }

                System.out.println("连接hive成功!");

                return connect;

        }

\


public static void main(String[] args) throws Exception {

        SwitchJDBCUtil jdbc = new SwitchJDBCUtil();

               // HiveQueryResultSet res;

                ResultSet res;

                Connection conn = null;

                Statement stmt = null;

                String sql = "show databases";

                //mysql测试

                //Connection con = jdlc.getCon("jdbc:mysql://192.168.101.194:3306/test",    "com.mysql.jdbc.Driver", "root", "root","D:/mysqljar");

                conn  = jdbc.getCon("jdbc:hive2://192.168.101.8:10000/", "org.apache.hive.jdbc.HiveDriver","hive","hive","C:\\Users\\Administrator\\Desktop\\test-hive1.1.0");

                stmt = conn.createStatement();

               //res = (HiveQueryResultSet) stmt.executeQuery(sql);

                res = stmt.executeQuery(sql);

                List<List> list = new ArrayList<>();

                while (res.next()) {

                        System.out.println(res.getString(1));

                }

        }

总结&思考

他自己总结了一个CSDN可以参考一下 我觉得么有吧解决过程所以自己又发了一个,因为是我帮忙改的所以知道过程,希望大家不要只是学解决的方法,也要学解决问题的过程,这样遇到问题才有解决的思路解决的方案,不然百度不出来的东西多了,百度解决不了,你也解决不了吗?我发的日志十有八九是原创的,而大部分百度都百度不出来,都是实际问题的解决。 最后说一句这里用的hive-jdbc1.1.0要有及其他版本,只换这个jar包即可,当然hadoop2.6以上的不一定支持,建议使用对应版本分开存放不易出错