本文已参与「新人创作礼」活动,一起开启掘金创作之路。
动态加载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))
原驱动包
源代码
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文件应该是
注意:我这里没有用同一版本,最好用集群中最高版本(如果向下皆兼容的话),不行就用最低版本,再不行只能一个个试了,我这个亲测可用,兼容CHD中hadoop2.0-2.6版本,动态加载的原因是我要管理多个集群。
但是放在服务器上依然报错:
跟入代码发现是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以上的不一定支持,建议使用对应版本分开存放不易出错