最近在做oracle到hbase的数据迁移,有一张表有blob字段,在导入时发现没有导入成功,在网上找了好久,都没有搞定,最后没办法只能研究一下sqoop的源码,自己解决了。
在研究源码后,发现sqoop中已经有了关于blob字段的处理方法了,但不知道什么原因,对hbase没有正确处理,存在bug。主要做如下修改:
在org.apache.sqoop.mapreduce.HBaseImportMapper类中,增加大字段的处理。
首先增加如下代码:
这是在初始化时,初始化大对象读取器,构造函数中最后一个是null,如果单个文件大于设定的最大值(--inline-lob-limit属性,默认是16M),会报错,这块需要先写在hdfs上,也可以自己写一个hdfs的路径。
private LargeObjectLoader lobLoader;
protected void setup(Context context)
throws IOException, InterruptedException {
this.lobLoader = new LargeObjectLoader(context.getConfiguration(),
null);
}
public void map(LongWritable key, SqoopRecord val, Context context)
throws IOException, InterruptedException {
/**原来的hbase map中是没有调用大对象的方法,在此处增加**/
try {
// Loading of LOBs was delayed until we have a Context.
val.loadLargeObjects(lobLoader);
} catch (SQLException sqlE) {
throw new IOException(sqlE);
}
context.write(val, NullWritable.get());
}
增加完以后代码后,会发现导入到hbase的图片,比实际的大好多,我的大了一倍左右,且图片无法正常显示,这是因为sqoop在将程序入库时,字符转换有问题,可以做如下操作:
在org.apache.sqoop.hbase.ToStringPutTransformer方法中,增加如下方法:
/**
* 如果是blob类型,转成string在转成byte后,结果会发生变化,有时候数据量会增大一倍,会出错。
* @author sking modify 2014-12-25
* @param val
* @return
*/
private byte[] toHBaseBytes(Object val) {
if (val instanceof BlobRef){
return ((BlobRef)val).getData();
}else{
return Bytes.toBytes(toHBaseString(val));
}
}
并将 private List<Put> putRecordInHBase(Map<String, Object> record,String colFamily, String rowKey)方法中
if (null != val) {
put.add(colFamilyBytes, getFieldNameBytes(colName),
Bytes.toBytes(toHBaseString(val)));
}
改成
if (null != val) {
put.add(colFamilyBytes, getFieldNameBytes(colName),