大数据项目连接工具类JDBCUtil、HBaseUtil、MyKafkaUtil、SparkUtil,四大工具类代码分享

一、 项目分层开发

**好处**

  1. 代码重用:通过将项目分解为多个层次,可以在不同的层次上重用代码,减少重复工作。
  2. 模块化:每个层次负责特定的功能,使得代码更易于维护和扩展。
  3. 降低耦合度:各层次之间的依赖关系降低,有利于团队协作和项目的可维护性。
  4. 提高可读性和可理解性:通过明确的层次结构,可以更容易地理解代码的功能和实现。
  5. 便于测试和调试:各层次可以独立进行测试和调试,提高了开发效率。

二、项目中Util工具类有什么作用?

项目中的Util工具类主要用于提供一些通用的、实用的功能和方法,以简化开发过程、提高代码可读性和可维护性。这些功能可能包括字符串处理、日期时间操作、文件操作、数学计算等。通过将这些通用功能封装在一个工具类中,可以避免在多个地方重复编写相同的代码,同时方便其他开发人员使用。

三、 MySQL连接工具类JDBCUtil

直接上代码

package org.niit.util


import com.alibaba.druid.pool.DruidDataSourceFactory

import java.sql.{Connection, PreparedStatement, ResultSet}
import java.util.Properties
import javax.sql.DataSource

/**
 * @作者 Yan
 * @时间 2023/4/24 10:43
 * @文件: JDBCUtil
 * @项目 org.niit.util
 */
object JDBCUtil {
  //初始化连接池
  var dataSource: DataSource = init()


  def init(): DataSource = {
    // 创建一个名为properties的Propertie
    val properties = new Properties()
    // 设置数据库驱动类名为com.mysql.jdbc.Driver
    properties.setProperty("driverClassName", "com.mysql.jdbc.Driver")
    // 设置数据库连接URL,指定要连接的主机为node1,端口为3306,数据库名为Takeaway,字符编码为UTF-8
    properties.setProperty("url", "jdbc:mysql://node1:3306/Takeaway?useUnicode=true&characterEncoding=UTF-8")
    // 设置数据库用户名为root
    properties.setProperty("username", "root")
    // 置数据库密码为Niit@123
    properties.setProperty("password", "Niit@123")
    // 设置最大活动连接数为50
    properties.setProperty("maxActive", "50")
    // 使用DruidDataSourceFactory工厂类创建数据源对象,并传入properties作为参数进行配置
    DruidDataSourceFactory.createDataSource(properties)
  }
  /*
  该函数的主要目的是初始化一个名为DataSource的数据源对象。在函数内部,首先创建了一个Properties对象,
  然后通过调用setProperty方法来设置各种属性,包括数据库驱动类名、连接URL、用户名、密码和最大活动连接数等。
  最后,使用DruidDataSourceFactory工厂类的createDataSource方法根据这些属性创建了数据源对象,并将其赋值给变量DataSource。
   */

  def getConnection: Connection = {
    // 调用 getConnection 函数获取数据库连接
    dataSource.getConnection
  }

  def executeUpdata(connection: Connection, sql: String, params: Array[Any]): Unit = {
    var rtn = 0
    var pstmt: PreparedStatement = null
    try {
      connection.setAutoCommit(false)
      pstmt = connection.prepareStatement(sql)

      if (params != null && params.length > 0) {
        for (i <- params.indices) {
          pstmt.setObject(i + 1, params(i))
        }
      }
      rtn = pstmt.executeUpdate()
      connection.commit()
      pstmt.close()
    } catch {
      case e: Exception => e.printStackTrace()
    }
    rtn
  }
  /*
  函数的主要逻辑如下:
  将数据库连接对象的自动提交属性设置为false,以便手动控制事务的提交。
  通过调用connection.prepareStatement(sql)方法创建一个预编译的SQL语句对象。
  如果提供了参数值,则使用循环将参数值设置到预编译语句对象中。这里假设params是一个可索引的对象,例如列表或数组。
  调用预编译语句对象的executeUpdate()方法执行更新操作,并将受影响的行数赋值给变量rtn。
  如果没有发生异常,则提交事务并关闭预编译语句对象。
  如果发生异常,则打印异常堆栈跟踪信息。
  最后,返回受影响的行数。
   */

