1、EmployeeDeptBean

package cn.sjq.bigdata.mr.equal.join;

import java.io.DataInput;

import java.io.DataOutput;

import java.io.IOException;

import org.apache.hadoop.io.WritableComparable;

/**

* Emp表和Dept表合并后的JavaBean

* 数据格式如下:

* Emp表:

* 7369,SMITH,CLERK,7902,1980/12/17,800,,20

7499,ALLEN,SALESMAN,7698,1981/2/20,1600,300,30

7521,WARD,SALESMAN,7698,1981/2/22,1250,500,30

Dept表:

10,ACCOUNTING,NEW YORK

20,RESEARCH,DALLAS

30,SALES,CHICAGO

40,OPERATIONS,BOSTON

要求:通过MapReduce处理后,输出数据格式为(要求按照DEPTNO升序,SAL降序):

DEPTNO EMPNO DNAME LOC ENAME JOB MGR HIREDATE SAL COMM

10 7369 ACCOUNTING NEW YORK SMITH SALESMAN 7902 17-DEC-80 1600 1400

如果需要对输出并排序,因此需要继承WritableComparator

* @author songjq

*

*/

public class EmployeeDeptBean implements WritableComparable<EmployeeDeptBean> {

//定义成员属性

private int deptNo = 0;

private int empno = 0;

private String dname = "";

private String loc = "";

private String ename = "";

private String job = "";

private int mgr = 0;

private String hiredate = "";

private float salary = 0f;

private float comm = 0f;

//定义emp表和dept表标志位flag 0:emp 1:dept

private int flag = 0;

public int getFlag() {

return flag;

}

public void setFlag(int flag) {

this.flag = flag;

}

public int getDeptNo() {

return deptNo;

}

public void setDeptNo(int deptNo) {

this.deptNo = deptNo;

}

public int getEmpno() {

return empno;

}

public void setEmpno(int empno) {

this.empno = empno;

}

public String getDname() {

return dname;

}

public void setDname(String dname) {

this.dname = dname;

}

public String getLoc() {

return loc;

}

public void setLoc(String loc) {

this.loc = loc;

}

public String getEname() {

return ename;

}

public void setEname(String ename) {

this.ename = ename;

}

public String getJob() {

return job;

}

public void setJob(String job) {

this.job = job;

}

public int getMgr() {

return mgr;

}

public void setMgr(int mgr) {

this.mgr = mgr;

}

public String getHiredate() {

return hiredate;

}

public void setHiredate(String hiredate) {

this.hiredate = hiredate;

}

public float getSalary() {

return salary;

}

public void setSalary(float salary) {

this.salary = salary;

}

public float getComm() {

return comm;

}

public void setComm(float comm) {

this.comm = comm;

}

public EmployeeDeptBean() {

}

public EmployeeDeptBean(int deptNo, int empno, String dname, String loc, String ename, String job, int mgr,

String hiredate, float salary, float comm, int flag) {

this.deptNo = deptNo;

this.empno = empno;

this.dname = dname;

this.loc = loc;

this.ename = ename;

this.job = job;

this.mgr = mgr;

this.hiredate = hiredate;

this.salary = salary;

this.comm = comm;

this.flag = flag;

}

/*

* 反序列化

* (non-Javadoc)

* @see org.apache.hadoop.io.Writable#readFields(java.io.DataInput)

*/

@Override

public void readFields(DataInput in) throws IOException {

this.deptNo = in.readInt();

this.empno = in.readInt();

this.dname = in.readUTF();

this.loc = in.readUTF();

this.ename = in.readUTF();

this.job = in.readUTF();

this.mgr = in.readInt();

this.hiredate = in.readUTF();

this.salary = in.readFloat();

this.comm = in.readFloat();

this.flag = in.readInt();

}

/*

* 序列化

* (non-Javadoc)

* @see org.apache.hadoop.io.Writable#write(java.io.DataOutput)

*/

@Override

public void write(DataOutput out) throws IOException {

out.writeInt(this.deptNo);

out.writeInt(this.empno);

out.writeUTF(this.dname);

out.writeUTF(this.loc);

out.writeUTF(this.ename);

out.writeUTF(this.job);

out.writeInt(this.mgr);

out.writeUTF(this.hiredate);

out.writeFloat(this.salary);

out.writeFloat(this.comm);

out.writeInt(this.flag);

}

/*

* 对象比较

* (non-Javadoc)

* @see java.lang.Comparable#compareTo(java.lang.Object)

*/

@Override

public int compareTo(EmployeeDeptBean o) {

//第一步:比较部门编号,升序排列

if(this.deptNo>o.getDeptNo()) {

return 1;

}else if(this.deptNo<o.getDeptNo()) {

return -1;

}

//第二步:比较薪资,降序排列

if(this.salary>=this.getSalary()) {

return -1;

}else {

return 1;

}

}

/*

*  重写toString方法

* (non-Javadoc)

* @see java.lang.Object#toString()

*/

@Override

public String toString() {

// DEPTNO EMPNO DNAME LOC ENAME JOB MGR HIREDATE SAL COMM

// 10 7369 ACCOUNTING NEW YORK SMITH SALESMAN 7902 17-DEC-80 1600 1400

return this.deptNo + "\t" + this.empno + "\t" + formatStr(this.dname, 12) + formatStr(this.ename, 12)

+ formatStr(this.job, 12) + formatStr(String.valueOf(this.mgr), 6) + formatStr(this.hiredate, 12)

+ formatStr(String.valueOf(this.salary), 6) + "\t" + formatStr(String.valueOf(this.comm), 6) + "\t" + formatStr(this.loc, 16);

}

/**

* 字符串填充空格

* @param str

* @param length

* @return

*/

public static String formatStr(String str, int length)

