案例:(最下面)
一、以编程方式动态指定元数据,将RDD转换为DataFrame -->> RDD2DataFrameProgrammatically
二、使用反射的方式将RDD->>DataFrame -->> RDD2DataFrameReflect
RDD转换为DataFrame之后的话,我们就可以直接针对HDFS等任何可以构建为RDD的数据,使用Spark SQL进行SQL查询了。这个功能是无比强大的。这样就可以针对HDFS中的数据,直接就可以使用SQL进行查询。
Spark SQL支持两种方式来将RDD转换为DataFrame。
第一种方式,是使用反射来推断包含了特定数据类型的RDD的元数据。这种基于反射的方式,代码比较简洁,当你已经知道你的RDD的元数据时,是一种非常不错的方式。
第二种方式,是通过编程接口来创建DataFrame,你可以在程序运行时动态构建一份元数据,然后将其应用到已经存在的RDD上。这种方式的代码比较冗长,但是如果在编写程序时,还不知道RDD的元数据,只有在程序运行时,才能动态得知其元数据,那么只能通过这种动态构建元数据的方式。
Java版本:Spark SQL是支持将包含了JavaBean的RDD转换为DataFrame的。JavaBean的信息,就定义了元数据。Spark SQL现在是不支持将包含了嵌套JavaBean或者List等复杂数据的JavaBean,作为元数据的。只支持一个包含简单数据类型的field的JavaBean。
Scala版本:而Scala由于其具有隐式转换的特性,所以Spark SQL的Scala接口,是支持自动将包含了case class的RDD转换为DataFrame的。case class就定义了元数据。Spark SQL会通过反射读取传递给case class的参数的名称,然后将其作为列名。与Java不同的是,Spark SQL是支持将包含了嵌套数据结构的case class作为元数据的,比如包含了Array等。
Java版本:当JavaBean无法预先定义和知道的时候,比如要动态从一个文件中读取数据结构,那么就只能用编程方式动态指定元数据了。首先要从原始RDD创建一个元素为Row的RDD;其次要创建一个StructType,来代表Row;最后将动态定义的元数据应用到RDD
Scala版本:Scala的实现方式,与Java是基本一样的。
DataFrame.javaRDD() 或 DataFrame.RDD()
java版本:
package Spark_SQL;
import org.apache.spark.SparkConf;
import org.apache.spark.SparkContext;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.SQLContext;
import java.util.List;
/**
* @Date: 2019/3/12 14:36
* @Author Angle
*/
/*
* 使用反射的方式将RDD->>DataFrame
*
* */
public class RDD2DataFrameReflect {
public static void main(String[] args){
//创建普通的RDD
SparkConf conf = new SparkConf().setAppName("RDD2DataFramReflect").setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);
SQLContext sqlContext = new SQLContext(sc);
JavaRDD lines = sc.textFile("E:\\IDEA\\textFile\\stu_RDD2Data.txt");
JavaRDD students = lines.map(new Function() {
@Override
public Student call(String line) throws Exception {
String[] lineSpilt = line.split(",");
Student stu = new Student();
stu.setId(Integer.valueOf(lineSpilt[0].trim()));
stu.setName(lineSpilt[1]);
stu.setAge(Integer.valueOf(lineSpilt[2].trim()));
return stu;
}
});
//利用反射将RDD转化为DataFrame
//将Student.class传入就是利用了反射,它是反射的一个应用
//底层通过Student Class进行反射获取其中的field
Dataset studentDF = sqlContext.createDataFrame(students, Student.class);
//拿到DataFrame表后,将其注册为一个临时表,针对其数据进行SQL
//这里要求JavaBean实现序列化接口Serializable
studentDF.registerTempTable("student");
//针对临时表操作SQL-->>>查询年龄小于18
Dataset teenagerDF = sqlContext.sql("select * from student where age<=18");
//查出来的DataFrame,转化成RDD
JavaRDD teenagerRDD = teenagerDF.javaRDD();
//将RDD的数据进行映射,映射为Student
JavaRDD teeStudentRDD = teenagerRDD.map(new Function() {
@Override
public Student call(Row row) throws Exception {
//row中数据的顺序可能和期望不同
Student stu = new Student();
stu.setAge(row.getInt(0));
stu.setId(row.getInt(1));
stu.setName(row.getString(2));
return stu;
}
});
//将数据collect回来,打印出来
List studentList = teeStudentRDD.collect();
for (Student stu:studentList){
System.out.println(stu);
}
}
}
scala版本 :
package SparkSQL_Scala
import org.apache.spark.sql.SQLContext
import org.apache.spark.{SparkConf, SparkContext}
/**
* @Date: 2019/3/12 16:48
* @Author Angle
*/
/*
* 要用scala开发Spark程序
*在其中要实现基于反射RDD到DataFrame的转换,必须要用object extends App方式
* 而不能用def main()方式来运行,否则会报错:no typetag for...class
*
* */
object RDD2DataFrameReflect_s extends App {
val conf = new SparkConf().setAppName("RDD2DataFrameReflect_S").setMaster("local")
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
//在Scala中使用反射进行RDD-->>DataFrame转换,需要手动导入隐式转换
//import sqlContext.implicits._
//样例类
case class Student(id:Int,name:String,age:Int)
val lines = sc.textFile("E:\\IDEA\\textFile\\stu_RDD2Data.txt")
//拿到普通的Student的RDD(元素为case class)
val students = lines.map(line => line.split(","))
.map(arr => Student(arr(0).trim.toInt,arr(1),arr(2).trim.toInt))
//创建DataFrame,传入RDD
val studentDF = sqlContext.createDataFrame(students)
//注册临时表
studentDF.registerTempTable("students");
val teenagerDF = sqlContext.sql("select * from students where age<=18")
//转换成RDD
val teenagerRDD = teenagerDF.rdd
//顺序--> age-name-id
//在scala中row中数据的顺序,是按照我们期望的排序的,和java不一样
teenagerRDD.map(row => Student(row(0).toString.toInt,row(1).toString,row(2).toString.toInt))
.collect()
.foreach(stu => println(stu.id + ":" + stu.name + ":" + stu.age))
//在scala中对row的使用比java的row更加丰富
//scala中可以用row的getAs()方法,获取指定列名的列
teenagerRDD.map(row => Student(
row.getAs[Int]("id"), row.getAs[String]("name"),row.getAs[Int]("age")))
.collect()
.foreach(stu => println(stu.id + ":" + stu.name + ":" +stu.age))
//可以通过row的getValuesMap()方法,获取指定几列的值,返回的是个map
teenagerRDD.map(row => {
val map = row.getValuesMap[Any](Array("id","name","age"))
Student(map("id").toString.toInt,map("name").toString,map("age").toString.toInt)
})
.collect()
.foreach(stu => println(stu.id + ":" + stu.name + ":" + stu.age))
}
Java版本:
package Spark_SQL;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.RowFactory;
import org.apache.spark.sql.SQLContext;
import org.apache.spark.sql.types.DataType;
import org.apache.spark.sql.types.DataTypes;
import org.apache.spark.sql.types.StructField;
import org.apache.spark.sql.types.StructType;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
/**
* @Date: 2019/3/13 18:59
* @Author Angle
*/
/*
* 以编程方式动态指定元数据,将RDD转换为DataFrame
*
* */
public class RDD2DataFrameProgrammatically {
public static void main(String[] args){
SparkConf conf = new SparkConf().setAppName("RDD2DataFrameProgrammatically").setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);
SQLContext sqlContext = new SQLContext(sc);
//一、创建普通的RDD,但要转换为RDD的格式
JavaRDD linesRDD = sc.textFile("E:\\IDEA\\textFile\\stu_RDD2Data.txt");
//往Row中塞数据的时候要注意:类型!!
JavaRDD studentRDD = linesRDD.map(new Function() {
@Override
public Row call(String line) throws Exception {
String[] lineSpilt = line.split(",");
//后面要用到Integer类型,所以应该把String类型转化为Integer
return RowFactory.create(Integer.valueOf(lineSpilt[0]),lineSpilt[1],Integer.valueOf(lineSpilt[2]));
}
});
//二、动态构造元数据
//如id,name等类型,可能在程序运行过程中,从mysql或配置文件加载,是不固定的--->>适合这种动态构造
ArrayList structFields = new ArrayList();
structFields.add(DataTypes.createStructField("id",DataTypes.IntegerType,true));
structFields.add(DataTypes.createStructField("name",DataTypes.StringType,true));
structFields.add(DataTypes.createStructField("age",DataTypes.IntegerType,true));
StructType structType = DataTypes.createStructType(structFields);
//三、将RDD转化为DataFrame
Dataset stuDF= sqlContext.createDataFrame(studentRDD,structType);
//注册为student_s表
stuDF.registerTempTable("student_s");
Dataset teenagerDF = sqlContext.sql("select * from student_s where age<=18");
//数据拉取到本地
List rows = teenagerDF.javaRDD().collect();
for (Row row:rows){
System.out.println(row);
}
}
}
scala版本:
package SparkSQL_Scala
import org.apache.spark.sql.types.{IntegerType, StringType, StructField, StructType}
import org.apache.spark.sql.{Row, SQLContext}
import org.apache.spark.{SparkConf, SparkContext}
/**
* @Date: 2019/3/13 20:22
* @Author Angle
*/
object RDD2DataFrameProgrammatically_s {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("RDD2DataFrameProgrammatically_s").setMaster("local")
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
//一、构造元素为Row的普通的RDD
val lines = sc.textFile("E:\\IDEA\\textFile\\stu_RDD2Data.txt",1)
.map{line => Row(line.split(",")(0).toInt,line.split(",")(1),line.split(",")(2).toInt)}
//二、编程方式动态构造元数据
val structType = StructType(Array(
StructField("id",IntegerType,true),
StructField("name",StringType,true),
StructField("age",IntegerType,true)))
//三、进行RDD到DataFrame的转换
val studentDF = sqlContext.createDataFrame(lines,structType)
//注册
studentDF.registerTempTable("student_s")
val teenagerDF = sqlContext.sql("select * from student_s where age<=19")
val teenagerRDD = teenagerDF.rdd.collect()
.foreach(row => println("id:" + row.get(0) + " name:" + row.get(1) + " age:" +row.get(2)))
}
}
接下来还会慢慢更新Spark相关知识,有问题还请指正