  def isExist(connection: Connection, sql: String, params: Array[Any]): Boolean = {
    var flag: Boolean = false
    var pstmt: PreparedStatement = null
    try {
      pstmt = connection.prepareStatement(sql)
      for (i <- params.indices) {
        pstmt.setObject(i + 1, params(i))
      }
      flag = pstmt.executeQuery().next()
      pstmt.close()
    } catch {
      case e: Exception => e.printStackTrace()
    }
    flag
  }
  /*
  定义一个布尔变量flag,初始值为false,用于存储查询结果的状态。
  定义一个PreparedStatement类型的变量pstmt,并将其初始化为null,用于执行预编译的SQL查询。
  使用try-catch块来捕获可能发生的异常。
  在try块中,通过调用connection.prepareStatement(sql)方法创建一个预编译的SQL查询语句对象,并将其赋值给pstmt。
  使用循环遍历params数组中的每个元素,并使用pstmt.setObject()方法将参数设置到预编译的SQL查询中。
  调用pstmt.executeQuery().next()方法执行查询,并将结果存储在flag变量中。如果查询返回至少一行结果,则flag将被设置为true,否则保持为false。
  关闭预编译的SQL查询语句对象,释放资源。
  在catch块中,捕获异常并打印堆栈跟踪信息。
  最后,返回flag变量作为函数的结果。简而言之,这段代码的作用是执行给定的SQL查询,并根据查询结果的状态返回一个布尔值。如果查询返回至少一行结果,则返回true,否则返回false。
   */

  def getDataFromMysql(connection: Connection, sql: String, params: Array[Any]):
  Long = {
    var result: Long = 0L
    var pstmt: PreparedStatement = null
    try {
      pstmt = connection.prepareStatement(sql)
      for (i <- params.indices) {
        pstmt.setObject(i + 1, params(i))
      }
      val resultSet: ResultSet = pstmt.executeQuery()
      while (resultSet.next()) {
        result = resultSet.getLong(1)
      }
      resultSet.close()
      pstmt.close()
    } catch {
      case e: Exception => e.printStackTrace()
    }
    result
  }
  /*
  函数的主要逻辑如下:

  首先,声明了一个变量result,用于存储查询结果,初始化为0。
  然后,声明了一个变量pstmt,用于存储预编译的SQL语句对象,初始化为null。
  在try块中,使用传入的连接对象connection调用prepareStatement方法来创建一个预编译的SQL语句对象,并将其赋值给pstmt。
  通过循环遍历params数组,将每个参数设置到预编译的SQL语句对象中对应的位置上。这里使用了Scala中的增强型for循环语法。
  调用预编译的SQL语句对象的executeQuery方法执行查询,并将结果集赋值给resultSet。
  通过循环遍历结果集,每次调用next方法将游标移动到下一行记录,然后使用getLong方法获取第一列的值,并将其赋值给result。
  最后,关闭结果集和预编译的SQL语句对象。
  如果在执行过程中发生异常,会进入catch块,打印异常堆栈信息。
  最终返回查询结果result。
   */


}

四、 HBaseUtil工具类

package org.niit.util

import org.apache.hadoop.hbase.client._
import org.apache.hadoop.hbase.util.Bytes
import org.apache.hadoop.hbase.{Cell, HBaseConfiguration, TableName}

import java.util

/**
 * @作者 Yan
 * @时间 2023/4/24 10:51
 * @文件: HBaseUtil
 * @项目 org.niit.util
 */
object HBaseUtil {

  private val conn = init()

  private var hTable: Table = _;

  def getConnection: Connection = {
    conn
  }