   {

     if (str == null)

       {

         str="";

       }

     int strLen = str.getBytes().length;

     if (strLen == length)

       {

         return str;

       } else if (strLen < length)

       {

         int temp = length - strLen;

         String tem = "";

         for (int i = 0; i < temp; i++)

           {

             tem = tem + " ";

           }

         return str + tem;

       } else

       {

         return str.substring(0, length);

       }

   }

}

2、EmpDeptEqualJoin

package cn.sjq.bigdata.mr.equal.join;

import java.io.IOException;

import java.text.NumberFormat;

import java.util.ArrayList;

import java.util.Iterator;

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.LongWritable;

import org.apache.hadoop.io.NullWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Job;

import org.apache.hadoop.mapreduce.Mapper;

import org.apache.hadoop.mapreduce.Reducer;

import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;

import org.apache.hadoop.mapreduce.lib.input.FileSplit;

import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import org.junit.Test;

/**

* 所有的Mapper类、Reducer类、Job类均在本类采用采用匿名内部类实现

* @author songjq

*

*/

public class EmpDeptEqualJoin {

/**

* Mapper端

* /scott下存在emp.csv,dept.csv

* 输入路径:/scott

* k1:输入偏移量

* v1:输入数据,输入可能为emp表,也可能为dept表

* k2:部门号DeptNo

* v2:输出EmployeeDeptBean

*

* 原理:这里主要利用MapReduce相同key输出到相同Reduce处理特性来实现表的等值连接

* 这里将所有相同部门号的部门信息及相同部门号的员工信息输出到同一个reduce进行合并处理

* @author songjq

*

*/

static class EmpDeptEqualJoinMapper extends Mapper<LongWritable, Text, IntWritable, EmployeeDeptBean> {

private EmployeeDeptBean ed = null;

private IntWritable tkey = new IntWritable();

@Override

protected void map(LongWritable k1, Text v1, Context context) throws IOException, InterruptedException {

// 获取输入数据,可能为emp.csv,也可能为dept.csv

String line = v1.toString();

// 分词

String[] fileds = line.split(",");

// 获取文件输入对象

FileSplit filesplit = (FileSplit) context.getInputSplit();

// 获取输入文件名称

String fname = filesplit.getPath().getName();

ed = new EmployeeDeptBean();

if (fname.equals("emp.csv")) {

// 7369,SMITH,CLERK,7902,1980/12/17,800,,20

ed.setDeptNo(Integer.parseInt(fileds[7]));

ed.setFlag(0);

ed.setEmpno(Integer.parseInt(fileds[0]));

ed.setEname(fileds[1]);

ed.setJob(fileds[2]);

try {

ed.setMgr(Integer.parseInt(fileds[3]));

}catch (Exception e) {

ed.setMgr(0);

}

ed.setHiredate(fileds[4]);

try {

// 防止fileds[5]为空抛空指针异常

ed.setSalary(Float.parseFloat(fileds[5]));

} catch (Exception e) {

ed.setSalary(0);

}

try {

// 防止fileds[6]为空抛空指针异常

ed.setComm(Float.parseFloat(fileds[6]));

} catch (Exception e) {

ed.setComm(0);

}

} else if (fname.equals("dept.csv")) {

ed.setFlag(1);

ed.setDeptNo(Integer.parseInt(fileds[0]));

ed.setDname(fileds[1]);

ed.setLoc(fileds[2]);

}

// 将EmployeeDeptBean对象输出到Reducer

tkey.set(ed.getDeptNo());

context.write(tkey , ed);

}

}

/**

* Reducer端

* k3:部门号

* v3:EmployeeDeptBean集合

* k4:NullWritable

* v4:EmployeeDeptBean

* 原理说明:

* 利用相同Key输出到相同的Reduce处理这一特性,将部门信息和员工信息进行等值合并(利用部门号相同这一条件)

* @author songjq

*

*/

static class EmpDeptEqualJoinReducer

extends Reducer<IntWritable, EmployeeDeptBean, NullWritable, EmployeeDeptBean> {

@Override

protected void reduce(IntWritable k3, Iterable<EmployeeDeptBean> v3, Context ctx)

throws IOException, InterruptedException {

Iterator<EmployeeDeptBean> iterator = v3.iterator();

//定义存放员工信息对象list

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

//定义部门属性信息

String dname = "";

String loc = "";

while(iterator.hasNext()) {

EmployeeDeptBean ed = iterator.next();

//定义emp表和dept表标志位flag, 0:emp 1:dept

if(ed.getFlag()==0) {

//不能直接list.add(ed);ed为同一个对象

EmployeeDeptBean edtmp = new EmployeeDeptBean();

//也不能直接将edtmp = ed;

edtmp.setDeptNo(ed.getDeptNo());

edtmp.setEname(ed.getEname());

edtmp.setEmpno(ed.getEmpno());

edtmp.setMgr(ed.getMgr());

edtmp.setJob(ed.getJob());

edtmp.setHiredate(ed.getHiredate());

edtmp.setSalary(ed.getSalary());

edtmp.setComm(ed.getComm());

list.add(edtmp);

}else if(ed.getFlag()==1) {

dname = ed.getDname();

loc = ed.getLoc();

}

}

//将部门信息合并到员工对象list

for(EmployeeDeptBean fulled:list) {

fulled.setDname(dname);

fulled.setLoc(loc);

ctx.write(NullWritable.get(), fulled);

}

}

}

/**

* 提交job到hadoop集群执行

* @param args

* @throws Exception

*/

@Test

public void EmpDeptEqualJoinJob() throws Exception {

//定义job

Job job = Job.getInstance(new Configuration());

//设置mapper及输出key,value数据类型

job.setMapperClass(EmpDeptEqualJoinMapper.class);

job.setMapOutputKeyClass(IntWritable.class);

job.setMapOutputValueClass(EmployeeDeptBean.class);

//设置reducer及输出key,value数据类型

job.setReducerClass(EmpDeptEqualJoinReducer.class);

job.setOutputKeyClass(NullWritable.class);

job.setOutputValueClass(EmployeeDeptBean.class);

//设置输入输出路径

FileInputFormat.setInputPaths(job, new Path("D:\\test\\tmp\\join\\equal"));

FileOutputFormat.setOutputPath(job, new Path("D:\\test\\tmp\\join\\equal_out1"));

//提交任务job

job.waitForCompletion(true);

}

}

