hive表增量抽取到oracle数据库的通用程序(一)
sqoop在export的时候 只能通过--export-dir参数来指定hdfs的路径。而目前的需求是需要将hive中某个表中的多个分区记录一次性导出到oracle数据库中,由于不支持通配符,又不想设置多个workflow。为了替代蹩脚的sqoop,准备使用java来开发通用包来替代这个导出功能。
通过给java程序提供具体的参数,完成数据的拉取。
为了与sqoop传参方式一致,使用了--开头(在java程序中其实是将--过滤掉了),相关的参数说明如下:
--hive_driver hive的驱动
--hive_url hiveserver2的连接url
--hive_username 连接hive2的用户名
--hive_password 连接hive2的密码
--hive_hql 要查询的hql语句
--rdms_driver 要导入到的关系型数据库的驱动
--rdms_url 关系型数据库的连接url
--rdms_username 关系型数据库的用户名
--rdms_password 关系型数据库的密码
--rdms_tableName 关系型数据库的表名
--rdms_columnNames 关系型数据库要插入的字段名
--rdms_presql 关系型数据库的预处理sql语句,可用于 导入数据前先执行删除记录,防止重复导入。
com.gw.exe.Hive2RMDS即为我自己定义的java程序。
package com.gw.exe; import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.gw.exe.DBUtil; public class Hive2RMDS { private static Connection conn = null;
private static PreparedStatement ps = null;
private static ResultSet rs = null; // hive参数
private static String hive_driver = "org.apache.hive.jdbc.HiveDriver";
private static String hive_url = null;
private static String hive_username = "hive2";
private static String hive_password = "";
private static String hive_hql = null; // RDMS参数
private static String rdms_driver = "oracle.jdbc.driver.OracleDriver";
private static String rdms_url = null;
private static String rdms_username = null;
private static String rdms_password = null;
private static String rdms_presql = null;
private static String rdms_tableName = null;
private static String rdms_columnNames = null; public static void main(String[] args) throws Exception { Map<String,String> map = getParams(args);
init(map); //预先处理rdms sql
if(map.containsKey("rdms_presql")){
exePreSql();
} //
List<Map<String,String>> list = getHiveList();
if(list!=null && list.size() > 0){
insertRdms(list);
} else {
throw new Exception("hive中未查询到记录");
}
} private static void init(Map<String,String> map){
//通过反射将传进来的参数赋值给静态变量
String[] paramNames = {"hive_driver","hive_url","hive_username","hive_password","hive_hql",
"rdms_driver","rdms_url","rdms_username","rdms_password",
"rdms_presql","rdms_tableName","rdms_columnNames"};
Hive2RMDS h2r = new Hive2RMDS();
for(String paramName:paramNames){
if(map.containsKey(paramName)){
try {
Field field = Hive2RMDS.class.getDeclaredField(paramName);
field.setAccessible(true);
field.set(h2r, map.get(paramName));
} catch (IllegalArgumentException | IllegalAccessException |NoSuchFieldException | SecurityException e) {
e.printStackTrace();
}
}
} //打印参数值:
System.out.println("解析后参数如下:");
for(String paramName:paramNames){
if (map.containsKey(paramName)){
System.out.println("key:" + paramName + " value:" + map.get(paramName));
}
}
} //解析参数
private static Map<String,String> getParams(String[] params){
Map<String,String> map = new HashMap<String,String>();
String key = null;
String value = null;
for(int i=0;i<params.length; i++){
if(i%2==0){
key = params[i].startsWith("--") ? params[i].substring(2) : params[i];
}
if(i%2==1){
value = params[i];
map.put(key, value);
key = null; value = null;
} }
return map;
} //获取hive记录
private static List<Map<String,String>> getHiveList(){ String[] columns = rdms_columnNames.split(",");
List<Map<String,String>> list = null;
try {
conn = DBUtil.getConnection(hive_driver,hive_url,hive_username,hive_password); // ps = conn.prepareStatement(hive_hql);
Statement ps = conn.createStatement();
rs = ps.executeQuery(hive_hql); list = new ArrayList<Map<String,String>>();
while(rs.next()){
Map<String,String> map = new HashMap<String,String>();
for(String column:columns){
String value = rs.getString(column);
map.put(column, value);
// System.out.println("column:" + column + " value:" + value);
} list.add(map);
}
} catch (SQLException e) {
e.printStackTrace();
// System.exit(1);
} finally {
DBUtil.close(rs, ps, conn);
}
System.out.println("hive中获取到记录数:" + (list!=null?list.size():0));
return list; } //写入rdms
private static void insertRdms(List<Map<String,String>> list){ try {
String[] columns = rdms_columnNames.split(",");
conn = DBUtil.getConnection(rdms_driver,rdms_url,rdms_username,rdms_password);
System.out.println("Connect:" + conn.toString());
//插入oracle
String sql = getInsertSqlString(rdms_tableName, rdms_columnNames.toUpperCase());
System.out.println("sql:" + sql); conn.setAutoCommit(false); ps = conn.prepareStatement(sql);
for(Map<String,String> map:list){
for(int i=1; i<= columns.length;i++){
ps.setObject(i, map.get(columns[i-1]));
}
ps.addBatch();
} int[] result = ps.executeBatch();
conn.commit(); System.out.println("insert : " + result.length);
//提交,设置事务初始值
} catch (SQLException e) {
e.printStackTrace();
} finally{
DBUtil.close(rs, ps, conn);
} } //根据表名和字段拼接insert sql
private static String getInsertSqlString(String tableName,String fieldNames){
int size = fieldNames.split(",").length;
StringBuffer sb = new StringBuffer("");
sb.append("insert into ").append(tableName).append("(").append(fieldNames).append(")")
.append("values(").append(String.join(",",Collections.nCopies(size,"?"))).append(")");
return sb.toString();
} //预先执行的sql
private static void exePreSql(){
Statement statement = null;
try {
conn = DBUtil.getConnection(rdms_driver,rdms_url,rdms_username,rdms_password);
statement = conn.createStatement();
int size = statement.executeUpdate(rdms_presql);
System.out.println("pre sql process record size : " + size);
} catch (SQLException e) {
e.printStackTrace();
} finally{
try {
if(statement != null){statement.close();}
}catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn != null){conn.close();}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
数据库连接 DBUtil.java
package com.gw.exe; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement; public class DBUtil { private static Connection conn = null; // 得到连接
public static Connection getConnection(String driver,String url,String username,String passwd) {
try
{
Class.forName(driver);
conn = DriverManager.getConnection(url, username, passwd);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
} // 关闭资源
public static void close(ResultSet rs, Statement ps, Connection conn) {
// 关闭资源(先开后关)
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
ps = null;
}
if (null != conn) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
} }
程序在执行过程中需要依赖hive的相关jar包,以下为最小依赖包(使用过程中缺少什么包,自己添加):
exehive2.jar是开发 的程序包。

说明:
1. 由于是将查询的结果一次性保存到List列表中,比较适合小规模的数据导出到关系型数据库中,比如几万记录,在导入到oracle中由于开启了批处理,因此效率很高。
可以自行修改程序,增加一个阈值比如10万,在获取hive结果时,增加判断,达到10万记录提交一次到oracle,不足10万条记录直接提交就可以了。
2. 没有update功能,使用时,可以先删除,后insert。
3. 程序应用版本: jdk1.8, hadoop2.7.3,hive.2.3.0 oozie4.3.0
hive表增量抽取到oracle数据库的通用程序(一)的更多相关文章
- hive表增量抽取到oracle数据库的通用程序(二)
hive表增量抽取到oracle数据库的通用程序(一) 前一篇介绍了java程序的如何编写.使用以及引用到的依赖包.这篇接着上一篇来介绍如何在oozie中使用该java程序. 在我的业务中,分为两段: ...
- hive表增量抽取到mysql(关系数据库)的通用程序(三)
hive表增量抽取到oracle数据库的通用程序(一) hive表增量抽取到oracle数据库的通用程序(二) 这几天又用到了该功能了,所以又改进了一版,增加了全量抽取和批量抽取两个参数.并且可以设置 ...
- 在.NET开发面向Oracle数据库的应用程序
其实这个不是一个什么新的话题.但是之前在多次项目中,总是遇到大家针对Oracle数据库的访问时,会有各种各样的问题,最基本的就是要在客户端安装各种client,版本不一样的话还有各种问题. 静下心来看 ...
- oozie4.3.0+sqoop1.4.6实现mysql到hive的增量抽取
1.准备数据源 mysql中表bigdata,数据如下: 2. 准备目标表 目标表存放hive中数据库dw_stg表bigdata 保存路径为 hdfs://localhost:9000/user/h ...
- 一条sql,有分页,表合并查询,多表连接,用于oracle数据库
SELECT * FROM ( SELECT TT.*,ROWNUM RN FROM ( SELECT A.CASE_ID AS TREATID, A.TYPE AS TYPE, B.CONTENT ...
- Oracle数据库表分区
一.Oracle数据库表分区概念和理解 1.1.已经存在的表没有方法可以直接转化为分区表. 1.2.不在分区字段上建立分区索引,在别的字段上建立索引相当于全局索引.效率 ...
- Oracle数据库不能创建表空间及表中文乱码问题
1.不能创建表空间问题 datafile为表空间的存放位置,没有将表空间存放路径指定为orcl数据库时,创建表空间出错如下 查看自己的Oracle安装位置,我的Oracle10g安装在虚拟XP系统中, ...
- oracle 数据库,能不能将查询的结果创建成新表。
这个是可以的.sql:create table tablename1 as select t2. * from tablename2 t2 where t2.filename =‘张三’. 解释:就是 ...
- ETL中的数据增量抽取机制
ETL中的数据增量抽取机制 ( 增量抽取是数据仓库ETL(extraction,transformation,loading,数据的抽取.转换和装载)实施过程中需要重点考虑的问 题.在ETL过 ...
随机推荐
- nodejs 遍历文件夹下所有的图片改名为中文
安装依赖 $ npm init -y && npm i fs-extra globby request -S main.js const fs = require('node-fs-e ...
- python标准库介绍——35 pipes 模块详解
==pipes 模块== (只用于 Unix) ``pipes`` 模块提供了 "转换管道 (conversion pipelines)" 的支持. 你可以创建包含许多外部工具调用 ...
- MySQL自成一派的查询提示
[查询提示] MySQL中可以给select语句各种提示,比如告诉它“查询的结果集特别大,请直接用磁盘临时表”,“请让这条select优先执行” .... [查询提示:与结果集相关] 与结果集相关的查 ...
- private static final long serialVersionUID = 1L;
作者:郭无心链接:https://www.zhihu.com/question/24852886/answer/117314768来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...
- c#中lock的使用(用于预约超出限额的流程)
一个项目,预约系统,核心二张表:预约表,预约限额表 用户点击预约按钮后, 1. 先select 预约限额表把该预约时间段的限额取出来, 2. 再select 预约表把该预约时间已经预约上的次数算出来 ...
- Atitit 软件项目系统托盘图标解决方案
Atitit 软件项目系统托盘图标解决方案 1.1. Nodejs node-webkit还实现了本地化的API,例如菜单栏,系统的托盘图标支持1 1.2. Java c# qt c++1 1.3 ...
- Atitit 个人信息数据文档知识分类
Atitit 个人信息数据文档知识分类 1.1. 知识分类法,参照图书分类法 1 2. Attilax知识分类 2 2.1. 公共文档(一般技术资料,通过标题可以网上搜索到的) 2 2.2. sum ...
- iPhone-获取网络数据或者路径的文件名
Phone中,在网络中的数据流中提取链接中的文件名称时,有很多方法,这里总结一些. 方法一:最直接. 1 NSString * urlString = @"http://www.ba ...
- 【Unity】第10章 Mecanim动画系统
分类:Unity.C#.VS2015 创建日期:2016-05-02 一.简介 Unity提供了两种动画系统:一种是早期版本提供的旧版(Legacy)动画系统,旧版本(Legacy)以后将逐步被淘汰掉 ...
- mysql中tinyint、smallint、int、bigint的区别
tinyint 从 -2^7 (-128) 到 2^7 - 1 (123) 的整型数据.存储大小为 1 个字节. unsigned 是从 0 到 255 的整型数据. 所以建表的时候 只能是tinyi ...