  //1. 声明名为“init”的方法,返回类型为“连接”。
  //2. HBaseConfiguration 对象是使用 HBaseConfiguration.create() 方法创建的。
  //3. HBaseConfiguration 对象配置为将 ZooKeeper 仲裁设置为“node1”。
  //4. 使用 ConnectionFactory.createConnection() 方法创建一个连接,传入 HBaseConfiguration 对象。
  //5. 从方法返回连接。

  def putData(put: Put): Unit = {
    hTable.put(put)
    hTable.close()
    conn.close()
  }

  def putDatas(puts: java.util.List[Put]): Unit = {

    hTable.put(puts)
    hTable.close()
    conn.close()
  }

  def main(args: Array[String]): Unit = {
    HBaseUtil.setHTable("bigdata:student")
    //val puts = new util.ArrayList[Put]()
    //    val put: Put = new Put(Bytes.toBytes("yyy1111"))
    //    put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("age"), Bytes.toBytes("20"))
    //    //puts.add(put)
    //    HBaseUtil.putData(put)
    val get = new Get(Bytes.toBytes("yyy1111"))
    val value = HBaseUtil.getData(get)
    println(value)

  }

  def setHTable(tableName: String): Table = {
    val hbaseTableName: TableName = TableName.valueOf(tableName)
    val hbaseTable = conn.getTable(hbaseTableName)
    hTable = hbaseTable
    hbaseTable
  }
  /*
  首先,函数使用TableName.valueOf(tableName)将传入的字符串参数tableName转换为TableName类型的对象,并将其赋值给变量hbaseTableName。
  接下来,通过调用conn.getTable(hbaseTableName)方法,获取与指定表名对应的HBase表对象,并将其赋值给变量hbaseTable。
  然后,将hbaseTable赋值给变量hTable,以便在函数外部使用该表对象。
  最后,函数返回hbaseTable,即获取到的HBase表对象。
   */

  def getData(get: Get): List[String] = {
    var value = List[String]()
    val result = hTable.get(get)
    val cells: util.List[Cell] = result.listCells()
    cells.forEach(c => {
      var res = Bytes.toString(c.getValueArray, c.getValueOffset, c.getValueLength)
      value = value :+ res
    })
    value
  }
  /*
  首先,创建一个空的字符串列表value,用于存储从hTable中获取的数据。
  调用hTable.get(get)方法,将参数get传递给它,并将结果赋值给变量result。
  从result中获取单元格列表,将其赋值给变量cells。
  使用forEach循环遍历每个单元格对象c。
  在循环体内部,首先使用Bytes.toString()方法将单元格的值数组转换为字符串。该方法接受三个参数:值数组、值偏移量和值长度。
  将转换后的字符串添加到value列表中,使用冒号操作符实现链式操作。
  最后,返回填充了数据的value列表。
   */

  private def init(): Connection = {
    val conf = HBaseConfiguration.create()
    conf.set("hbase.zookeeper.quorum", "node1")
    val connection = ConnectionFactory.createConnection(conf)
    connection
  }
  /*
  private def init(): Connection = {:这是一个私有方法,返回类型为Connection,方法名为init。它没有参数。
  val conf = HBaseConfiguration.create():创建一个HBase配置对象conf。HBaseConfiguration是Hadoop中用于配置HBase的类。
  conf.set("hbase.zookeeper.quorum", "node1"):设置HBase的ZooKeeper集群地址。"hbase.zookeeper.quorum"是HBase配置文件中的一个属性,用于指定ZooKeeper集群的地址,这里设置为"node1"。
  val connection = ConnectionFactory.createConnection(conf):使用之前创建的配置对象conf创建一个HBase连接,并将连接对象赋值给变量connection。ConnectionFactory是Hadoop中用于创建连接的工厂类。
  connection:这是对上一步创建的连接对象的引用。
  }:结束方法定义。
  这段代码的主要作用是创建一个HBase连接,并将其存储在connection变量中。

   */

}

五、 Kafka工具类MyKafkaUtil

package org.niit.util

import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.spark.streaming.StreamingContext
import org.apache.spark.streaming.dstream.InputDStream
import org.apache.spark.streaming.kafka010.{ConsumerStrategies, KafkaUtils, LocationStrategies}

