**好处**
:
项目中的Util工具类主要用于提供一些通用的、实用的功能和方法,以简化开发过程、提高代码可读性和可维护性。这些功能可能包括字符串处理、日期时间操作、文件操作、数学计算等。通过将这些通用功能封装在一个工具类中,可以避免在多个地方重复编写相同的代码,同时方便其他开发人员使用。
直接上代码
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。
*/
}
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变量中。
*/
}
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 直接流对象。
}
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相关的本地变量。本地变量是在当前线程中定义的变量,只能在该线程中访问和修改。
//删除这些本地变量可能是为了避免内存泄漏或其他问题,或者为了确保每个线程都有自己的实例,以避免线程安全问题。
}
分层开发是现在程序员必备知识,如何使用分层开发呢?