Spark中POJO与Dataset相互转换

本文介绍如何在Spark中实现POJO与Dataset之间的互相转换,包括直接转换的方法及通过JSON过渡解决类型不兼容问题的技术细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0x0 Dataset转POJO

方法:

  1. 将查询出的结果转为RDD
  2. 将RDD创建为DataFrame,并传入schema参数
  3. 调用as方法,将Dataset转为相应的POJO Dataset
  4. 调用collectAsList()方法

代码如下:

1.表结构

+--------+---------+-------+
|col_name|data_type|comment|
+--------+---------+-------+
|      id|   string|   null|
|    name|   string|   null|
|   class|   string|   null|
+--------+---------+-------+

2.POJO类

public class Student {
    String id;
    String name;
    String major;
    ...
}

3.转换代码

SparkSession spark = CloudUtils.getSparkSession();

        // 查询原始数据
        Dataset<Row> student = spark.sql("select * from `event`.`student`");
        // 生成schema
        List<StructField> fields = new ArrayList<>();
        fields.add(DataTypes.createStructField("id", DataTypes.StringType, true));
        fields.add(DataTypes.createStructField("name", DataTypes.StringType, true));
        fields.add(DataTypes.createStructField("major", DataTypes.StringType, true));
        StructType schema = DataTypes.createStructType(fields);

        // 转换查询结果为POJO List
        List<Student> students = spark.createDataFrame(student.toJavaRDD(), schema)
                .as(Encoders.bean(Student.class))
                .collectAsList();
        System.out.println(students);

注意:
Dataset中的日期类型为timestamp和java中的Date类型不兼容,和Timestamp类型相互兼容。
为了解决上述问题,我们可以先将Dataset转为JSON,然后将JSON转为POJO,代码如下:

        // 查出数据并转为json集合
        List<String> jsonList = spark.sql("select * from `event`.`user`")
                .toJSON()
                .collectAsList();
        // 将json转为pojo,这里使用的是FastJSON        
        List<User> users = jsonList.stream()
                .map(jsonString -> JSON.parseObject(jsonString, User.class))
                .collect(Collectors.toList());
        System.out.println(users);

0x1 POJO转Dataset

1.表结构

+---------+---------+-------+
|col_name |data_type|comment|
+---------+---------+-------+
| user_id |   string|   null|
|user_name|   string|   null|
|user_age |   int   |   null|
+---------+---------+-------+

2.POJO类

public class User{
    String userId;
    String userName;
    Integer userAge;
    ...
}

转换代码:

        // 获取users列表
        List<User> users = createUsers();
        // 使用createDataFrame转为dataset
        Dataset<Row> ds = spark.createDataFrame(users, User.class);
        // 将驼峰式列名改为下划线式列名,camelToUnderline方法网上搜索
        String[] columns = ds.columns();
        String[] newColumns = Arrays.stream(columns)
                .map(column -> camelToUnderline(column))
                .toArray(String[]::new);
        // 转为新的df(重命名后的)
        ds.toDF(newColumns);
        ds.show();

同样注意:
对于有些类型无法转换的情况,仍然采用json过渡,代码如下:

        // 创建user list
        List<User> users = createUsers();
        // 将user list转为json list
        List<String> jsonList = users.stream()
                .map(JSON::toJSONString)
                .collect(Collectors.toList());
        // 将json list转为json dataset
        Dataset<String> jsonDataset = spark.createDataset(jsonList, Encoders.STRING());
        // 转换为row dataset
        Dataset<Row> ds = spark.read().json(jsonDataset.toJavaRDD());
        ds.show();

输出结果:

+------------+---+----+
|    birthday| id|name|
+------------+---+----+
|689875200000|  1| AAA|
|689875200000|  2| BBB|
+------------+---+----+
以下是一个基本的Java代码示例,用于将MySQL中的数据同步到Hudi: ```java import org.apache.hudi.DataSourceWriteOptions; import org.apache.hudi.HoodieSparkUtils; import org.apache.hudi.OverwriteWithLatestAvroPayload; import org.apache.hudi.QuickstartUtils; import org.apache.hudi.api.HoodieWriteClient; import org.apache.hudi.common.model.HoodieTableType; import org.apache.hudi.config.HoodieWriteConfig; import org.apache.spark.api.java.JavaRDD; import org.apache.spark.api.java.JavaSparkContext; import org.apache.spark.sql.Dataset; import org.apache.spark.sql.Row; import org.apache.spark.sql.SparkSession; import java.util.Collections; import java.util.Properties; public class MySQLToHudiSync { public static void main(String[] args) { String tableName = "hudi_table"; String basePath = "file:///tmp/hudi_table"; String jdbcUrl = "jdbc:mysql://<mysql_host>:<mysql_port>/<mysql_db>"; String username = "<mysql_username>"; String password = "<mysql_password>"; String partitionKey = "id"; String hudiTableType = HoodieTableType.COPY_ON_WRITE.name(); SparkSession spark = SparkSession.builder().appName("MySQLToHudiSync").config("spark.serializer", "org.apache.spark.serializer.KryoSerializer").getOrCreate(); JavaSparkContext jsc = new JavaSparkContext(spark.sparkContext()); Properties connectionProperties = new Properties(); connectionProperties.put("user", username); connectionProperties.put("password", password); Dataset<Row> jdbcDF = spark.read().jdbc(jdbcUrl, tableName, connectionProperties); JavaRDD<Row> rowsRDD = jdbcDF.javaRDD(); HoodieWriteConfig hoodieWriteConfig = HoodieWriteConfig.newBuilder().withPath(basePath) .withSchema(QuickstartUtils.getSchema()).withParallelism(2, 2) .withBulkInsertParallelism(3).withFinalizeWriteParallelism(2) .withStorageConfig(HoodieSparkUtils.getDefaultHoodieConf(jsc.hadoopConfiguration())) .withAutoCommit(false).withTableType(hudiTableType) .forTable(tableName).withIndexConfig(HoodieIndexConfig.newBuilder().withIndexType(HoodieIndex.IndexType.BLOOM).build()) .withProps(Collections.singletonMap(DataSourceWriteOptions.RECORDKEY_FIELD_OPT_KEY().key(), partitionKey)).build(); HoodieWriteClient hoodieWriteClient = new HoodieWriteClient<>(jsc, hoodieWriteConfig); hoodieWriteClient.upsert(rowsRDD.map(row -> { String key = row.getAs(partitionKey).toString(); return new UpsertPOJO(key, row); }), hoodieWriteConfig.getBasePath(), hoodieWriteConfig.getTableType(), OverwriteWithLatestAvroPayload.class.getName()); hoodieWriteClient.commit(); } public static class UpsertPOJO implements Serializable { private String key; private Row row; public UpsertPOJO(String key, Row row) { this.key = key; this.row = row; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public Row getRow() { return row; } public void setRow(Row row) { this.row = row; } } } ``` 代码中的`tableName`是要同步的MySQL表的名称,`basePath`是Hudi表的根路径。`jdbcUrl`,`username`和`password`是连接MySQL所需的凭据。`partitionKey`是Hudi表中用作分区键的字段名称。`hudiTableType`是Hudi表的类型,可以是`COPY_ON_WRITE`或`MERGE_ON_READ`。 代码中使用`HoodieWriteConfig`对象配置Hudi写入选项,例如`withPath`,`withSchema`,`withParallelism`,`withBulkInsertParallelism`等。`forTable`方法指定Hudi表的名称。`withIndexConfig`方法配置Hudi索引选项,例如索引类型和配置。`withProps`方法设置自定义属性。`withAutoCommit`方法用于控制提交方式,可以是自动提交或手动提交。 最后,使用`HoodieWriteClient`对象将MySQL数据插入Hudi表,使用`upsert`方法进行插入。`UpsertPOJO`类是自定义的POJO类,用于将MySQL中的行转换为要插入到Hudi表中的数据。`commit`方法用于提交更改。 请注意,此代码示例仅用于演示目的,并且可能需要进行修改以适应您的特定需求。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值