/**
 * @作者 Yan
 * @时间 2023/4/24 10:52
 * @文件: MyKafkaUtil
 * @项目 org.niit.util
 */
object MyKafkaUtil {

  var kafkaParam = Map(
    "bootstrap.servers" -> "node1:9092",
    "key.deserializer" -> classOf[StringDeserializer],
    "value.deserializer" -> classOf[StringDeserializer],
    //如果没有初始化偏移量或者当前的偏移量不存在任何服务器上,可以使用这个配置属性
    //可以使用这个配置,latest 自动重置偏移量为最新的偏移量
    "auto.offset.reset" -> "earliest",
    //如果是 true,则这个消费者的偏移量会在后台自动提交,但是 kafka 宕机容易丢失数据
    //如果是 false,会需要手动维护 kafka 偏移量
    "enable.auto.commit" -> (true: java.lang.Boolean)
  )

  def getKafkaStream(groupId: String, topic: String, ssc: StreamingContext):
  InputDStream[ConsumerRecord[String, String]] = {

    kafkaParam = kafkaParam ++ Map("group.id" -> groupId)

    val dStream: InputDStream[ConsumerRecord[String, String]] =
      KafkaUtils.createDirectStream[String, String](ssc,
        LocationStrategies.PreferConsistent, ConsumerStrategies.Subscribe[String,
          String](Array(topic), kafkaParam))
    dStream
  }

  //这段代码定义了一个名为 getKafkaStream 的函数,其返回类型是 InputDStream[ConsumerRecord[String, String]],用于创建 Kafka 流并将其用于流式处理。
  //该函数的参数包括 groupId,topic 和 ssc。
  //该函数内部创建了一个 kafkaParam 对象,该对象包含了 groupId 参数的键值对,并将其添加到了一个 Map 对象中。
  //然后,该函数使用 KafkaUtils.createDirectStream 方法创建了一个 Kafka 直接流,并将其用于流式处理。
  // 该方法的参数包括 ssc,LocationStrategies.PreferConsistent,表示该流应使用位于 Kafka 集群中心的位置策略,
  // ConsumerStrategies.Subscribe[String, String](Array(topic), kafkaParam),表示该流应使用订阅策略来订阅 Kafka 主题,
  // 并将 kafkaParam 对象作为参数传递给该策略。
  //最后,该函数返回创建的 Kafka 直接流对象。

}

六、 spark工具类SparkUtil

package org.niit.util