3、执行结果

10 7934 ACCOUNTING  MILLER      CLERK       7782  1982/1/23   1300.0 0.0   NEW YORK        

10 7839 ACCOUNTING  KING        PRESIDENT   0     1981/11/17  5000.0 0.0   NEW YORK        

10 7782 ACCOUNTING  CLARK       MANAGER     7839  1981/6/9    2450.0 0.0   NEW YORK        

20 7876 RESEARCH    ADAMS       CLERK       7788  1987/5/23   1100.0 0.0   DALLAS          

20 7788 RESEARCH    SCOTT       ANALYST     7566  1987/4/19   3000.0 0.0   DALLAS          

20 7369 RESEARCH    SMITH       CLERK       7902  1980/12/17  800.0 0.0   DALLAS          

20 7566 RESEARCH    JONES       MANAGER     7839  1981/4/2    2975.0 0.0   DALLAS          

20 7902 RESEARCH    FORD        ANALYST     7566  1981/12/3   3000.0 0.0   DALLAS          

30 7844 SALES       TURNER      SALESMAN    7698  1981/9/8    1500.0 0.0   CHICAGO        

30 7499 SALES       ALLEN       SALESMAN    7698  1981/2/20   1600.0 300.0 CHICAGO        

30 7698 SALES       BLAKE       MANAGER     7839  1981/5/1    2850.0 0.0   CHICAGO        

30 7654 SALES       MARTIN      SALESMAN    7698  1981/9/28   1250.0 1400.0 CHICAGO        

30 7521 SALES       WARD        SALESMAN    7698  1981/2/22   1250.0 500.0 CHICAGO        

30 7900 SALES       JAMES       CLERK       7698  1981/12/3   950.0 0.0   CHICAGO