• DataX二次开发——(4)新增hivereader、hivewriter


    1、修改根目录的pom文件

    新增hivereader、hivewriter

    1. <modules>
    2. <module>commonmodule>
    3. <module>coremodule>
    4. <module>transformermodule>
    5. <module>mysqlreadermodule>
    6. <module>drdsreadermodule>
    7. <module>sqlserverreadermodule>
    8. <module>postgresqlreadermodule>
    9. <module>kingbaseesreadermodule>
    10. <module>oraclereadermodule>
    11. <module>odpsreadermodule>
    12. <module>otsreadermodule>
    13. <module>otsstreamreadermodule>
    14. <module>txtfilereadermodule>
    15. <module>hdfsreadermodule>
    16. <module>streamreadermodule>
    17. <module>ossreadermodule>
    18. <module>ftpreadermodule>
    19. <module>mongodbreadermodule>
    20. <module>rdbmsreadermodule>
    21. <module>hbase11xreadermodule>
    22. <module>hbase094xreadermodule>
    23. <module>tsdbreadermodule>
    24. <module>opentsdbreadermodule>
    25. <module>cassandrareadermodule>
    26. <module>gdbreadermodule>
    27. <module>oceanbasev10readermodule>
    28. <module>hivereadermodule>
    29. <module>mysqlwritermodule>
    30. <module>tdenginewritermodule>
    31. <module>drdswritermodule>
    32. <module>odpswritermodule>
    33. <module>txtfilewritermodule>
    34. <module>ftpwritermodule>
    35. <module>hdfswritermodule>
    36. <module>streamwritermodule>
    37. <module>otswritermodule>
    38. <module>oraclewritermodule>
    39. <module>sqlserverwritermodule>
    40. <module>postgresqlwritermodule>
    41. <module>kingbaseeswritermodule>
    42. <module>osswritermodule>
    43. <module>mongodbwritermodule>
    44. <module>adswritermodule>
    45. <module>ocswritermodule>
    46. <module>rdbmswritermodule>
    47. <module>hbase11xwritermodule>
    48. <module>hbase094xwritermodule>
    49. <module>hbase11xsqlwritermodule>
    50. <module>hbase11xsqlreadermodule>
    51. <module>elasticsearchwritermodule>
    52. <module>tsdbwritermodule>
    53. <module>adbpgwritermodule>
    54. <module>gdbwritermodule>
    55. <module>cassandrawritermodule>
    56. <module>clickhousewritermodule>
    57. <module>oscarwritermodule>
    58. <module>oceanbasev10writermodule>
    59. <module>plugin-rdbms-utilmodule>
    60. <module>plugin-unstructured-storage-utilmodule>
    61. <module>hbase20xsqlreadermodule>
    62. <module>hbase20xsqlwritermodule>
    63. <module>kuduwritermodule>
    64. <module>tdenginereadermodule>
    65. <module>hivewritermodule>
    66. modules>

    2、修改根目录的package.xml

    新增hivereader、hivewriter的打包依赖

    1. <fileSet>
    2. <directory>hivereader/target/datax/directory>
    3. <includes>
    4. <include>**/*.*include>
    5. includes>
    6. <outputDirectory>dataxoutputDirectory>
    7. fileSet>
    8. <fileSet>
    9. <directory>hivewriter/target/datax/directory>
    10. <includes>
    11. <include>**/*.*include>
    12. includes>
    13. <outputDirectory>dataxoutputDirectory>
    14. fileSet>

    3、新建hivereader模块

    项目结构

     package.xml

    1. <assembly
    2. xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
    5. <id>id>
    6. <formats>
    7. <format>dirformat>
    8. formats>
    9. <includeBaseDirectory>falseincludeBaseDirectory>
    10. <fileSets>
    11. <fileSet>
    12. <directory>src/main/resourcesdirectory>
    13. <includes>
    14. <include>plugin.jsoninclude>
    15. <include>plugin_job_template.jsoninclude>
    16. includes>
    17. <outputDirectory>plugin/reader/hivereaderoutputDirectory>
    18. fileSet>
    19. <fileSet>
    20. <directory>target/directory>
    21. <includes>
    22. <include>hivereader-0.0.1-SNAPSHOT.jarinclude>
    23. includes>
    24. <outputDirectory>plugin/reader/hivereaderoutputDirectory>
    25. fileSet>
    26. fileSets>
    27. <dependencySets>
    28. <dependencySet>
    29. <useProjectArtifact>falseuseProjectArtifact>
    30. <outputDirectory>plugin/reader/hivereader/libsoutputDirectory>
    31. <scope>runtimescope>
    32. dependencySet>
    33. dependencySets>
    34. assembly>

    Constant.class

    1. package com.alibaba.datax.plugin.reader.hivereader;
    2. public class Constant {
    3. public final static String TEMP_DATABASE_DEFAULT = "default"; // 参考CDH的default库
    4. public static final String TEMP_DATABSE_HDFS_LOCATION_DEFAULT = "/user/{username}/warehouse/";// 参考CDH的default库的路径
    5. public static final String TEMP_TABLE_NAME_PREFIX="tmp_datax_hivereader_";
    6. // public final static String HIVE_CMD_DEFAULT = "hive"; //
    7. public final static String HIVE_SQL_SET_DEFAULT = ""; //
    8. public final static String FIELDDELIMITER_DEFAULT = "\\u0001"; //
    9. public final static String NULL_FORMAT_DEFAULT="\\N" ;
    10. public static final String TEXT = "TEXT";
    11. public static final String ORC = "ORC";
    12. public static final String CSV = "CSV";
    13. public static final String SEQ = "SEQ";
    14. public static final String RC = "RC";
    15. }

    DFSUtil.class

    1. package com.alibaba.datax.plugin.reader.hivereader;
    2. import com.alibaba.datax.common.element.*;
    3. import com.alibaba.datax.common.exception.DataXException;
    4. import com.alibaba.datax.common.plugin.RecordSender;
    5. import com.alibaba.datax.common.plugin.TaskPluginCollector;
    6. import com.alibaba.datax.common.util.Configuration;
    7. import com.alibaba.datax.plugin.unstructuredstorage.reader.ColumnEntry;
    8. import com.alibaba.datax.plugin.unstructuredstorage.reader.UnstructuredStorageReaderErrorCode;
    9. import com.alibaba.datax.plugin.unstructuredstorage.reader.UnstructuredStorageReaderUtil;
    10. import com.alibaba.fastjson.JSON;
    11. import com.alibaba.fastjson.JSONObject;
    12. import org.apache.commons.lang3.StringUtils;
    13. import org.apache.hadoop.fs.FSDataInputStream;
    14. import org.apache.hadoop.fs.FileStatus;
    15. import org.apache.hadoop.fs.FileSystem;
    16. import org.apache.hadoop.fs.Path;
    17. import org.apache.hadoop.hive.ql.io.RCFile;
    18. import org.apache.hadoop.hive.ql.io.RCFileRecordReader;
    19. import org.apache.hadoop.hive.ql.io.orc.OrcFile;
    20. import org.apache.hadoop.hive.ql.io.orc.OrcInputFormat;
    21. import org.apache.hadoop.hive.ql.io.orc.OrcSerde;
    22. import org.apache.hadoop.hive.ql.io.orc.Reader;
    23. import org.apache.hadoop.hive.serde2.columnar.BytesRefArrayWritable;
    24. import org.apache.hadoop.hive.serde2.columnar.BytesRefWritable;
    25. import org.apache.hadoop.hive.serde2.objectinspector.StructField;
    26. import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
    27. import org.apache.hadoop.io.*;
    28. import org.apache.hadoop.mapred.*;
    29. import org.apache.hadoop.security.UserGroupInformation;
    30. import org.apache.hadoop.util.ReflectionUtils;
    31. import org.slf4j.Logger;
    32. import org.slf4j.LoggerFactory;
    33. import java.io.IOException;
    34. import java.io.InputStream;
    35. import java.net.URI;
    36. import java.net.URISyntaxException;
    37. import java.nio.ByteBuffer;
    38. import java.text.SimpleDateFormat;
    39. import java.util.*;
    40. public class DFSUtil {
    41. private static final Logger LOG = LoggerFactory.getLogger(HiveReader.Job.class);
    42. private org.apache.hadoop.conf.Configuration hadoopConf = null;
    43. private String username = null;
    44. private String specifiedFileType = null;
    45. private Boolean haveKerberos = false;
    46. private String kerberosKeytabFilePath;
    47. private String kerberosPrincipal;
    48. private static final int DIRECTORY_SIZE_GUESS = 16 * 1024;
    49. public static final String HDFS_DEFAULTFS_KEY = "fs.defaultFS";
    50. public static final String HADOOP_SECURITY_AUTHENTICATION_KEY = "hadoop.security.authentication";
    51. public DFSUtil(Configuration taskConfig) {
    52. hadoopConf = new org.apache.hadoop.conf.Configuration();
    53. //io.file.buffer.size 性能参数
    54. //http://blog.csdn.net/yangjl38/article/details/7583374
    55. Configuration hadoopSiteParams = taskConfig.getConfiguration(Key.HADOOP_CONFIG);
    56. JSONObject hadoopSiteParamsAsJsonObject = JSON.parseObject(taskConfig.getString(Key.HADOOP_CONFIG));
    57. if (null != hadoopSiteParams) {
    58. Set paramKeys = hadoopSiteParams.getKeys();
    59. for (String each : paramKeys) {
    60. hadoopConf.set(each, hadoopSiteParamsAsJsonObject.getString(each));
    61. }
    62. }
    63. hadoopConf.set(HDFS_DEFAULTFS_KEY, taskConfig.getString(Key.DEFAULT_FS));
    64. this.username = taskConfig.getString(Key.USERNAME);
    65. System.setProperty("HADOOP_USER_NAME", this.username);
    66. //是否有Kerberos认证
    67. this.haveKerberos = taskConfig.getBool(Key.HAVE_KERBEROS, false);
    68. if (haveKerberos) {
    69. this.kerberosKeytabFilePath = taskConfig.getString(Key.KERBEROS_KEYTAB_FILE_PATH);
    70. this.kerberosPrincipal = taskConfig.getString(Key.KERBEROS_PRINCIPAL);
    71. this.hadoopConf.set(HADOOP_SECURITY_AUTHENTICATION_KEY, "kerberos");
    72. }
    73. this.kerberosAuthentication(this.kerberosPrincipal, this.kerberosKeytabFilePath);
    74. LOG.info(String.format("hadoopConfig details:%s", JSON.toJSONString(this.hadoopConf)));
    75. }
    76. private void kerberosAuthentication(String kerberosPrincipal, String kerberosKeytabFilePath) {
    77. if (haveKerberos && StringUtils.isNotBlank(this.kerberosPrincipal) && StringUtils.isNotBlank(this.kerberosKeytabFilePath)) {
    78. UserGroupInformation.setConfiguration(this.hadoopConf);
    79. try {
    80. UserGroupInformation.loginUserFromKeytab(kerberosPrincipal, kerberosKeytabFilePath);
    81. } catch (Exception e) {
    82. String message = String.format("kerberos认证失败,请确定kerberosKeytabFilePath[%s]和kerberosPrincipal[%s]填写正确",
    83. kerberosKeytabFilePath, kerberosPrincipal);
    84. throw DataXException.asDataXException(HiveReaderErrorCode.KERBEROS_LOGIN_ERROR, message, e);
    85. }
    86. }
    87. }
    88. /**
    89. * 获取指定路径列表下符合条件的所有文件的绝对路径
    90. *
    91. * @param srcPaths 路径列表
    92. * @param specifiedFileType 指定文件类型
    93. */
    94. public HashSet getAllFiles(List srcPaths, String specifiedFileType) {
    95. this.specifiedFileType = specifiedFileType;
    96. if (!srcPaths.isEmpty()) {
    97. for (String eachPath : srcPaths) {
    98. LOG.info(String.format("get HDFS all files in path = [%s]", eachPath));
    99. getHDFSAllFiles(eachPath);
    100. }
    101. }
    102. return sourceHDFSAllFilesList;
    103. }
    104. private HashSet sourceHDFSAllFilesList = new HashSet();
    105. public HashSet getHDFSAllFiles(String hdfsPath) {
    106. try {
    107. FileSystem hdfs = FileSystem.get(new URI(hadoopConf.get(HDFS_DEFAULTFS_KEY)),hadoopConf,username);
    108. //判断hdfsPath是否包含正则符号
    109. if (hdfsPath.contains("*") || hdfsPath.contains("?")) {
    110. Path path = new Path(hdfsPath);
    111. FileStatus stats[] = hdfs.globStatus(path);
    112. for (FileStatus f : stats) {
    113. if (f.isFile()) {
    114. if (f.getLen() == 0) {
    115. String message = String.format("文件[%s]长度为0,将会跳过不作处理!", hdfsPath);
    116. LOG.warn(message);
    117. } else {
    118. addSourceFileByType(f.getPath().toString());
    119. }
    120. } else if (f.isDirectory()) {
    121. getHDFSAllFilesNORegex(f.getPath().toString(), hdfs);
    122. }
    123. }
    124. } else {
    125. getHDFSAllFilesNORegex(hdfsPath, hdfs);
    126. }
    127. return sourceHDFSAllFilesList;
    128. } catch (IOException | InterruptedException | URISyntaxException e) {
    129. String message = String.format("无法读取路径[%s]下的所有文件,请确认您的配置项fs.defaultFS, path的值是否正确," +
    130. "是否有读写权限,网络是否已断开!", hdfsPath);
    131. LOG.error(message);
    132. throw DataXException.asDataXException(HiveReaderErrorCode.PATH_CONFIG_ERROR, e);
    133. }
    134. }
    135. private HashSet getHDFSAllFilesNORegex(String path, FileSystem hdfs) throws IOException {
    136. // 获取要读取的文件的根目录
    137. Path listFiles = new Path(path);
    138. // If the network disconnected, this method will retry 45 times
    139. // each time the retry interval for 20 seconds
    140. // 获取要读取的文件的根目录的所有二级子文件目录
    141. FileStatus stats[] = hdfs.listStatus(listFiles);
    142. for (FileStatus f : stats) {
    143. // 判断是不是目录,如果是目录,递归调用
    144. if (f.isDirectory()) {
    145. LOG.info(String.format("[%s] 是目录, 递归获取该目录下的文件", f.getPath().toString()));
    146. getHDFSAllFilesNORegex(f.getPath().toString(), hdfs);
    147. } else if (f.isFile()) {
    148. addSourceFileByType(f.getPath().toString());
    149. } else {
    150. String message = String.format("该路径[%s]文件类型既不是目录也不是文件,插件自动忽略。",
    151. f.getPath().toString());
    152. LOG.info(message);
    153. }
    154. }
    155. return sourceHDFSAllFilesList;
    156. }
    157. // 根据用户指定的文件类型,将指定的文件类型的路径加入sourceHDFSAllFilesList
    158. private void addSourceFileByType(String filePath) {
    159. // 检查file的类型和用户配置的fileType类型是否一致
    160. boolean isMatchedFileType = checkHdfsFileType(filePath, this.specifiedFileType);
    161. if (isMatchedFileType) {
    162. LOG.info(String.format("[%s]是[%s]类型的文件, 将该文件加入source files列表", filePath, this.specifiedFileType));
    163. sourceHDFSAllFilesList.add(filePath);
    164. } else {
    165. String message = String.format("文件[%s]的类型与用户配置的fileType类型不一致," +
    166. "请确认您配置的目录下面所有文件的类型均为[%s]"
    167. , filePath, this.specifiedFileType);
    168. LOG.error(message);
    169. throw DataXException.asDataXException(
    170. HiveReaderErrorCode.FILE_TYPE_UNSUPPORT, message);
    171. }
    172. }
    173. public InputStream getInputStream(String filepath) {
    174. InputStream inputStream;
    175. Path path = new Path(filepath);
    176. try {
    177. FileSystem fs = FileSystem.get(new URI(hadoopConf.get(HDFS_DEFAULTFS_KEY)),hadoopConf,username);
    178. //If the network disconnected, this method will retry 45 times
    179. //each time the retry interval for 20 seconds
    180. inputStream = fs.open(path);
    181. return inputStream;
    182. } catch (IOException | URISyntaxException | InterruptedException e) {
    183. String message = String.format("读取文件 : [%s] 时出错,请确认文件:[%s]存在且配置的用户有权限读取", filepath, filepath);
    184. throw DataXException.asDataXException(HiveReaderErrorCode.READ_FILE_ERROR, message, e);
    185. }
    186. }
    187. public void sequenceFileStartRead(String sourceSequenceFilePath, Configuration readerSliceConfig,
    188. RecordSender recordSender, TaskPluginCollector taskPluginCollector) {
    189. LOG.info(String.format("Start Read sequence file [%s].", sourceSequenceFilePath));
    190. Path seqFilePath = new Path(sourceSequenceFilePath);
    191. SequenceFile.Reader reader = null;
    192. try {
    193. //获取SequenceFile.Reader实例
    194. reader = new SequenceFile.Reader(this.hadoopConf,
    195. SequenceFile.Reader.file(seqFilePath));
    196. //获取key 与 value
    197. Writable key = (Writable) ReflectionUtils.newInstance(reader.getKeyClass(), this.hadoopConf);
    198. Text value = new Text();
    199. while (reader.next(key, value)) {
    200. if (StringUtils.isNotBlank(value.toString())) {
    201. UnstructuredStorageReaderUtil.transportOneRecord(recordSender,
    202. readerSliceConfig, taskPluginCollector, value.toString());
    203. }
    204. }
    205. } catch (Exception e) {
    206. String message = String.format("SequenceFile.Reader读取文件[%s]时出错", sourceSequenceFilePath);
    207. LOG.error(message);
    208. throw DataXException.asDataXException(HiveReaderErrorCode.READ_SEQUENCEFILE_ERROR, message, e);
    209. } finally {
    210. IOUtils.closeStream(reader);
    211. LOG.info("Finally, Close stream SequenceFile.Reader.");
    212. }
    213. }
    214. public void rcFileStartRead(String sourceRcFilePath, Configuration readerSliceConfig,
    215. RecordSender recordSender, TaskPluginCollector taskPluginCollector) {
    216. LOG.info(String.format("Start Read rcfile [%s].", sourceRcFilePath));
    217. List column = UnstructuredStorageReaderUtil
    218. .getListColumnEntry(readerSliceConfig, com.alibaba.datax.plugin.unstructuredstorage.reader.Key.COLUMN);
    219. // warn: no default value '\N'
    220. String nullFormat = readerSliceConfig.getString(com.alibaba.datax.plugin.unstructuredstorage.reader.Key.NULL_FORMAT);
    221. Path rcFilePath = new Path(sourceRcFilePath);
    222. FileSystem fs = null;
    223. RCFileRecordReader recordReader = null;
    224. try {
    225. fs = FileSystem.get(rcFilePath.toUri(), hadoopConf,username);
    226. long fileLen = fs.getFileStatus(rcFilePath).getLen();
    227. FileSplit split = new FileSplit(rcFilePath, 0, fileLen, (String[]) null);
    228. recordReader = new RCFileRecordReader(hadoopConf, split);
    229. LongWritable key = new LongWritable();
    230. BytesRefArrayWritable value = new BytesRefArrayWritable();
    231. Text txt = new Text();
    232. while (recordReader.next(key, value)) {
    233. String[] sourceLine = new String[value.size()];
    234. txt.clear();
    235. for (int i = 0; i < value.size(); i++) {
    236. BytesRefWritable v = value.get(i);
    237. txt.set(v.getData(), v.getStart(), v.getLength());
    238. sourceLine[i] = txt.toString();
    239. }
    240. UnstructuredStorageReaderUtil.transportOneRecord(recordSender,
    241. column, sourceLine, nullFormat, taskPluginCollector);
    242. }
    243. } catch (IOException | InterruptedException e) {
    244. String message = String.format("读取文件[%s]时出错", sourceRcFilePath);
    245. LOG.error(message);
    246. throw DataXException.asDataXException(HiveReaderErrorCode.READ_RCFILE_ERROR, message, e);
    247. } finally {
    248. try {
    249. if (recordReader != null) {
    250. recordReader.close();
    251. LOG.info("Finally, Close RCFileRecordReader.");
    252. }
    253. } catch (IOException e) {
    254. LOG.warn(String.format("finally: 关闭RCFileRecordReader失败, %s", e.getMessage()));
    255. }
    256. }
    257. }
    258. public void orcFileStartRead(String sourceOrcFilePath, Configuration readerSliceConfig,
    259. RecordSender recordSender, TaskPluginCollector taskPluginCollector) {
    260. LOG.info(String.format("Start Read orcfile [%s].", sourceOrcFilePath));
    261. List column = UnstructuredStorageReaderUtil
    262. .getListColumnEntry(readerSliceConfig, com.alibaba.datax.plugin.unstructuredstorage.reader.Key.COLUMN);
    263. String nullFormat = readerSliceConfig.getString(com.alibaba.datax.plugin.unstructuredstorage.reader.Key.NULL_FORMAT);
    264. StringBuilder allColumns = new StringBuilder();
    265. StringBuilder allColumnTypes = new StringBuilder();
    266. boolean isReadAllColumns = false;
    267. int columnIndexMax = -1;
    268. // 判断是否读取所有列
    269. if (null == column || column.size() == 0) {
    270. int allColumnsCount = getAllColumnsCount(sourceOrcFilePath);
    271. columnIndexMax = allColumnsCount - 1;
    272. isReadAllColumns = true;
    273. } else {
    274. columnIndexMax = getMaxIndex(column);
    275. }
    276. for (int i = 0; i <= columnIndexMax; i++) {
    277. allColumns.append("col");
    278. allColumnTypes.append("string");
    279. if (i != columnIndexMax) {
    280. allColumns.append(",");
    281. allColumnTypes.append(":");
    282. }
    283. }
    284. if (columnIndexMax >= 0) {
    285. JobConf conf = new JobConf(hadoopConf);
    286. Path orcFilePath = new Path(sourceOrcFilePath);
    287. Properties p = new Properties();
    288. p.setProperty("columns", allColumns.toString());
    289. p.setProperty("columns.types", allColumnTypes.toString());
    290. try {
    291. OrcSerde serde = new OrcSerde();
    292. serde.initialize(conf, p);
    293. StructObjectInspector inspector = (StructObjectInspector) serde.getObjectInspector();
    294. InputFormat in = new OrcInputFormat();
    295. FileInputFormat.setInputPaths(conf, orcFilePath.toString());
    296. //If the network disconnected, will retry 45 times, each time the retry interval for 20 seconds
    297. //Each file as a split
    298. //TODO multy threads
    299. InputSplit[] splits = in.getSplits(conf, 1);
    300. RecordReader reader = in.getRecordReader(splits[0], conf, Reporter.NULL);//获取reader
    301. Object key = reader.createKey();
    302. Object value = reader.createValue();// OrcStruct
    303. // 获取列信息
    304. Listextends StructField> fields = inspector.getAllStructFieldRefs();
    305. List recordFields;
    306. while (reader.next(key, value)) {//next 读取数据到 value(OrcStruct)
    307. recordFields = new ArrayList();
    308. for (int i = 0; i <= columnIndexMax; i++) {
    309. Object field = inspector.getStructFieldData(value, fields.get(i));//从 OrcStruct 数组中 返回对应列 数据
    310. recordFields.add(field);
    311. }
    312. transportOneRecord(column, recordFields, recordSender,
    313. taskPluginCollector, isReadAllColumns, nullFormat);
    314. }
    315. reader.close();
    316. } catch (Exception e) {
    317. String message = String.format("从orcfile文件路径[%s]中读取数据发生异常,请联系系统管理员。"
    318. , sourceOrcFilePath);
    319. LOG.error(message);
    320. throw DataXException.asDataXException(HiveReaderErrorCode.READ_FILE_ERROR, message);
    321. }
    322. } else {
    323. String message = String.format("请确认您所读取的列配置正确!columnIndexMax 小于0,column:%s", JSON.toJSONString(column));
    324. throw DataXException.asDataXException(HiveReaderErrorCode.BAD_CONFIG_VALUE, message);
    325. }
    326. }
    327. private Record transportOneRecord(List columnConfigs, List recordFields
    328. , RecordSender recordSender, TaskPluginCollector taskPluginCollector, boolean isReadAllColumns, String nullFormat) {
    329. Record record = recordSender.createRecord();
    330. Column columnGenerated;
    331. try {
    332. if (isReadAllColumns) {
    333. // 读取所有列,创建都为String类型的column
    334. for (Object recordField : recordFields) {
    335. String columnValue = null;
    336. if (recordField != null) {
    337. columnValue = recordField.toString();
    338. }
    339. columnGenerated = new StringColumn(columnValue);
    340. record.addColumn(columnGenerated);
    341. }
    342. } else {
    343. for (ColumnEntry columnConfig : columnConfigs) {
    344. String columnType = columnConfig.getType();
    345. Integer columnIndex = columnConfig.getIndex();
    346. String columnConst = columnConfig.getValue();
    347. String columnValue = null;
    348. if (null != columnIndex) {
    349. if (null != recordFields.get(columnIndex))
    350. columnValue = recordFields.get(columnIndex).toString();
    351. } else {
    352. columnValue = columnConst;
    353. }
    354. Type type = Type.valueOf(columnType.toUpperCase());
    355. // it's all ok if nullFormat is null
    356. if (StringUtils.equals(columnValue, nullFormat)) {
    357. columnValue = null;
    358. }
    359. switch (type) {
    360. case STRING:
    361. columnGenerated = new StringColumn(columnValue);
    362. break;
    363. case LONG:
    364. try {
    365. columnGenerated = new LongColumn(columnValue);
    366. } catch (Exception e) {
    367. throw new IllegalArgumentException(String.format(
    368. "类型转换错误, 无法将[%s] 转换为[%s]", columnValue,
    369. "LONG"));
    370. }
    371. break;
    372. case DOUBLE:
    373. try {
    374. columnGenerated = new DoubleColumn(columnValue);
    375. } catch (Exception e) {
    376. throw new IllegalArgumentException(String.format(
    377. "类型转换错误, 无法将[%s] 转换为[%s]", columnValue,
    378. "DOUBLE"));
    379. }
    380. break;
    381. case BOOLEAN:
    382. try {
    383. columnGenerated = new BoolColumn(columnValue);
    384. } catch (Exception e) {
    385. throw new IllegalArgumentException(String.format(
    386. "类型转换错误, 无法将[%s] 转换为[%s]", columnValue,
    387. "BOOLEAN"));
    388. }
    389. break;
    390. case DATE:
    391. try {
    392. if (columnValue == null) {
    393. columnGenerated = new DateColumn((Date) null);
    394. } else {
    395. String formatString = columnConfig.getFormat();
    396. if (StringUtils.isNotBlank(formatString)) {
    397. // 用户自己配置的格式转换
    398. SimpleDateFormat format = new SimpleDateFormat(
    399. formatString);
    400. columnGenerated = new DateColumn(
    401. format.parse(columnValue));
    402. } else {
    403. // 框架尝试转换
    404. columnGenerated = new DateColumn(
    405. new StringColumn(columnValue)
    406. .asDate());
    407. }
    408. }
    409. } catch (Exception e) {
    410. throw new IllegalArgumentException(String.format(
    411. "类型转换错误, 无法将[%s] 转换为[%s]", columnValue,
    412. "DATE"));
    413. }
    414. break;
    415. default:
    416. String errorMessage = String.format(
    417. "您配置的列类型暂不支持 : [%s]", columnType);
    418. LOG.error(errorMessage);
    419. throw DataXException
    420. .asDataXException(
    421. UnstructuredStorageReaderErrorCode.NOT_SUPPORT_TYPE,
    422. errorMessage);
    423. }
    424. record.addColumn(columnGenerated);
    425. }
    426. }
    427. recordSender.sendToWriter(record);
    428. } catch (IllegalArgumentException iae) {
    429. taskPluginCollector
    430. .collectDirtyRecord(record, iae.getMessage());
    431. } catch (IndexOutOfBoundsException ioe) {
    432. taskPluginCollector
    433. .collectDirtyRecord(record, ioe.getMessage());
    434. } catch (Exception e) {
    435. if (e instanceof DataXException) {
    436. throw (DataXException) e;
    437. }
    438. // 每一种转换失败都是脏数据处理,包括数字格式 & 日期格式
    439. taskPluginCollector.collectDirtyRecord(record, e.getMessage());
    440. }
    441. return record;
    442. }
    443. private int getAllColumnsCount(String filePath) {
    444. Path path = new Path(filePath);
    445. try {
    446. Reader reader = OrcFile.createReader(path, OrcFile.readerOptions(hadoopConf));
    447. return reader.getTypes().get(0).getSubtypesCount();
    448. } catch (IOException e) {
    449. String message = "读取orcfile column列数失败,请联系系统管理员";
    450. throw DataXException.asDataXException(HiveReaderErrorCode.READ_FILE_ERROR, message);
    451. }
    452. }
    453. private int getMaxIndex(List columnConfigs) {
    454. int maxIndex = -1;
    455. for (ColumnEntry columnConfig : columnConfigs) {
    456. Integer columnIndex = columnConfig.getIndex();
    457. if (columnIndex != null && columnIndex < 0) {
    458. String message = String.format("您column中配置的index不能小于0,请修改为正确的index,column配置:%s",
    459. JSON.toJSONString(columnConfigs));
    460. LOG.error(message);
    461. throw DataXException.asDataXException(HiveReaderErrorCode.CONFIG_INVALID_EXCEPTION, message);
    462. } else if (columnIndex != null && columnIndex > maxIndex) {
    463. maxIndex = columnIndex;
    464. }
    465. }
    466. return maxIndex;
    467. }
    468. private enum Type {
    469. STRING, LONG, BOOLEAN, DOUBLE, DATE,
    470. }
    471. public boolean checkHdfsFileType(String filepath, String specifiedFileType) {
    472. Path file = new Path(filepath);
    473. try {
    474. FileSystem fs = FileSystem.get(new URI(hadoopConf.get(HDFS_DEFAULTFS_KEY)),hadoopConf,username);
    475. FSDataInputStream in = fs.open(file);
    476. if (StringUtils.equalsIgnoreCase(specifiedFileType, Constant.CSV)
    477. || StringUtils.equalsIgnoreCase(specifiedFileType, Constant.TEXT)) {
    478. boolean isORC = isORCFile(file, fs, in);// 判断是否是 ORC File
    479. if (isORC) {
    480. return false;
    481. }
    482. boolean isRC = isRCFile(filepath, in);// 判断是否是 RC File
    483. if (isRC) {
    484. return false;
    485. }
    486. boolean isSEQ = isSequenceFile(filepath, in);// 判断是否是 Sequence File
    487. if (isSEQ) {
    488. return false;
    489. }
    490. // 如果不是ORC,RC和SEQ,则默认为是TEXT或CSV类型
    491. return !isORC && !isRC && !isSEQ;
    492. } else if (StringUtils.equalsIgnoreCase(specifiedFileType, Constant.ORC)) {
    493. return isORCFile(file, fs, in);
    494. } else if (StringUtils.equalsIgnoreCase(specifiedFileType, Constant.RC)) {
    495. return isRCFile(filepath, in);
    496. } else if (StringUtils.equalsIgnoreCase(specifiedFileType, Constant.SEQ)) {
    497. return isSequenceFile(filepath, in);
    498. }
    499. } catch (Exception e) {
    500. String message = String.format("检查文件[%s]类型失败,目前支持ORC,SEQUENCE,RCFile,TEXT,CSV五种格式的文件," +
    501. "请检查您文件类型和文件是否正确。", filepath);
    502. LOG.error(message);
    503. throw DataXException.asDataXException(HiveReaderErrorCode.READ_FILE_ERROR, message, e);
    504. }
    505. return false;
    506. }
    507. // 判断file是否是ORC File
    508. private boolean isORCFile(Path file, FileSystem fs, FSDataInputStream in) {
    509. try {
    510. // figure out the size of the file using the option or filesystem
    511. long size = fs.getFileStatus(file).getLen();
    512. //read last bytes into buffer to get PostScript
    513. int readSize = (int) Math.min(size, DIRECTORY_SIZE_GUESS);
    514. in.seek(size - readSize);
    515. ByteBuffer buffer = ByteBuffer.allocate(readSize);
    516. in.readFully(buffer.array(), buffer.arrayOffset() + buffer.position(),
    517. buffer.remaining());
    518. //read the PostScript
    519. //get length of PostScript
    520. int psLen = buffer.get(readSize - 1) & 0xff;
    521. int len = OrcFile.MAGIC.length();
    522. if (psLen < len + 1) {
    523. return false;
    524. }
    525. int offset = buffer.arrayOffset() + buffer.position() + buffer.limit() - 1
    526. - len;
    527. byte[] array = buffer.array();
    528. // now look for the magic string at the end of the postscript.
    529. if (Text.decode(array, offset, len).equals(OrcFile.MAGIC)) {
    530. return true;
    531. } else {
    532. // If it isn't there, this may be the 0.11.0 version of ORC.
    533. // Read the first 3 bytes of the file to check for the header
    534. in.seek(0);
    535. byte[] header = new byte[len];
    536. in.readFully(header, 0, len);
    537. // if it isn't there, this isn't an ORC file
    538. if (Text.decode(header, 0, len).equals(OrcFile.MAGIC)) {
    539. return true;
    540. }
    541. }
    542. } catch (IOException e) {
    543. LOG.info(String.format("检查文件类型: [%s] 不是ORC File.", file.toString()));
    544. }
    545. return false;
    546. }
    547. // 判断file是否是RC file
    548. private boolean isRCFile(String filepath, FSDataInputStream in) {
    549. // The first version of RCFile used the sequence file header.
    550. final byte[] ORIGINAL_MAGIC = new byte[]{(byte) 'S', (byte) 'E', (byte) 'Q'};
    551. // The 'magic' bytes at the beginning of the RCFile
    552. final byte[] RC_MAGIC = new byte[]{(byte) 'R', (byte) 'C', (byte) 'F'};
    553. // the version that was included with the original magic, which is mapped
    554. // into ORIGINAL_VERSION
    555. final byte ORIGINAL_MAGIC_VERSION_WITH_METADATA = 6;
    556. // All of the versions should be place in this list.
    557. final int ORIGINAL_VERSION = 0; // version with SEQ
    558. final int NEW_MAGIC_VERSION = 1; // version with RCF
    559. final int CURRENT_VERSION = NEW_MAGIC_VERSION;
    560. byte version;
    561. byte[] magic = new byte[RC_MAGIC.length];
    562. try {
    563. in.seek(0);
    564. in.readFully(magic);
    565. if (Arrays.equals(magic, ORIGINAL_MAGIC)) {
    566. byte vers = in.readByte();
    567. if (vers != ORIGINAL_MAGIC_VERSION_WITH_METADATA) {
    568. return false;
    569. }
    570. version = ORIGINAL_VERSION;
    571. } else {
    572. if (!Arrays.equals(magic, RC_MAGIC)) {
    573. return false;
    574. }
    575. // Set 'version'
    576. version = in.readByte();
    577. if (version > CURRENT_VERSION) {
    578. return false;
    579. }
    580. }
    581. if (version == ORIGINAL_VERSION) {
    582. try {
    583. Class keyCls = hadoopConf.getClassByName(Text.readString(in));
    584. Class valCls = hadoopConf.getClassByName(Text.readString(in));
    585. if (!keyCls.equals(RCFile.KeyBuffer.class)
    586. || !valCls.equals(RCFile.ValueBuffer.class)) {
    587. return false;
    588. }
    589. } catch (ClassNotFoundException e) {
    590. return false;
    591. }
    592. }
    593. boolean decompress = in.readBoolean(); // is compressed?
    594. if (version == ORIGINAL_VERSION) {
    595. // is block-compressed? it should be always false.
    596. boolean blkCompressed = in.readBoolean();
    597. if (blkCompressed) {
    598. return false;
    599. }
    600. }
    601. return true;
    602. } catch (IOException e) {
    603. LOG.info(String.format("检查文件类型: [%s] 不是RC File.", filepath));
    604. }
    605. return false;
    606. }
    607. // 判断file是否是Sequence file
    608. private boolean isSequenceFile(String filepath, FSDataInputStream in) {
    609. byte[] SEQ_MAGIC = new byte[]{(byte) 'S', (byte) 'E', (byte) 'Q'};
    610. byte[] magic = new byte[SEQ_MAGIC.length];
    611. try {
    612. in.seek(0);
    613. in.readFully(magic);
    614. if (Arrays.equals(magic, SEQ_MAGIC)) {
    615. return true;
    616. } else {
    617. return false;
    618. }
    619. } catch (IOException e) {
    620. LOG.info(String.format("检查文件类型: [%s] 不是Sequence File.", filepath));
    621. }
    622. return false;
    623. }
    624. }
    625. HiveReader.class

      1. package com.alibaba.datax.plugin.reader.hivereader;
      2. import com.alibaba.datax.common.exception.DataXException;
      3. import com.alibaba.datax.common.plugin.RecordSender;
      4. import com.alibaba.datax.common.spi.Reader;
      5. import com.alibaba.datax.common.util.Configuration;
      6. import com.alibaba.datax.plugin.unstructuredstorage.reader.UnstructuredStorageReaderUtil;
      7. import org.apache.commons.lang.StringEscapeUtils;
      8. import org.apache.commons.lang3.StringUtils;
      9. import org.apache.commons.lang3.time.FastDateFormat;
      10. import org.slf4j.Logger;
      11. import org.slf4j.LoggerFactory;
      12. import java.io.InputStream;
      13. import java.util.ArrayList;
      14. import java.util.Date;
      15. import java.util.HashSet;
      16. import java.util.List;
      17. public class HiveReader extends Reader {
      18. /**
      19. * Job 中的方法仅执行一次,Task 中方法会由框架启动多个 Task 线程并行执行。
      20. *

      21. * 整个 Reader 执行流程是:
      22. *
      23. * Job类init-->prepare-->split
      24. *
      25. * Task类init-->prepare-->startRead-->post-->destroy
      26. * Task类init-->prepare-->startRead-->post-->destroy
      27. *
      28. * Job类post-->destroy
      29. *
    626. */
    627. public static class Job extends Reader.Job {
    628. private static final Logger LOG = LoggerFactory.getLogger(Job.class);
    629. private Configuration readerOriginConfig = null;
    630. @Override
    631. public void init() {
    632. LOG.info("init() begin...");
    633. this.readerOriginConfig = super.getPluginJobConf();//获取配置文件信息{parameter 里面的参数}
    634. this.validate();
    635. LOG.info("init() ok and end...");
    636. LOG.info("HiveReader流程说明[1:Reader的HiveQL导入临时表(TextFile无压缩的HDFS) ;2:临时表的HDFS到目标Writer;3:删除临时表]");
    637. }
    638. private void validate() {
    639. this.readerOriginConfig.getNecessaryValue(Key.DEFAULT_FS,
    640. HiveReaderErrorCode.DEFAULT_FS_NOT_FIND_ERROR);
    641. List sqls = this.readerOriginConfig.getList(Key.HIVE_SQL, String.class);
    642. if (null == sqls || sqls.size() == 0) {
    643. throw DataXException.asDataXException(
    644. HiveReaderErrorCode.SQL_NOT_FIND_ERROR,
    645. "您未配置hive sql");
    646. }
    647. //check Kerberos
    648. Boolean haveKerberos = this.readerOriginConfig.getBool(Key.HAVE_KERBEROS, false);
    649. if (haveKerberos) {
    650. this.readerOriginConfig.getNecessaryValue(Key.KERBEROS_KEYTAB_FILE_PATH, HiveReaderErrorCode.REQUIRED_VALUE);
    651. this.readerOriginConfig.getNecessaryValue(Key.KERBEROS_PRINCIPAL, HiveReaderErrorCode.REQUIRED_VALUE);
    652. }
    653. }
    654. @Override
    655. public List split(int adviceNumber) {
    656. //按照Hive sql的个数 获取配置文件的个数
    657. LOG.info("split() begin...");
    658. List sqls = this.readerOriginConfig.getList(Key.HIVE_SQL, String.class);
    659. List readerSplitConfigs = new ArrayList();
    660. Configuration splitedConfig = null;
    661. for (String querySql : sqls) {
    662. splitedConfig = this.readerOriginConfig.clone();
    663. splitedConfig.set(Key.HIVE_SQL, querySql);
    664. readerSplitConfigs.add(splitedConfig);
    665. }
    666. return readerSplitConfigs;
    667. }
    668. //全局post
    669. @Override
    670. public void post() {
    671. LOG.info("任务执行完毕,hive reader post");
    672. }
    673. @Override
    674. public void destroy() {
    675. }
    676. }
    677. public static class Task extends Reader.Task {
    678. private static final Logger LOG = LoggerFactory.getLogger(Task.class);
    679. private Configuration taskConfig;
    680. private String hiveSql;
    681. private String hiveJdbcUrl;
    682. private String username;
    683. private String password;
    684. private String tmpPath;
    685. private String tableName;
    686. private String tempDatabase;
    687. private String tempHdfsLocation;
    688. // private String hive_cmd;
    689. private String hive_sql_set;
    690. private String fieldDelimiter;
    691. private String nullFormat;
    692. private String hive_fieldDelimiter;
    693. private DFSUtil dfsUtil = null;
    694. private HashSet sourceFiles;
    695. @Override
    696. public void init() {
    697. this.tableName = hiveTableName();
    698. //获取配置
    699. this.taskConfig = super.getPluginJobConf();//获取job 分割后的每一个任务单独的配置文件
    700. this.hiveSql = taskConfig.getString(Key.HIVE_SQL);//获取hive sql
    701. this.hiveJdbcUrl = taskConfig.getString(Key.HIVE_JDBC_URL);//获取hive jdbcUrl
    702. this.username = taskConfig.getString(Key.USERNAME);//获取hive 用户名
    703. this.password = taskConfig.getString(Key.PASSWORD);//获取hive 密码
    704. this.tempDatabase = taskConfig.getString(Key.TEMP_DATABASE, Constant.TEMP_DATABASE_DEFAULT);// 临时表的数据库
    705. this.tempHdfsLocation = taskConfig.getString(Key.TEMP_DATABASE_HDFS_LOCATION,
    706. Constant.TEMP_DATABSE_HDFS_LOCATION_DEFAULT.replace("{username}", this.username));// 临时表的数据库路径
    707. // this.hive_cmd = taskConfig.getString(Key.HIVE_CMD, Constant.HIVE_CMD_DEFAULT);
    708. this.hive_sql_set = taskConfig.getString(Key.HIVE_SQL_SET, Constant.HIVE_SQL_SET_DEFAULT);
    709. //判断set语句的结尾是否是分号,不是给加一个
    710. if (!this.hive_sql_set.trim().endsWith(";")) {
    711. this.hive_sql_set = this.hive_sql_set + ";";
    712. }
    713. this.fieldDelimiter = taskConfig.getString(Key.FIELDDELIMITER, Constant.FIELDDELIMITER_DEFAULT);
    714. this.hive_fieldDelimiter = this.fieldDelimiter;
    715. this.fieldDelimiter = StringEscapeUtils.unescapeJava(this.fieldDelimiter);
    716. this.taskConfig.set(Key.FIELDDELIMITER, this.fieldDelimiter);//设置hive 存储文件 hdfs默认的分隔符,传输时候会分隔
    717. this.nullFormat = taskConfig.getString(Key.NULL_FORMAT, Constant.NULL_FORMAT_DEFAULT);
    718. this.taskConfig.set(Key.NULL_FORMAT, this.nullFormat);
    719. //判断set语句的结尾是否是分号,不是给加一个
    720. if (!this.tempHdfsLocation.trim().endsWith("/")) {
    721. this.tempHdfsLocation = this.tempHdfsLocation + "/";
    722. }
    723. this.tmpPath = this.tempHdfsLocation + this.tableName;//创建临时Hive表 存储地址
    724. LOG.info("配置分隔符后:" + this.taskConfig.toJSON());
    725. this.dfsUtil = new DFSUtil(this.taskConfig);//初始化工具类
    726. }
    727. @Override
    728. public void prepare() {
    729. //创建临时Hive表,指定存储地址
    730. String hiveQueryCmd = this.hive_sql_set + " use " + this.tempDatabase + "; create table "
    731. + this.tableName + " ROW FORMAT DELIMITED FIELDS TERMINATED BY '" + this.hive_fieldDelimiter
    732. + "' STORED AS TEXTFILE "
    733. + " as " + this.hiveSql;
    734. LOG.info("hiveCmd ----> :" + hiveQueryCmd);
    735. // String[] cmd = new String[]{this.hive_cmd, "-e", "\"" + hiveQueryCmd + " \""};
    736. // LOG.info(cmd.toString());
    737. //执行脚本,创建临时表
    738. if (!HiveServer2ConnectUtil.execHiveSql(this.username, this.password, hiveQueryCmd, this.hiveJdbcUrl)) {
    739. throw DataXException.asDataXException(
    740. HiveReaderErrorCode.SHELL_ERROR,
    741. "创建hive临时表脚本执行失败");
    742. }
    743. // if (!ShellUtil.exec(new String[]{this.hive_cmd, " -e", "\"" + hiveQueryCmd + " \""})) {
    744. // throw DataXException.asDataXException(
    745. // HiveReaderErrorCode.SHELL_ERROR,
    746. // "创建hive临时表脚本执行失败");
    747. // }
    748. LOG.info("创建hive 临时表结束 end!!!");
    749. LOG.info("prepare(), start to getAllFiles...");
    750. List path = new ArrayList();
    751. path.add(tmpPath);
    752. this.sourceFiles = dfsUtil.getAllFiles(path, Constant.TEXT);
    753. LOG.info(String.format("您即将读取的文件数为: [%s], 列表为: [%s]",
    754. this.sourceFiles.size(),
    755. StringUtils.join(this.sourceFiles, ",")));
    756. }
    757. @Override
    758. public void startRead(RecordSender recordSender) {
    759. //读取临时hive表的hdfs文件
    760. LOG.info("read start");
    761. for (String sourceFile : this.sourceFiles) {
    762. LOG.info(String.format("reading file : [%s]", sourceFile));
    763. //默认读取的是TEXT文件格式
    764. InputStream inputStream = dfsUtil.getInputStream(sourceFile);
    765. UnstructuredStorageReaderUtil.readFromStream(inputStream, sourceFile, this.taskConfig,
    766. recordSender, this.getTaskPluginCollector());
    767. if (recordSender != null) {
    768. recordSender.flush();
    769. }
    770. }
    771. LOG.info("end read source files...");
    772. }
    773. //只是局部post 属于每个task
    774. @Override
    775. public void post() {
    776. LOG.info("one task hive read post...");
    777. deleteTmpTable();
    778. }
    779. private void deleteTmpTable() {
    780. String hiveCmd = this.hive_sql_set + " use " + this.tempDatabase + "; drop table if exists " + this.tableName;
    781. LOG.info("清空数据:hiveCmd ----> :" + hiveCmd);
    782. //执行脚本,删除临时表
    783. if (!HiveServer2ConnectUtil.execHiveSql(this.username, this.password, hiveCmd, this.hiveJdbcUrl)) {
    784. throw DataXException.asDataXException(
    785. HiveReaderErrorCode.SHELL_ERROR,
    786. "删除hive临时表脚本执行失败");
    787. }
    788. // if (!ShellUtil.exec(new String[]{this.hive_cmd, "-e", "\"" + hiveCmd + "\""})) {
    789. // throw DataXException.asDataXException(
    790. // HiveReaderErrorCode.SHELL_ERROR,
    791. // "删除hive临时表脚本执行失败");
    792. // }
    793. }
    794. @Override
    795. public void destroy() {
    796. LOG.info("hive read destroy...");
    797. }
    798. //创建hive临时表名称
    799. private String hiveTableName() {
    800. StringBuilder str = new StringBuilder();
    801. FastDateFormat fdf = FastDateFormat.getInstance("yyyyMMdd");
    802. str.append(Constant.TEMP_TABLE_NAME_PREFIX)
    803. .append(fdf.format(new Date()))
    804. .append("_")
    805. .append(System.currentTimeMillis());
    806. // .append("_").append(KeyUtil.genUniqueKey());
    807. return str.toString().toLowerCase();
    808. }
    809. }
    810. }
    811. HiveReaderErrorCode.class

      1. package com.alibaba.datax.plugin.reader.hivereader;
      2. import com.alibaba.datax.common.spi.ErrorCode;
      3. public enum HiveReaderErrorCode implements ErrorCode {
      4. BAD_CONFIG_VALUE("HiveReader-00", "您配置的值不合法."),
      5. SQL_NOT_FIND_ERROR("HiveReader-01", "您未配置hive sql"),
      6. DEFAULT_FS_NOT_FIND_ERROR("HiveReader-02", "您未配置defaultFS值"),
      7. ILLEGAL_VALUE("HiveReader-03", "值错误"),
      8. CONFIG_INVALID_EXCEPTION("HiveReader-04", "参数配置错误"),
      9. REQUIRED_VALUE("HiveReader-05", "您缺失了必须填写的参数值."),
      10. SHELL_ERROR("HiveReader-06", "hive 脚本执行失败."),
      11. PATH_CONFIG_ERROR("HdfsReader-09", "您配置的path格式有误"),
      12. READ_FILE_ERROR("HdfsReader-10", "读取文件出错"),
      13. FILE_TYPE_UNSUPPORT("HdfsReader-12", "文件类型目前不支持"),
      14. KERBEROS_LOGIN_ERROR("HdfsReader-13", "KERBEROS认证失败"),
      15. READ_SEQUENCEFILE_ERROR("HdfsReader-14", "读取SequenceFile文件出错"),
      16. READ_RCFILE_ERROR("HdfsReader-15", "读取RCFile文件出错"),;
      17. ;
      18. private final String code;
      19. private final String description;
      20. private HiveReaderErrorCode(String code, String description) {
      21. this.code = code;
      22. this.description = description;
      23. }
      24. @Override
      25. public String getCode() {
      26. return this.code;
      27. }
      28. @Override
      29. public String getDescription() {
      30. return this.description;
      31. }
      32. @Override
      33. public String toString() {
      34. return String.format("Code:[%s], Description:[%s]. ", this.code,
      35. this.description);
      36. }
      37. }

      HiveServer2ConnectUtil.class

      1. package com.alibaba.datax.plugin.reader.hivereader;
      2. import org.apache.commons.lang3.StringUtils;
      3. import org.slf4j.Logger;
      4. import org.slf4j.LoggerFactory;
      5. import java.sql.Connection;
      6. import java.sql.DriverManager;
      7. import java.sql.SQLException;
      8. import java.sql.Statement;
      9. public class HiveServer2ConnectUtil {
      10. private static final Logger LOG = LoggerFactory.getLogger(HiveServer2ConnectUtil.class);
      11. /**
      12. * @param args
      13. * @throws SQLException
      14. */
      15. public static void main(String[] args) {
      16. execHiveSql("hive", null,
      17. "; use default; create table tmp_datax_hivereader_20220808_1659953092709 ROW FORMAT DELIMITED FIELDS TERMINATED BY '\\u0001' STORED AS TEXTFILE as select id,username,password from default.t_user;",
      18. "jdbc:hive2://10.252.92.4:10000");
      19. }
      20. /**
      21. * hive执行多个sql
      22. *
      23. * @param username
      24. * @param password
      25. * @param hiveSql
      26. * @param hiveJdbcUrl
      27. * @return
      28. */
      29. public static boolean execHiveSql(String username, String password, String hiveSql, String hiveJdbcUrl) {
      30. try {
      31. Class.forName("org.apache.hive.jdbc.HiveDriver");
      32. } catch (ClassNotFoundException e) {
      33. e.printStackTrace();
      34. System.exit(1);
      35. }
      36. try {
      37. LOG.info("hiveJdbcUrl:{}", hiveJdbcUrl);
      38. LOG.info("username:{}", username);
      39. LOG.info("password:{}", password);
      40. Connection conn = DriverManager.getConnection(hiveJdbcUrl, username, password);
      41. Statement stmt = conn.createStatement();
      42. String[] hiveSqls = hiveSql.split(";");
      43. for (int i = 0; i < hiveSqls.length; i++) {
      44. if (StringUtils.isNotEmpty(hiveSqls[i])) {
      45. stmt.execute(hiveSqls[i]);
      46. }
      47. }
      48. return true;
      49. } catch (SQLException sqlException) {
      50. LOG.error(sqlException.getMessage(), sqlException);
      51. return false;
      52. }
      53. }
      54. }

      Key.class 

      1. package com.alibaba.datax.plugin.reader.hivereader;
      2. public class Key {
      3. /**
      4. * 1.必选:hiveSql,defaultFS
      5. * 2.可选(有缺省值):
      6. * tempDatabase(default)
      7. * tempHdfsLocation(/tmp/hive/)
      8. * hive_cmd(hive)
      9. * fieldDelimiter(\u0001)
      10. * 3.可选(无缺省值):hive_sql_set
      11. */
      12. public final static String DEFAULT_FS = "defaultFS";
      13. // reader执行的hiveSql语句
      14. public final static String HIVE_SQL = "hiveSql";
      15. // hive的Jdbc链接
      16. public final static String HIVE_JDBC_URL = "hiveJdbcUrl";
      17. // hive的用户名
      18. public final static String USERNAME = "username";
      19. // hive的密码
      20. public final static String PASSWORD = "password";
      21. // 临时表所在的数据库名称
      22. public final static String TEMP_DATABASE = "tempDatabase";
      23. // 临时标存放的HDFS目录
      24. public final static String TEMP_DATABASE_HDFS_LOCATION = "tempDatabasePath";
      25. // hive -e命令
      26. public final static String HIVE_CMD = "hive_cmd";
      27. public final static String HIVE_SQL_SET = "hive_sql_set";
      28. // 存储文件 hdfs默认的分隔符
      29. public final static String FIELDDELIMITER = "fieldDelimiter";
      30. public static final String NULL_FORMAT = "nullFormat";
      31. public static final String HADOOP_CONFIG = "hadoopConfig";
      32. public static final String HAVE_KERBEROS = "haveKerberos";
      33. public static final String KERBEROS_KEYTAB_FILE_PATH = "kerberosKeytabFilePath";
      34. public static final String KERBEROS_PRINCIPAL = "kerberosPrincipal";
      35. }

      pom.xml

      1. <project xmlns="http://maven.apache.org/POM/4.0.0"
      2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      4. <parent>
      5. <artifactId>datax-allartifactId>
      6. <groupId>com.alibaba.dataxgroupId>
      7. <version>0.0.1-SNAPSHOTversion>
      8. parent>
      9. <modelVersion>4.0.0modelVersion>
      10. <artifactId>hivereaderartifactId>
      11. <properties>
      12. <hive.version>2.1.1hive.version>
      13. <hadoop.version>2.7.1hadoop.version>
      14. properties>
      15. <dependencies>
      16. <dependency>
      17. <groupId>com.alibaba.dataxgroupId>
      18. <artifactId>datax-commonartifactId>
      19. <version>${datax-project-version}version>
      20. <exclusions>
      21. <exclusion>
      22. <artifactId>slf4j-log4j12artifactId>
      23. <groupId>org.slf4jgroupId>
      24. exclusion>
      25. exclusions>
      26. dependency>
      27. <dependency>
      28. <groupId>org.slf4jgroupId>
      29. <artifactId>slf4j-apiartifactId>
      30. dependency>
      31. <dependency>
      32. <groupId>ch.qos.logbackgroupId>
      33. <artifactId>logback-classicartifactId>
      34. dependency>
      35. <dependency>
      36. <groupId>org.apache.hadoopgroupId>
      37. <artifactId>hadoop-hdfsartifactId>
      38. <version>${hadoop.version}version>
      39. dependency>
      40. <dependency>
      41. <groupId>org.apache.hadoopgroupId>
      42. <artifactId>hadoop-commonartifactId>
      43. <version>${hadoop.version}version>
      44. dependency>
      45. <dependency>
      46. <groupId>org.apache.hadoopgroupId>
      47. <artifactId>hadoop-yarn-commonartifactId>
      48. <version>${hadoop.version}version>
      49. dependency>
      50. <dependency>
      51. <groupId>org.apache.hadoopgroupId>
      52. <artifactId>hadoop-mapreduce-client-coreartifactId>
      53. <version>${hadoop.version}version>
      54. dependency>
      55. <dependency>
      56. <groupId>org.apache.hivegroupId>
      57. <artifactId>hive-execartifactId>
      58. <version>${hive.version}version>
      59. dependency>
      60. <dependency>
      61. <groupId>org.apache.hivegroupId>
      62. <artifactId>hive-serdeartifactId>
      63. <version>${hive.version}version>
      64. dependency>
      65. <dependency>
      66. <groupId>org.apache.hivegroupId>
      67. <artifactId>hive-serviceartifactId>
      68. <version>${hive.version}version>
      69. dependency>
      70. <dependency>
      71. <groupId>org.apache.hivegroupId>
      72. <artifactId>hive-commonartifactId>
      73. <version>${hive.version}version>
      74. dependency>
      75. <dependency>
      76. <groupId>org.apache.hive.hcataloggroupId>
      77. <artifactId>hive-hcatalog-coreartifactId>
      78. <version>${hive.version}version>
      79. dependency>
      80. <dependency>
      81. <groupId>org.apache.hivegroupId>
      82. <artifactId>hive-jdbcartifactId>
      83. <version>${hive.version}version>
      84. dependency>
      85. <dependency>
      86. <groupId>com.alibaba.dataxgroupId>
      87. <artifactId>plugin-unstructured-storage-utilartifactId>
      88. <version>${datax-project-version}version>
      89. dependency>
      90. dependencies>
      91. <build>
      92. <plugins>
      93. <plugin>
      94. <artifactId>maven-compiler-pluginartifactId>
      95. <configuration>
      96. <source>1.8source>
      97. <target>1.8target>
      98. <encoding>${project-sourceEncoding}encoding>
      99. configuration>
      100. plugin>
      101. <plugin>
      102. <artifactId>maven-assembly-pluginartifactId>
      103. <configuration>
      104. <descriptors>
      105. <descriptor>src/main/assembly/package.xmldescriptor>
      106. descriptors>
      107. <finalName>dataxfinalName>
      108. configuration>
      109. <executions>
      110. <execution>
      111. <id>dwzipid>
      112. <phase>packagephase>
      113. <goals>
      114. <goal>singlegoal>
      115. goals>
      116. execution>
      117. executions>
      118. plugin>
      119. plugins>
      120. build>
      121. project>

      plugin_job_template.json

      1. {
      2. "name": "hivereader",
      3. "parameter": {
      4. "defaultFS": "hdfs://:",
      5. "hiveJdbcUrl": "jdbc:hive2://",
      6. "username": "hive",
      7. "hiveSql": [
      8. "select id,username,password from default.t_user;"
      9. ]
      10. }
      11. }

      4、新建hivewriter模块

      项目结构

       package.xml

      1. <assembly
      2. xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
      3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      4. xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
      5. <id>id>
      6. <formats>
      7. <format>dirformat>
      8. formats>
      9. <includeBaseDirectory>falseincludeBaseDirectory>
      10. <fileSets>
      11. <fileSet>
      12. <directory>src/main/resourcesdirectory>
      13. <includes>
      14. <include>plugin.jsoninclude>
      15. includes>
      16. <outputDirectory>plugin/writer/hivewriteroutputDirectory>
      17. fileSet>
      18. <fileSet>
      19. <directory>target/directory>
      20. <includes>
      21. <include>hivewriter-0.0.1-SNAPSHOT.jarinclude>
      22. includes>
      23. <outputDirectory>plugin/writer/hivewriteroutputDirectory>
      24. fileSet>
      25. fileSets>
      26. <dependencySets>
      27. <dependencySet>
      28. <useProjectArtifact>falseuseProjectArtifact>
      29. <outputDirectory>plugin/writer/hivewriter/libsoutputDirectory>
      30. <scope>runtimescope>
      31. dependencySet>
      32. dependencySets>
      33. assembly>

      Constans.class

      1. package com.alibaba.datax.plugin.writer.hivewriter;
      2. public class Constants {
      3. public static final String TEMP_TABLE_NAME_PREFIX_DEFAULT="tmp_datax_hivewriter_";
      4. public final static String HIVE_CMD_DEFAULT = "hive";
      5. public final static String HIVE_SQL_SET_DEFAULT = "";
      6. public final static String HIVE_TARGET_TABLE_COMPRESS_SQL= "";
      7. public static final String WRITE_MODE_DEFAULT="insert";
      8. public final static String HIVE_PRESQL_DEFAULT = "";
      9. public final static String HIVE_POSTSQL_DEFAULT = "";
      10. public static final String INSERT_PRE_SQL="SET hive.exec.dynamic.partition=true;"
      11. +"SET hive.exec.dynamic.partition.mode=nonstrict;"
      12. +"SET hive.exec.max.dynamic.partitions.pernode=100000;"
      13. +"SET hive.exec.max.dynamic.partitions=100000;";
      14. public final static String FIELDDELIMITER_DEFAULT = "\\u0001";
      15. public final static String COMPRESS_DEFAULT="gzip";
      16. // 此默认值,暂无使用
      17. public static final String DEFAULT_NULL_FORMAT = "\\N";
      18. }

      HdfsHelper.class

      1. package com.alibaba.datax.plugin.writer.hivewriter;
      2. import com.alibaba.datax.common.element.Column;
      3. import com.alibaba.datax.common.element.Record;
      4. import com.alibaba.datax.common.exception.DataXException;
      5. import com.alibaba.datax.common.plugin.RecordReceiver;
      6. import com.alibaba.datax.common.plugin.TaskPluginCollector;
      7. import com.alibaba.datax.common.util.Configuration;
      8. import com.alibaba.fastjson.JSON;
      9. import com.alibaba.fastjson.JSONObject;
      10. import com.google.common.collect.Lists;
      11. import org.apache.commons.lang3.StringUtils;
      12. import org.apache.commons.lang3.tuple.MutablePair;
      13. import org.apache.hadoop.fs.*;
      14. import org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat;
      15. import org.apache.hadoop.hive.ql.io.orc.OrcSerde;
      16. import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
      17. import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
      18. import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
      19. import org.apache.hadoop.io.NullWritable;
      20. import org.apache.hadoop.io.Text;
      21. import org.apache.hadoop.io.compress.CompressionCodec;
      22. import org.apache.hadoop.mapred.*;
      23. import org.apache.hadoop.security.UserGroupInformation;
      24. import org.slf4j.Logger;
      25. import org.slf4j.LoggerFactory;
      26. import java.io.IOException;
      27. import java.net.URI;
      28. import java.net.URISyntaxException;
      29. import java.text.SimpleDateFormat;
      30. import java.util.*;
      31. public class HdfsHelper {
      32. public static final Logger LOG = LoggerFactory.getLogger(HiveWriter.Job.class);
      33. public FileSystem fileSystem = null;
      34. public JobConf conf = null;
      35. public org.apache.hadoop.conf.Configuration hadoopConf = null;
      36. public static final String HADOOP_SECURITY_AUTHENTICATION_KEY = "hadoop.security.authentication";
      37. public static final String HDFS_DEFAULTFS_KEY = "fs.defaultFS";
      38. private String username = null;
      39. // Kerberos
      40. private Boolean haveKerberos = false;
      41. private String kerberosKeytabFilePath;
      42. private String kerberosPrincipal;
      43. public void getFileSystem(String defaultFS, Configuration taskConfig) {
      44. hadoopConf = new org.apache.hadoop.conf.Configuration();
      45. Configuration hadoopSiteParams = taskConfig.getConfiguration(Key.HADOOP_CONFIG);
      46. JSONObject hadoopSiteParamsAsJsonObject = JSON.parseObject(taskConfig.getString(Key.HADOOP_CONFIG));
      47. if (null != hadoopSiteParams) {
      48. Set paramKeys = hadoopSiteParams.getKeys();
      49. for (String each : paramKeys) {
      50. hadoopConf.set(each, hadoopSiteParamsAsJsonObject.getString(each));
      51. }
      52. }
      53. hadoopConf.set(HDFS_DEFAULTFS_KEY, defaultFS);
      54. this.username = taskConfig.getString(Key.USERNAME);
      55. System.setProperty("HADOOP_USER_NAME", this.username);
      56. //是否有Kerberos认证
      57. this.haveKerberos = taskConfig.getBool(Key.HAVE_KERBEROS, false);
      58. if (haveKerberos) {
      59. this.kerberosKeytabFilePath = taskConfig.getString(Key.KERBEROS_KEYTAB_FILE_PATH);
      60. this.kerberosPrincipal = taskConfig.getString(Key.KERBEROS_PRINCIPAL);
      61. hadoopConf.set(HADOOP_SECURITY_AUTHENTICATION_KEY, "kerberos");
      62. }
      63. this.kerberosAuthentication(this.kerberosPrincipal, this.kerberosKeytabFilePath);
      64. conf = new JobConf(hadoopConf);
      65. conf.setUser(this.username);
      66. try {
      67. LOG.info("defaultFS:{},user:{}", defaultFS, this.username);
      68. fileSystem = FileSystem.get(new URI(hadoopConf.get(HDFS_DEFAULTFS_KEY)), conf, this.username);
      69. } catch (IOException e) {
      70. String message = String.format("获取FileSystem时发生网络IO异常,请检查您的网络是否正常!HDFS地址:[%s]",
      71. "message:defaultFS =" + defaultFS);
      72. LOG.error(message);
      73. throw DataXException.asDataXException(HiveWriterErrorCode.CONNECT_HDFS_IO_ERROR, e);
      74. } catch (Exception e) {
      75. String message = String.format("获取FileSystem失败,请检查HDFS地址是否正确: [%s]",
      76. "message:defaultFS =" + defaultFS);
      77. LOG.error(message);
      78. throw DataXException.asDataXException(HiveWriterErrorCode.CONNECT_HDFS_IO_ERROR, e);
      79. }
      80. if (null == fileSystem || null == conf) {
      81. String message = String.format("获取FileSystem失败,请检查HDFS地址是否正确: [%s]",
      82. "message:defaultFS =" + defaultFS);
      83. LOG.error(message);
      84. throw DataXException.asDataXException(HiveWriterErrorCode.CONNECT_HDFS_IO_ERROR, message);
      85. }
      86. }
      87. private void kerberosAuthentication(String kerberosPrincipal, String kerberosKeytabFilePath) {
      88. if (haveKerberos && StringUtils.isNotBlank(this.kerberosPrincipal) && StringUtils.isNotBlank(this.kerberosKeytabFilePath)) {
      89. UserGroupInformation.setConfiguration(this.hadoopConf);
      90. try {
      91. UserGroupInformation.loginUserFromKeytab(kerberosPrincipal, kerberosKeytabFilePath);
      92. } catch (Exception e) {
      93. String message = String.format("kerberos认证失败,请确定kerberosKeytabFilePath[%s]和kerberosPrincipal[%s]填写正确",
      94. kerberosKeytabFilePath, kerberosPrincipal);
      95. LOG.error(message);
      96. throw DataXException.asDataXException(HiveWriterErrorCode.KERBEROS_LOGIN_ERROR, e);
      97. }
      98. }
      99. }
      100. /**
      101. * 获取指定目录先的文件列表
      102. *
      103. * @param dir
      104. * @return 拿到的是文件全路径,
      105. */
      106. public String[] hdfsDirList(String dir) {
      107. Path path = new Path(dir);
      108. String[] files = null;
      109. try {
      110. FileStatus[] status = fileSystem.listStatus(path);
      111. files = new String[status.length];
      112. for(int i=0;i
      113. files[i] = status[i].getPath().toString();
      114. }
      115. } catch (IOException e) {
      116. String message = String.format("获取目录[%s]文件列表时发生网络IO异常,请检查您的网络是否正常!", dir);
      117. LOG.error(message);
      118. throw DataXException.asDataXException(HiveWriterErrorCode.CONNECT_HDFS_IO_ERROR, e);
      119. }
      120. return files;
      121. }
      122. /**
      123. * 获取以fileName__ 开头的文件列表
      124. *
      125. * @param dir
      126. * @param fileName
      127. * @return
      128. */
      129. public Path[] hdfsDirList(String dir, String fileName) {
      130. Path path = new Path(dir);
      131. Path[] files = null;
      132. String filterFileName = fileName + "__*";
      133. try {
      134. PathFilter pathFilter = new GlobFilter(filterFileName);
      135. FileStatus[] status = fileSystem.listStatus(path,pathFilter);
      136. files = new Path[status.length];
      137. for(int i=0;i
      138. files[i] = status[i].getPath();
      139. }
      140. } catch (IOException e) {
      141. String message = String.format("获取目录[%s]下文件名以[%s]开头的文件列表时发生网络IO异常,请检查您的网络是否正常!",
      142. dir,fileName);
      143. LOG.error(message);
      144. throw DataXException.asDataXException(HiveWriterErrorCode.CONNECT_HDFS_IO_ERROR, e);
      145. }
      146. return files;
      147. }
      148. public boolean isPathexists(String filePath) {
      149. Path path = new Path(filePath);
      150. boolean exist = false;
      151. try {
      152. exist = fileSystem.exists(path);
      153. } catch (IOException e) {
      154. String message = String.format("判断文件路径[%s]是否存在时发生网络IO异常,请检查您的网络是否正常!",
      155. "message:filePath =" + filePath);
      156. LOG.error(message);
      157. throw DataXException.asDataXException(HiveWriterErrorCode.CONNECT_HDFS_IO_ERROR, e);
      158. }
      159. return exist;
      160. }
      161. public boolean isPathDir(String filePath) {
      162. Path path = new Path(filePath);
      163. boolean isDir = false;
      164. try {
      165. isDir = fileSystem.isDirectory(path);
      166. } catch (IOException e) {
      167. String message = String.format("判断路径[%s]是否是目录时发生网络IO异常,请检查您的网络是否正常!", filePath);
      168. LOG.error(message);
      169. throw DataXException.asDataXException(HiveWriterErrorCode.CONNECT_HDFS_IO_ERROR, e);
      170. }
      171. return isDir;
      172. }
      173. public void deleteFiles(Path[] paths){
      174. for(int i=0;i
      175. LOG.info(String.format("delete file [%s].", paths[i].toString()));
      176. try {
      177. fileSystem.delete(paths[i],true);
      178. } catch (IOException e) {
      179. String message = String.format("删除文件[%s]时发生IO异常,请检查您的网络是否正常!",
      180. paths[i].toString());
      181. LOG.error(message);
      182. throw DataXException.asDataXException(HiveWriterErrorCode.CONNECT_HDFS_IO_ERROR, e);
      183. }
      184. }
      185. }
      186. public void deleteDir(Path path){
      187. LOG.info(String.format("start delete tmp dir [%s] .",path.toString()));
      188. try {
      189. if(isPathexists(path.toString())) {
      190. fileSystem.delete(path, true);
      191. }
      192. } catch (Exception e) {
      193. String message = String.format("删除临时目录[%s]时发生IO异常,请检查您的网络是否正常!", path.toString());
      194. LOG.error(message);
      195. throw DataXException.asDataXException(HiveWriterErrorCode.CONNECT_HDFS_IO_ERROR, e);
      196. }
      197. LOG.info(String.format("finish delete tmp dir [%s] .",path.toString()));
      198. }
      199. public void renameFile(HashSet tmpFiles, HashSet endFiles) {
      200. Path tmpFilesParent = null;
      201. if (tmpFiles.size() != endFiles.size()) {
      202. String message = String.format("临时目录下文件名个数与目标文件名个数不一致!");
      203. LOG.error(message);
      204. throw DataXException.asDataXException(HiveWriterErrorCode.HDFS_RENAME_FILE_ERROR, message);
      205. } else {
      206. try {
      207. for (Iterator it1 = tmpFiles.iterator(), it2 = endFiles.iterator(); it1.hasNext() && it2.hasNext(); ) {
      208. String srcFile = it1.next().toString();
      209. String dstFile = it2.next().toString();
      210. Path srcFilePah = new Path(srcFile);
      211. Path dstFilePah = new Path(dstFile);
      212. if (tmpFilesParent == null) {
      213. tmpFilesParent = srcFilePah.getParent();
      214. }
      215. LOG.info(String.format("start rename file [%s] to file [%s].", srcFile, dstFile));
      216. boolean renameTag = false;
      217. long fileLen = fileSystem.getFileStatus(srcFilePah).getLen();
      218. if (fileLen > 0) {
      219. renameTag = fileSystem.rename(srcFilePah, dstFilePah);
      220. if (!renameTag) {
      221. String message = String.format("重命名文件[%s]失败,请检查您的网络是否正常!", srcFile);
      222. LOG.error(message);
      223. throw DataXException.asDataXException(HiveWriterErrorCode.HDFS_RENAME_FILE_ERROR, message);
      224. }
      225. LOG.info(String.format("finish rename file [%s] to file [%s].", srcFile, dstFile));
      226. } else {
      227. LOG.info(String.format("文件[%s]内容为空,请检查写入是否正常!", srcFile));
      228. }
      229. }
      230. } catch (Exception e) {
      231. String message = String.format("重命名文件时发生异常,请检查您的网络是否正常!");
      232. LOG.error(message);
      233. throw DataXException.asDataXException(HiveWriterErrorCode.CONNECT_HDFS_IO_ERROR, e);
      234. } finally {
      235. deleteDir(tmpFilesParent);
      236. }
      237. }
      238. }
      239. //关闭FileSystem
      240. public void closeFileSystem(){
      241. try {
      242. fileSystem.close();
      243. } catch (IOException e) {
      244. String message = String.format("关闭FileSystem时发生IO异常,请检查您的网络是否正常!");
      245. LOG.error(message);
      246. throw DataXException.asDataXException(HiveWriterErrorCode.CONNECT_HDFS_IO_ERROR, e);
      247. }
      248. }
      249. //textfile格式文件
      250. public FSDataOutputStream getOutputStream(String path){
      251. Path storePath = new Path(path);
      252. FSDataOutputStream fSDataOutputStream = null;
      253. try {
      254. fSDataOutputStream = fileSystem.create(storePath);
      255. } catch (IOException e) {
      256. String message = String.format("Create an FSDataOutputStream at the indicated Path[%s] failed: [%s]",
      257. "message:path =" + path);
      258. LOG.error(message);
      259. throw DataXException.asDataXException(HiveWriterErrorCode.Write_FILE_IO_ERROR, e);
      260. }
      261. return fSDataOutputStream;
      262. }
      263. /**
      264. * 写textfile类型文件
      265. *
      266. * @param lineReceiver
      267. * @param config
      268. * @param fileName
      269. * @param taskPluginCollector
      270. */
      271. public void textFileStartWrite(RecordReceiver lineReceiver, Configuration config, String fileName,
      272. TaskPluginCollector taskPluginCollector) {
      273. char fieldDelimiter = config.getChar(Key.FIELD_DELIMITER);
      274. List columns = config.getListConfiguration(Key.COLUMN);
      275. String compress = config.getString(Key.COMPRESS, null);
      276. SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmm");
      277. String attempt = "attempt_" + dateFormat.format(new Date()) + "_0001_m_000000_0";
      278. Path outputPath = new Path(fileName);
      279. //todo 需要进一步确定TASK_ATTEMPT_ID
      280. conf.set(JobContext.TASK_ATTEMPT_ID, attempt);
      281. FileOutputFormat outFormat = new TextOutputFormat();
      282. outFormat.setOutputPath(conf, outputPath);
      283. outFormat.setWorkOutputPath(conf, outputPath);
      284. if (null != compress) {
      285. Classextends CompressionCodec> codecClass = getCompressCodec(compress);
      286. if (null != codecClass) {
      287. outFormat.setOutputCompressorClass(conf, codecClass);
      288. }
      289. }
      290. try {
      291. RecordWriter writer = outFormat.getRecordWriter(fileSystem, conf, outputPath.toString(), Reporter.NULL);
      292. Record record = null;
      293. while ((record = lineReceiver.getFromReader()) != null) {
      294. MutablePair transportResult = transportOneRecord(record, fieldDelimiter, columns, taskPluginCollector,config);
      295. if (!transportResult.getRight()) {
      296. writer.write(NullWritable.get(),transportResult.getLeft());
      297. }
      298. }
      299. writer.close(Reporter.NULL);
      300. } catch (Exception e) {
      301. String message = String.format("写文件文件[%s]时发生IO异常,请检查您的网络是否正常!", fileName);
      302. LOG.error(message);
      303. Path path = new Path(fileName);
      304. deleteDir(path.getParent());
      305. throw DataXException.asDataXException(HiveWriterErrorCode.Write_FILE_IO_ERROR, e);
      306. }
      307. }
      308. public static MutablePair transportOneRecord(
      309. Record record, char fieldDelimiter, List columnsConfiguration, TaskPluginCollector taskPluginCollector, Configuration config) {
      310. MutablePair, Boolean> transportResultList = transportOneRecord(record, columnsConfiguration, taskPluginCollector, config);
      311. //保存<转换后的数据,是否是脏数据>
      312. MutablePair transportResult = new MutablePair();
      313. transportResult.setRight(false);
      314. if (null != transportResultList) {
      315. Text recordResult = new Text(StringUtils.join(transportResultList.getLeft(), fieldDelimiter));
      316. transportResult.setRight(transportResultList.getRight());
      317. transportResult.setLeft(recordResult);
      318. }
      319. return transportResult;
      320. }
      321. public Classextends CompressionCodec> getCompressCodec(String compress) {
      322. Classextends CompressionCodec> codecClass = null;
      323. if (null == compress) {
      324. codecClass = null;
      325. } else if ("GZIP".equalsIgnoreCase(compress)) {
      326. codecClass = org.apache.hadoop.io.compress.GzipCodec.class;
      327. } else if ("BZIP2".equalsIgnoreCase(compress)) {
      328. codecClass = org.apache.hadoop.io.compress.BZip2Codec.class;
      329. } else if ("SNAPPY".equalsIgnoreCase(compress)) {
      330. //todo 等需求明确后支持 需要用户安装SnappyCodec
      331. codecClass = org.apache.hadoop.io.compress.SnappyCodec.class;
      332. // org.apache.hadoop.hive.ql.io.orc.ZlibCodec.class not public
      333. //codecClass = org.apache.hadoop.hive.ql.io.orc.ZlibCodec.class;
      334. } else {
      335. throw DataXException.asDataXException(HiveWriterErrorCode.ILLEGAL_VALUE,
      336. String.format("目前不支持您配置的 compress 模式 : [%s]", compress));
      337. }
      338. return codecClass;
      339. }
      340. /**
      341. * 写orcfile类型文件
      342. *
      343. * @param lineReceiver
      344. * @param config
      345. * @param fileName
      346. * @param taskPluginCollector
      347. */
      348. public void orcFileStartWrite(RecordReceiver lineReceiver, Configuration config, String fileName,
      349. TaskPluginCollector taskPluginCollector) {
      350. List columns = config.getListConfiguration(Key.COLUMN);
      351. String compress = config.getString(Key.COMPRESS, null);
      352. List columnNames = getColumnNames(columns);
      353. List columnTypeInspectors = getColumnTypeInspectors(columns);
      354. StructObjectInspector inspector = (StructObjectInspector) ObjectInspectorFactory
      355. .getStandardStructObjectInspector(columnNames, columnTypeInspectors);
      356. OrcSerde orcSerde = new OrcSerde();
      357. FileOutputFormat outFormat = new OrcOutputFormat();
      358. if (!"NONE".equalsIgnoreCase(compress) && null != compress) {
      359. Classextends CompressionCodec> codecClass = getCompressCodec(compress);
      360. if (null != codecClass) {
      361. outFormat.setOutputCompressorClass(conf, codecClass);
      362. }
      363. }
      364. try {
      365. RecordWriter writer = outFormat.getRecordWriter(fileSystem, conf, fileName, Reporter.NULL);
      366. Record record = null;
      367. while ((record = lineReceiver.getFromReader()) != null) {
      368. MutablePair, Boolean> transportResult = transportOneRecord(record, columns, taskPluginCollector, config);
      369. if (!transportResult.getRight()) {
      370. writer.write(NullWritable.get(), orcSerde.serialize(transportResult.getLeft(), inspector));
      371. }
      372. }
      373. writer.close(Reporter.NULL);
      374. } catch (Exception e) {
      375. String message = String.format("写文件文件[%s]时发生IO异常,请检查您的网络是否正常!", fileName);
      376. LOG.error(message);
      377. Path path = new Path(fileName);
      378. deleteDir(path.getParent());
      379. throw DataXException.asDataXException(HiveWriterErrorCode.Write_FILE_IO_ERROR, e);
      380. }
      381. }
      382. public List getColumnNames(List columns) {
      383. List columnNames = Lists.newArrayList();
      384. for (Configuration eachColumnConf : columns) {
      385. columnNames.add(eachColumnConf.getString(Key.NAME));
      386. }
      387. return columnNames;
      388. }
      389. /**
      390. * id int,
      391. * username string,
      392. * telephone string,
      393. * mail string,
      394. * day string
      395. */
      396. public String getColumnInfo(List columns) {
      397. StringBuilder str = new StringBuilder();
      398. List columnNames = Lists.newArrayList();
      399. for (int i = 0; i < columns.size(); i++) {
      400. Configuration eachColumnConf = columns.get(i);
      401. String name = eachColumnConf.getString(Key.NAME);//列名称
      402. String type = eachColumnConf.getString(Key.TYPE);//列类型
      403. str.append(name).append(" ").append(type);
      404. if (i != (columns.size() - 1)) {
      405. str.append(",");
      406. }
      407. }
      408. return str.toString();
      409. }
      410. /**
      411. * By LingZhy on 2021/4/29 16:58
      412. *
      413. * @return java.lang.String
      414. * @description
      415. * @params * @param columns
      416. */
      417. public String getColumnName(List columns) {
      418. StringBuilder str = new StringBuilder();
      419. List list = Lists.newArrayList();
      420. for (int i = 0; i < columns.size(); i++) {
      421. Configuration eachColumnConf = columns.get(i);
      422. String name = eachColumnConf.getString(Key.NAME).toLowerCase();
      423. list.add(name);
      424. }
      425. return String.join(",", list);
      426. }
      427. /**
      428. * 根据writer配置的字段类型,构建inspector
      429. *
      430. * @param columns
      431. * @return
      432. */
      433. public List getColumnTypeInspectors(List columns) {
      434. List columnTypeInspectors = Lists.newArrayList();
      435. for (Configuration eachColumnConf : columns) {
      436. SupportHiveDataType columnType = SupportHiveDataType.valueOf(eachColumnConf.getString(Key.TYPE).toUpperCase());
      437. ObjectInspector objectInspector = null;
      438. switch (columnType) {
      439. case TINYINT:
      440. objectInspector = ObjectInspectorFactory.getReflectionObjectInspector(Byte.class, ObjectInspectorFactory.ObjectInspectorOptions.JAVA);
      441. break;
      442. case SMALLINT:
      443. objectInspector = ObjectInspectorFactory.getReflectionObjectInspector(Short.class, ObjectInspectorFactory.ObjectInspectorOptions.JAVA);
      444. break;
      445. case INT:
      446. objectInspector = ObjectInspectorFactory.getReflectionObjectInspector(Integer.class, ObjectInspectorFactory.ObjectInspectorOptions.JAVA);
      447. break;
      448. case BIGINT:
      449. objectInspector = ObjectInspectorFactory.getReflectionObjectInspector(Long.class, ObjectInspectorFactory.ObjectInspectorOptions.JAVA);
      450. break;
      451. case FLOAT:
      452. objectInspector = ObjectInspectorFactory.getReflectionObjectInspector(Float.class, ObjectInspectorFactory.ObjectInspectorOptions.JAVA);
      453. break;
      454. case DOUBLE:
      455. objectInspector = ObjectInspectorFactory.getReflectionObjectInspector(Double.class, ObjectInspectorFactory.ObjectInspectorOptions.JAVA);
      456. break;
      457. case TIMESTAMP:
      458. objectInspector = ObjectInspectorFactory.getReflectionObjectInspector(java.sql.Timestamp.class, ObjectInspectorFactory.ObjectInspectorOptions.JAVA);
      459. break;
      460. case DATE:
      461. objectInspector = ObjectInspectorFactory.getReflectionObjectInspector(java.sql.Date.class, ObjectInspectorFactory.ObjectInspectorOptions.JAVA);
      462. break;
      463. case STRING:
      464. case VARCHAR:
      465. case CHAR:
      466. objectInspector = ObjectInspectorFactory.getReflectionObjectInspector(String.class, ObjectInspectorFactory.ObjectInspectorOptions.JAVA);
      467. break;
      468. case BOOLEAN:
      469. objectInspector = ObjectInspectorFactory.getReflectionObjectInspector(Boolean.class, ObjectInspectorFactory.ObjectInspectorOptions.JAVA);
      470. break;
      471. default:
      472. throw DataXException
      473. .asDataXException(
      474. HiveWriterErrorCode.ILLEGAL_VALUE,
      475. String.format(
      476. "您的配置文件中的列配置信息有误. 因为DataX 不支持数据库写入这种字段类型. 字段名:[%s], 字段类型:[%d]. 请修改表中该字段的类型或者不同步该字段.",
      477. eachColumnConf.getString(Key.NAME),
      478. eachColumnConf.getString(Key.TYPE)));
      479. }
      480. columnTypeInspectors.add(objectInspector);
      481. }
      482. return columnTypeInspectors;
      483. }
      484. public OrcSerde getOrcSerde(Configuration config) {
      485. String fieldDelimiter = config.getString(Key.FIELD_DELIMITER);
      486. String compress = config.getString(Key.COMPRESS);
      487. String encoding = config.getString(Key.ENCODING);
      488. OrcSerde orcSerde = new OrcSerde();
      489. Properties properties = new Properties();
      490. properties.setProperty("orc.bloom.filter.columns", fieldDelimiter);
      491. properties.setProperty("orc.compress", compress);
      492. properties.setProperty("orc.encoding.strategy", encoding);
      493. orcSerde.initialize(conf, properties);
      494. return orcSerde;
      495. }
      496. public static MutablePair, Boolean> transportOneRecord(
      497. Record record, List columnsConfiguration,
      498. TaskPluginCollector taskPluginCollector, Configuration config) {
      499. MutablePair, Boolean> transportResult = new MutablePair, Boolean>();
      500. transportResult.setRight(false);
      501. List recordList = Lists.newArrayList();
      502. int recordLength = record.getColumnNumber();
      503. if (0 != recordLength) {
      504. Column column;
      505. for (int i = 0; i < recordLength; i++) {
      506. column = record.getColumn(i);
      507. //todo as method
      508. if (null != column.getRawData()) {
      509. String rowData = column.getRawData().toString();
      510. SupportHiveDataType columnType = SupportHiveDataType.valueOf(
      511. columnsConfiguration.get(i).getString(Key.TYPE).toUpperCase());
      512. //根据writer端类型配置做类型转换
      513. try {
      514. switch (columnType) {
      515. case TINYINT:
      516. recordList.add(Byte.valueOf(rowData));
      517. break;
      518. case SMALLINT:
      519. recordList.add(Short.valueOf(rowData));
      520. break;
      521. case INT:
      522. recordList.add(Integer.valueOf(rowData));
      523. break;
      524. case BIGINT:
      525. recordList.add(column.asLong());
      526. break;
      527. case FLOAT:
      528. recordList.add(Float.valueOf(rowData));
      529. break;
      530. case DOUBLE:
      531. recordList.add(column.asDouble());
      532. break;
      533. case STRING:
      534. case VARCHAR:
      535. case CHAR:
      536. recordList.add(column.asString());
      537. break;
      538. case BOOLEAN:
      539. recordList.add(column.asBoolean());
      540. break;
      541. case DATE:
      542. recordList.add(new java.sql.Date(column.asDate().getTime()));
      543. break;
      544. case TIMESTAMP:
      545. recordList.add(new java.sql.Timestamp(column.asDate().getTime()));
      546. break;
      547. default:
      548. throw DataXException
      549. .asDataXException(
      550. HiveWriterErrorCode.ILLEGAL_VALUE,
      551. String.format(
      552. "您的配置文件中的列配置信息有误. 因为DataX 不支持数据库写入这种字段类型. 字段名:[%s], 字段类型:[%d]. 请修改表中该字段的类型或者不同步该字段.",
      553. columnsConfiguration.get(i).getString(Key.NAME),
      554. columnsConfiguration.get(i).getString(Key.TYPE)));
      555. }
      556. } catch (Exception e) {
      557. // warn: 此处认为脏数据
      558. String message = String.format(
      559. "字段类型转换错误:你目标字段为[%s]类型,实际字段值为[%s].",
      560. columnsConfiguration.get(i).getString(Key.TYPE), column.getRawData().toString());
      561. taskPluginCollector.collectDirtyRecord(record, message);
      562. transportResult.setRight(true);
      563. break;
      564. }
      565. } else {
      566. // warn: it's all ok if nullFormat is null
      567. //recordList.add(null);
      568. // fix 写入hdfs的text格式时,需要指定NULL为\N
      569. String nullFormat = config.getString(Key.NULL_FORMAT);
      570. if (nullFormat == null) {
      571. recordList.add(null);
      572. } else {
      573. recordList.add(nullFormat);
      574. }
      575. }
      576. }
      577. }
      578. transportResult.setLeft(recordList);
      579. return transportResult;
      580. }
      581. }
      582. HiveServer2ConnectUtil.class

        1. package com.alibaba.datax.plugin.writer.hivewriter;
        2. import org.apache.commons.lang3.StringUtils;
        3. import org.slf4j.Logger;
        4. import org.slf4j.LoggerFactory;
        5. import java.sql.Connection;
        6. import java.sql.DriverManager;
        7. import java.sql.SQLException;
        8. import java.sql.Statement;
        9. public class HiveServer2ConnectUtil {
        10. private static final Logger LOG = LoggerFactory.getLogger(HiveServer2ConnectUtil.class);
        11. /**
        12. * @param args
        13. * @throws SQLException
        14. */
        15. public static void main(String[] args) {
        16. execHiveSql("hive", null,
        17. "; use default; create table tmp_datax_hivereader_20220808_1659953092709 ROW FORMAT DELIMITED FIELDS TERMINATED BY '\\u0001' STORED AS TEXTFILE as select id,username,password from default.t_user;",
        18. "jdbc:hive2://10.252.92.4:10000");
        19. }
        20. /**
        21. * hive执行多个sql
        22. *
        23. * @param username
        24. * @param password
        25. * @param hiveSql
        26. * @param hiveJdbcUrl
        27. * @return
        28. */
        29. public static boolean execHiveSql(String username, String password, String hiveSql, String hiveJdbcUrl) {
        30. try {
        31. Class.forName("org.apache.hive.jdbc.HiveDriver");
        32. } catch (ClassNotFoundException e) {
        33. e.printStackTrace();
        34. System.exit(1);
        35. }
        36. try {
        37. LOG.info("hiveJdbcUrl:{}", hiveJdbcUrl);
        38. LOG.info("username:{}", username);
        39. LOG.info("password:{}", password);
        40. Connection conn = DriverManager.getConnection(hiveJdbcUrl, username, password);
        41. Statement stmt = conn.createStatement();
        42. String[] hiveSqls = hiveSql.split(";");
        43. for (int i = 0; i < hiveSqls.length; i++) {
        44. if (StringUtils.isNotEmpty(hiveSqls[i])) {
        45. stmt.execute(hiveSqls[i]);
        46. }
        47. }
        48. return true;
        49. } catch (SQLException sqlException) {
        50. LOG.error(sqlException.getMessage(), sqlException);
        51. return false;
        52. }
        53. }
        54. }

        HiveWriter.class

        1. package com.alibaba.datax.plugin.writer.hivewriter;
        2. import com.alibaba.datax.common.exception.DataXException;
        3. import com.alibaba.datax.common.plugin.RecordReceiver;
        4. import com.alibaba.datax.common.spi.Writer;
        5. import com.alibaba.datax.common.util.Configuration;
        6. import org.apache.commons.lang.StringEscapeUtils;
        7. import org.apache.commons.lang3.StringUtils;
        8. import org.apache.commons.lang3.time.FastDateFormat;
        9. import org.slf4j.Logger;
        10. import org.slf4j.LoggerFactory;
        11. import java.util.ArrayList;
        12. import java.util.Date;
        13. import java.util.List;
        14. //import java.util.UUID;
        15. //import com.alibaba.datax.common.util.KeyUtil;
        16. //import com.alibaba.datax.common.util.ShellUtil;
        17. public class HiveWriter extends Writer {
        18. public static class Job extends Writer.Job {
        19. private static final Logger log = LoggerFactory.getLogger(Job.class);
        20. private Configuration conf = null;
        21. private String defaultFS;
        22. private String tmpPath;
        23. private String tmpTableName;
        24. private String tempHdfsLocation;
        25. @Override
        26. public void init() {
        27. this.conf = super.getPluginJobConf();//获取配置文件信息{parameter 里面的参数}
        28. log.info("hive writer params:{}", conf.toJSON());
        29. //校验 参数配置
        30. log.info("HiveWriter流程说明[1:创建hive临时表 ;2:Reader的数据导入到临时表HDFS路径(无分区);3:临时表数据插入到目标表;4:删除临时表]");
        31. this.validateParameter();
        32. }
        33. private void validateParameter() {
        34. this.conf.getNecessaryValue(Key.DATABASE_NAME, HiveWriterErrorCode.REQUIRED_VALUE);
        35. this.conf.getNecessaryValue(Key.TABLE_NAME, HiveWriterErrorCode.REQUIRED_VALUE);
        36. this.conf.getNecessaryValue(Key.DEFAULT_FS, HiveWriterErrorCode.REQUIRED_VALUE);
        37. this.conf.getNecessaryValue(Key.HIVE_DATABASE_TMP_LOCATION, HiveWriterErrorCode.REQUIRED_VALUE);
        38. //Kerberos check
        39. Boolean haveKerberos = this.conf.getBool(Key.HAVE_KERBEROS, false);
        40. if (haveKerberos) {
        41. this.conf.getNecessaryValue(Key.KERBEROS_KEYTAB_FILE_PATH, HiveWriterErrorCode.REQUIRED_VALUE);
        42. this.conf.getNecessaryValue(Key.KERBEROS_PRINCIPAL, HiveWriterErrorCode.REQUIRED_VALUE);
        43. }
        44. }
        45. @Override
        46. public void prepare() {
        47. this.tempHdfsLocation = this.conf.getString(Key.HIVE_DATABASE_TMP_LOCATION);
        48. }
        49. @Override
        50. public List split(int mandatoryNumber) {
        51. this.defaultFS = this.conf.getString(Key.DEFAULT_FS);
        52. //按照reader 切分的情况来组织相同个数的writer配置文件 (reader channel writer)
        53. List configurations = new ArrayList(mandatoryNumber);
        54. for (int i = 0; i < mandatoryNumber; i++) {
        55. Configuration splitedTaskConfig = this.conf.clone();
        56. this.tmpTableName = hiveTableName();
        57. //判断set语句的结尾是否是/,不是给加一个
        58. if (!this.tempHdfsLocation.trim().endsWith("/")) {
        59. this.tempHdfsLocation = this.tempHdfsLocation + "/";
        60. }
        61. //创建临时Hive表,指定hive表在hdfs上的存储路径
        62. this.tmpPath = this.tempHdfsLocation + this.tmpTableName.toLowerCase();
        63. //后面需要指定写入的文件名称
        64. // String fileSuffix = UUID.randomUUID().toString().replace('-', '_');
        65. String fullFileName = String.format("%s%s/%s", defaultFS, this.tmpPath, this.tmpTableName);// 临时存储的文件路径
        66. splitedTaskConfig.set(Key.HIVE_DATABASE_TMP_LOCATION, tmpPath);
        67. splitedTaskConfig.set(Key.TMP_FULL_NAME, fullFileName);
        68. splitedTaskConfig.set(Key.TEMP_TABLE_NAME_PREFIX, this.tmpTableName);
        69. //分区字段解析 "dt","type"
        70. List partitions = this.conf.getList(Key.PARTITION, String.class);
        71. String partitionInfo = StringUtils.join(partitions, ",");
        72. splitedTaskConfig.set(Key.PARTITION, partitionInfo);
        73. configurations.add(splitedTaskConfig);
        74. }
        75. return configurations;
        76. }
        77. @Override
        78. public void post() {
        79. }
        80. @Override
        81. public void destroy() {
        82. }
        83. private String hiveTableName() {
        84. StringBuilder str = new StringBuilder();
        85. FastDateFormat fdf = FastDateFormat.getInstance("yyyyMMdd");
        86. str.append(Constants.TEMP_TABLE_NAME_PREFIX_DEFAULT).append(fdf.format(new Date()))
        87. // .append("_").append(KeyUtil.genUniqueKey());
        88. .append("_").append(System.currentTimeMillis());
        89. return str.toString();
        90. }
        91. }
        92. public static class Task extends Writer.Task {
        93. //写入hive步骤 (1)创建临时表 (2)读取数据写入临时表 (3) 从临时表写出数据
        94. private static final Logger LOG = LoggerFactory.getLogger(Task.class);
        95. private Configuration conf;
        96. private String defaultFS;
        97. private String username;
        98. private String password;
        99. private String hiveJdbcUrl;
        100. private String databaseName;
        101. private String tableName;//目标表名称
        102. private String writeMode;
        103. private String partition;
        104. private String tmpDataBase;
        105. private String tmpTableName;
        106. private boolean alreadyDel = false;
        107. private String hive_cmd;
        108. private String hive_sql_set;
        109. private HdfsHelper hdfsHelper = null;//工具类
        110. private String fieldDelimiter;
        111. private String hive_fieldDelimiter;
        112. private String compress;
        113. private String hive_target_table_compress_sql;
        114. private String hive_preSql;
        115. private String hive_postSql;
        116. @Override
        117. public void init() {
        118. this.conf = super.getPluginJobConf();
        119. //初始化每个task参数
        120. this.defaultFS = this.conf.getString(Key.DEFAULT_FS);
        121. this.username = this.conf.getString(Key.USERNAME);
        122. this.password = this.conf.getString(Key.PASSWORD);
        123. this.hiveJdbcUrl = this.conf.getString(Key.HIVE_JDBC_URL);
        124. this.databaseName = this.conf.getString(Key.DATABASE_NAME);
        125. this.tableName = this.conf.getString(Key.TABLE_NAME);
        126. this.partition = this.conf.getString(Key.PARTITION);
        127. this.writeMode = this.conf.getString(Key.WRITE_MODE, Constants.WRITE_MODE_DEFAULT);
        128. this.tmpDataBase = this.conf.getString(Key.HIVE_TMP_DATABASE, this.databaseName);
        129. this.tmpTableName = this.conf.getString(Key.TEMP_TABLE_NAME_PREFIX);
        130. this.hive_cmd = this.conf.getString(Key.HIVE_CMD, Constants.HIVE_CMD_DEFAULT);
        131. this.hive_sql_set = this.conf.getString(Key.HIVE_SQL_SET, Constants.HIVE_SQL_SET_DEFAULT);
        132. this.fieldDelimiter = this.conf.getString(Key.FIELD_DELIMITER, Constants.FIELDDELIMITER_DEFAULT);
        133. this.compress = this.conf.getString(Key.COMPRESS, Constants.COMPRESS_DEFAULT);
        134. this.hive_preSql = this.conf.getString(Key.HIVE_PRESQL, Constants.HIVE_PRESQL_DEFAULT);
        135. this.hive_postSql = this.conf.getString(Key.HIVE_POSTSQL, Constants.HIVE_POSTSQL_DEFAULT);
        136. this.hive_fieldDelimiter = this.fieldDelimiter;
        137. this.fieldDelimiter = StringEscapeUtils.unescapeJava(this.fieldDelimiter);
        138. this.conf.set(Key.FIELD_DELIMITER, this.fieldDelimiter);//设置hive 存储文件 hdfs默认的分隔符,传输时候会分隔
        139. this.conf.set(Key.COMPRESS, this.compress);
        140. this.hive_target_table_compress_sql = this.conf.getString(Key.HIVE_TARGET_TABLE_COMPRESS_SQL, Constants.HIVE_TARGET_TABLE_COMPRESS_SQL);
        141. //判断set语句的结尾是否是分号,不是给加一个
        142. if (!this.hive_sql_set.trim().endsWith(";")) {
        143. this.hive_sql_set = this.hive_sql_set + ";";
        144. }
        145. if (!this.hive_preSql.trim().endsWith(";")) {
        146. this.hive_preSql = this.hive_preSql + ";";
        147. }
        148. if (!this.hive_postSql.trim().endsWith(";")) {
        149. this.hive_postSql = this.hive_postSql + ";";
        150. }
        151. hdfsHelper = new HdfsHelper();
        152. hdfsHelper.getFileSystem(defaultFS, conf);
        153. }
        154. @Override
        155. public void prepare() {
        156. //创建临时表
        157. List columns = this.conf.getListConfiguration(Key.COLUMN);
        158. String columnsInfo = hdfsHelper.getColumnInfo(columns);
        159. String hive_presql_str = "";
        160. if (this.hive_preSql.equals("select 1;")) {
        161. hive_presql_str = "";
        162. } else if (StringUtils.isNotBlank(this.hive_preSql)) {
        163. String hivepresql_Info = this.hive_preSql;
        164. hive_presql_str = hivepresql_Info;
        165. }
        166. String hiveCmd = this.hive_sql_set + hive_presql_str + " use " + this.tmpDataBase + "; " +
        167. "create table " + this.tmpTableName + "(" + columnsInfo + ") " +
        168. " ROW FORMAT DELIMITED FIELDS TERMINATED BY '" + this.hive_fieldDelimiter + "' stored as TEXTFILE ";
        169. LOG.info("创建hive临时表 ----> :" + hiveCmd);
        170. //执行脚本,创建临时表
        171. if (!HiveServer2ConnectUtil.execHiveSql(this.username, this.password, hiveCmd, this.hiveJdbcUrl)) {
        172. throw DataXException.asDataXException(
        173. HiveWriterErrorCode.SHELL_ERROR,
        174. "创建hive临时表脚本执行失败");
        175. }
        176. // if (!ShellUtil.exec(new String[]{this.hive_cmd, "-e", "\"" + hiveCmd + "\""})) {
        177. // throw DataXException.asDataXException(
        178. // HiveWriterErrorCode.SHELL_ERROR,
        179. // "创建hive临时表脚本执行失败");
        180. // }
        181. addHook();
        182. LOG.info("创建hive 临时表结束 end!!!");
        183. }
        184. @Override
        185. public void startWrite(RecordReceiver lineReceiver) {
        186. List columns = this.conf.getListConfiguration(Key.COLUMN);
        187. String columnsStr = hdfsHelper.getColumnName(columns);
        188. // String columnsInfo=hdfsHelper.getColumnInfo(columns);
        189. // String[] columnsInfoStr = columnsInfo.split(",");
        190. // List list = new ArrayList<>();
        191. //
        192. // for (String c:columnsInfoStr){
        193. // list.add(c.split(" ")[0]);
        194. // }
        195. // String columnsStr = String.join(",",list);
        196. //(2)读取数据写入临时表,默认创建的临时表是textfile格式
        197. LOG.info("begin do write...");
        198. String fullFileName = this.conf.getString(Key.TMP_FULL_NAME);// 临时存储的文件路径
        199. LOG.info(String.format("write to file : [%s]", fullFileName));
        200. //写TEXT FILE
        201. hdfsHelper.textFileStartWrite(lineReceiver, this.conf, fullFileName, this.getTaskPluginCollector());
        202. LOG.info("end do write tmp text table");
        203. LOG.info("columnsStr:" + columnsStr);
        204. String writeModeSql = null;
        205. if (this.writeMode.equals("overwrite")) {
        206. writeModeSql = "overwrite";
        207. } else {
        208. writeModeSql = "into";
        209. }
        210. String partition_str = "";
        211. if (StringUtils.isNotBlank(this.partition)) {
        212. //获取分区字段
        213. String partitionInfo = this.partition;
        214. partition_str = " partition(" + partitionInfo + ") ";
        215. }
        216. //从临时表写入到目标表
        217. String insertCmd = this.hive_sql_set + " use " + this.databaseName + ";" +
        218. Constants.INSERT_PRE_SQL + this.hive_target_table_compress_sql +
        219. " insert " + writeModeSql + " table " + this.tableName + partition_str +
        220. " (" + columnsStr + ")" +
        221. " select " + columnsStr + " from " + this.tmpDataBase + "." + this.tmpTableName + ";";
        222. LOG.info("insertCmd ----> :" + insertCmd);
        223. //执行脚本,导入数据到目标hive表
        224. if (!HiveServer2ConnectUtil.execHiveSql(this.username, this.password, insertCmd, this.hiveJdbcUrl)) {
        225. throw DataXException.asDataXException(
        226. HiveWriterErrorCode.SHELL_ERROR,
        227. "导入数据到目标hive表失败");
        228. }
        229. // if (!ShellUtil.exec(new String[]{this.hive_cmd, "-e", "\"" + insertCmd + "\""})) {
        230. // throw DataXException.asDataXException(
        231. // HiveWriterErrorCode.SHELL_ERROR,
        232. // "导入数据到目标hive表失败");
        233. // }
        234. LOG.info("end do write");
        235. }
        236. @Override
        237. public void post() {
        238. LOG.info("one task hive write post...end");
        239. deleteTmpTable();
        240. }
        241. @Override
        242. public void destroy() {
        243. }
        244. private void addHook() {
        245. if (!alreadyDel) {
        246. Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
        247. @Override
        248. public void run() {
        249. deleteTmpTable();
        250. }
        251. }));
        252. }
        253. }
        254. private void deleteTmpTable() {
        255. String hive_postsql_str = "";
        256. if (this.hive_postSql.equals("select 1;")) {
        257. hive_postsql_str = "";
        258. } else if (StringUtils.isNotBlank(this.hive_postSql)) {
        259. //获取分区字段
        260. String hivepostsql_Info = this.hive_postSql;
        261. hive_postsql_str = hivepostsql_Info;
        262. }
        263. String hiveCmd = this.hive_sql_set + " use " + this.tmpDataBase + ";" +
        264. "drop table if exists " + tmpTableName + ";" + hive_postsql_str;//注意要删除的是临时表
        265. LOG.info("hiveCmd ----> :" + hiveCmd);
        266. //执行脚本,删除hive临时表
        267. if (!HiveServer2ConnectUtil.execHiveSql(this.username, this.password, hiveCmd, this.hiveJdbcUrl)) {
        268. throw DataXException.asDataXException(
        269. HiveWriterErrorCode.SHELL_ERROR,
        270. "删除hive临时表脚本执行失败");
        271. }
        272. // if (!ShellUtil.exec(new String[]{this.hive_cmd, "-e", "\"" + hiveCmd + "\""})) {
        273. // throw DataXException.asDataXException(
        274. // HiveWriterErrorCode.SHELL_ERROR,
        275. // "删除hive临时表脚本执行失败");
        276. // }
        277. alreadyDel = true;
        278. }
        279. }
        280. }

        HiveWriterErrorCode.class

        1. package com.alibaba.datax.plugin.writer.hivewriter;
        2. import com.alibaba.datax.common.spi.ErrorCode;
        3. public enum HiveWriterErrorCode implements ErrorCode {
        4. REQUIRED_VALUE("HiveWriter-00", "您缺失了必须填写的参数值."),
        5. SHELL_ERROR("HiveReader-06", "hive 脚本执行失败."),
        6. CONFIG_INVALID_EXCEPTION("HdfsWriter-00", "您的参数配置错误."),
        7. ILLEGAL_VALUE("HdfsWriter-02", "您填写的参数值不合法."),
        8. WRITER_FILE_WITH_CHARSET_ERROR("HdfsWriter-03", "您配置的编码未能正常写入."),
        9. Write_FILE_IO_ERROR("HdfsWriter-04", "您配置的文件在写入时出现IO异常."),
        10. WRITER_RUNTIME_EXCEPTION("HdfsWriter-05", "出现运行时异常, 请联系我们."),
        11. CONNECT_HDFS_IO_ERROR("HdfsWriter-06", "与HDFS建立连接时出现IO异常."),
        12. COLUMN_REQUIRED_VALUE("HdfsWriter-07", "您column配置中缺失了必须填写的参数值."),
        13. HDFS_RENAME_FILE_ERROR("HdfsWriter-08", "将文件移动到配置路径失败."),
        14. KERBEROS_LOGIN_ERROR("HdfsWriter-09", "KERBEROS认证失败");
        15. ;
        16. private final String code;
        17. private final String description;
        18. private HiveWriterErrorCode(String code, String description) {
        19. this.code = code;
        20. this.description = description;
        21. }
        22. @Override
        23. public String getCode() {
        24. return this.code;
        25. }
        26. @Override
        27. public String getDescription() {
        28. return this.description;
        29. }
        30. @Override
        31. public String toString() {
        32. return String.format("Code:[%s], Description:[%s].", this.code,
        33. this.description);
        34. }
        35. }

        Key.class

        1. package com.alibaba.datax.plugin.writer.hivewriter;
        2. public class Key {
        3. /**
        4. * 1.必选:defaultFS,databaseName,tableName,column,tmpDatabasePath,tmpDatabase
        5. * 2.可选(有缺省值):
        6. * writeMode(insert)
        7. * hive_cmd(hive)
        8. * fieldDelimiter(\u0001)
        9. * compress(gzip)
        10. * tmpTableName(tmp_datax_hivewriter_)
        11. * 3.可选(无缺省值):partition,fullFileName,encoding
        12. * */
        13. public final static String DEFAULT_FS = "defaultFS";
        14. public final static String USERNAME = "username";
        15. public final static String PASSWORD = "password";
        16. public final static String HIVE_JDBC_URL = "hiveJdbcUrl";
        17. public final static String DATABASE_NAME = "databaseName";//目标数据库名
        18. public final static String TABLE_NAME = "tableName";//目标表名
        19. public static final String WRITE_MODE = "writeMode";//表的写入方式insert、overwrite
        20. public static final String COLUMN = "column";//目标表的列
        21. public static final String NAME = "name";//目标表的字段名
        22. public static final String TYPE = "type";//目标表的字段类型
        23. public static final String PARTITION="partition";//分区字段
        24. public static final String HIVE_DATABASE_TMP_LOCATION="tmpDatabasePath";//临时hive表所在数据库的location路径
        25. public static final String HIVE_TMP_DATABASE="tmpDatabase";//临时HIVE表所在的数据库
        26. public static final String TEMP_TABLE_NAME_PREFIX="tmpTableName";//临时HIVE表名前缀
        27. public static final String TMP_FULL_NAME="fullFileName";//临时hive表 HDFS文件名称
        28. public static final String HIVE_CMD = "hive_cmd"; //hive
        29. public final static String HIVE_SQL_SET = "hive_sql_set";
        30. public static final String HIVE_PRESQL = "hive_preSql";
        31. public final static String HIVE_POSTSQL = "hive_postSql";
        32. public final static String HIVE_TARGET_TABLE_COMPRESS_SQL="hive_target_table_compress_sql";
        33. public static final String COMPRESS = "compress";//临时表压缩格式
        34. public static final String ENCODING="encoding";
        35. public static final String FIELD_DELIMITER="fieldDelimiter";
        36. public static final String NULL_FORMAT = "nullFormat";
        37. public static final String HAVE_KERBEROS = "haveKerberos";
        38. public static final String KERBEROS_KEYTAB_FILE_PATH = "kerberosKeytabFilePath";
        39. public static final String KERBEROS_PRINCIPAL = "kerberosPrincipal";
        40. public static final String HADOOP_CONFIG = "hadoopConfig";
        41. }
        SupportHiveDataType.class
        1. package com.alibaba.datax.plugin.writer.hivewriter;
        2. public enum SupportHiveDataType {
        3. TINYINT,
        4. SMALLINT,
        5. INT,
        6. BIGINT,
        7. FLOAT,
        8. DOUBLE,
        9. TIMESTAMP,
        10. DATE,
        11. STRING,
        12. VARCHAR,
        13. CHAR,
        14. BOOLEAN
        15. }

        plugin_job.template.json

        1. {
        2. "name": "hivewriter",
        3. "parameter": {
        4. "print":true,
        5. "username": "hive",
        6. "defaultFS": "hdfs://",
        7. "hiveJdbcUrl": "jdbc:hive2://",
        8. "databaseName": "default",
        9. "tableName": "t_user",
        10. "writeMode": "insert",
        11. "tmpDatabase":"tmp",
        12. "tmpDatabasePath":"/user/hive/warehouse/tmp.db/",
        13. "column": [
        14. {
        15. "name": "id",
        16. "type": "INT"
        17. },
        18. {
        19. "name": "username",
        20. "type": "STRING"
        21. },
        22. {
        23. "name": "password",
        24. "type": "STRING"
        25. }
        26. ]
        27. }
        28. }

         

        问题处理:

        1、ERROR tool.ImportTool: Encountered IOException running import job: java.io.IOException: Cannot run program "hive": error=2, No such file or directory

        原因是hive没有设置环境变量,根据你自己的路径配置环境变量

        vim /etc/profile  (切换root用户)

        source /etc/profile

        参考博客:

        datax同步hive到mongo_文大侠的博客-CSDN博客_datax hivereader

        https://github.com/deanxiao/DataX-HiveReader

        第3.6章:DataX访问Hive2拉取数据(拓展篇)_流木随风的博客-CSDN博客_datax连接hive

        【HDFS】mkdir: Permission denied: user=root, access=WRITE, inode="/":hdfs:supergroup:drwxr-xr-x - 灰信网(软件开发博客聚合)

        GitHub - deanxiao/DataX-HiveWriter: HiveWriter for alibaba DataX

      583. 相关阅读:
        北斗导航系统为渔船保驾护航,助力海洋渔业发展
        LocalDateTime的使用
        MySQL数据库管理基本操作(一)
        Spring Boot接收从前端传过来的数据常用方式以及处理的技巧
        Python 下载的 11 种姿势,一种比一种高级
        设计模式之 delegate 委托模式:Swift 实现
        Numpy(三)Numpy的函数与排序
        未来世界:16项改变人类社会的新技术
        手写RPC框架 第六天 负载均衡
        nginx配置IP白名单
      584. 原文地址:https://blog.csdn.net/Carson073/article/details/126156443