import org.apache.spark.sql.SparkSession
import org.apache.spark.streaming.{Duration, Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @作者 Yan
 * @时间 2023/4/21 13:56
 * @文件: SparkUtil
 * @项目 org.niit.util
 */
object SparkUtil {
  private val scLocal = new ThreadLocal[SparkContext] //专门存储SC的线程池
  private val sparkLocal = new ThreadLocal[SparkSession]
  private val sscLocal = new ThreadLocal[StreamingContext]
  private var sc: SparkContext = _;
  private var seconds: Duration = _;
  private var ssc: StreamingContext = _;
  private var spark: SparkSession = _;

  def CreateSpark(sparkConf: SparkConf, seconds: Duration = Seconds(10)): SparkContext = {

    if (sc == null) {
      spark = SparkSession.builder().config(sparkConf).getOrCreate()
      sc = spark.sparkContext
      putSC(sc)
      putSpark(spark)

      if (this.seconds != seconds) {
        sscLocal.remove()
        ssc = new StreamingContext(sc, seconds)
        this.seconds = seconds
      };
      putSSC(ssc)
    }
    sc
  }
  /*
  首先,函数检查是否已经存在一个Spark上下文(sc),如果不存在,则执行以下操作:
  
  使用给定的sparkConf构建或获取一个SparkSession,并将其赋值给变量spark。
  从spark中获取Spark上下文,并将其赋值给变量sc。
  将sc存储在一个私有变量中(可能是类的成员变量)。
  将spark存储在一个私有变量中(可能是类的成员变量)。
  如果当前实例的seconds属性与传入的seconds参数不相等,则执行以下操作:
  移除本地的流式计算上下文(sscLocal)。
  使用新的sc和传入的seconds参数创建一个新的流式计算上下文(ssc)。
  将新的seconds参数存储在当前实例的属性中。
  将新的流式计算上下文(ssc)存储在一个私有变量中(可能是类的成员变量)。
  最后,返回Spark上下文(sc)。
   */

  private def putSC(sc: SparkContext): Unit = {
    scLocal.set(sc)
  }

  private def putSpark(spark: SparkSession): Unit = {
    sparkLocal.set(spark)
  }

  private def putSSC(ssc: StreamingContext): Unit = {
    sscLocal.set(ssc)
  }

  def getOrCreateStreamingContext(sparkContext: SparkContext, seconds: Duration): StreamingContext = {


    if (this.seconds != seconds) {
      sscLocal.remove()
      ssc = new StreamingContext(sparkContext, seconds)
      this.seconds = seconds
      putSSC(ssc)
    }
    ssc
  }
  /*
  首先,函数会检查当前实例中的seconds属性是否与传入的seconds参数相等。如果不相等,说明需要创建一个新的StreamingContext对象。
  在创建新的StreamingContext对象之前,会先移除当前实例中可能已经存在的旧的StreamingContext对象(通过调用sscLocal.remove()方法)。然后,使用传入的sparkContext和seconds参数创建一个新的StreamingContext对象,并将其赋值给变量ssc。
  接下来,将传入的seconds参数赋值给当前实例的seconds属性,以便下次调用时可以进行比较。最后,调用putSSC(ssc)方法将新创建的StreamingContext对象存储起来(具体实现细节未给出)。
  如果当前实例中的seconds属性与传入的参数相等,则直接返回当前实例中的StreamingContext对象(即变量ssc)。
  总结一下,这段代码的功能是根据传入的参数来获取或创建一个StreamingContext对象,并在需要时更新相关属性和存储信息。
   */

  def takeSC(): SparkContext = {
    scLocal.get()//返回一个 SparkContext 对象,通过调用 scLocal.get() 方法从本地变量 scLocal 中获取。
  }

  def takeSpark(): SparkSession = {
    sparkLocal.get()  //返回一个 SparkSession 对象,通过调用 sparkLocal.get() 方法从本地变量 sparkLocal 中获取。
  }

  def takeSSC(): StreamingContext = {
    sscLocal.get()//返回一个 StreamingContext 对象,通过调用 sscLocal.get() 方法从本地变量 sscLocal 中获取。
  }

  def clear(): Unit = {
    scLocal.remove()
    sparkLocal.remove()
    sscLocal.remove()
  }
  //函数体内有三个语句,它们的作用是从当前线程的本地变量中删除名为"scLocal"、"sparkLocal"和"sscLocal"的变量。
  //分布式计算框架Apache Spark相关的本地变量。本地变量是在当前线程中定义的变量,只能在该线程中访问和修改。
  //删除这些本地变量可能是为了避免内存泄漏或其他问题,或者为了确保每个线程都有自己的实例,以避免线程安全问题。

}

七、总结

分层开发是现在程序员必备知识,如何使用分层开发呢?

  1. 确定项目的需求和功能,将其分解为多个层次。通常包括表示层、业务逻辑层和数据访问层等。
  2. 在每个层次上编写相应的代码,实现各自的功能。例如,在表示层编写用户界面代码,在业务逻辑层编写处理业务逻辑的代码,在数据访问层编写与数据库交互的代码。
  3. 保持各层次之间的低耦合度,避免在一个层次中修改导致其他层次出现问题。可以通过接口、抽象类等方式实现。
  4. 在需要的地方调用其他层次的代码,完成特定功能。例如,在业务逻辑层调用数据访问层的代码来获取数据。
  5. 对项目进行单元测试和集成测试,确保各层次的功能正确无误。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

严同学正在努力

老板发财!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值