Scala应用 —— JDBC的创建

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
实时计算 Flink 版,5000CU*H 3个月
实时数仓Hologres,5000CU*H 100GB 3个月
简介: 这篇文章介绍了如何使用Scala实现JDBC连接。首先,通过在pom.xml添加MySQL JDBC驱动依赖,然后使用`Class.forName()`加载驱动,接着创建连接对象。初始化执行器涉及创建执行器对象和设置参数。执行操作时,根据DML(数据修改语言)和DQL(数据查询语言)返回不同结果。文章提出了一个柯里化的`jdbc`函数,以处理不同操作步骤和多类型结果。结果类型通过枚举和抽象类`Three`的子类来表示,包括异常、DML影响行数和DQL查询结果。最后,展示了`jdbc`方法的实现,以及如何处理结果并转换为具体对象。代码示例中,查询结果被转换为`Test`对象数组并打印。

image.png
image.png

@[TOC]

Scala应用 —— JDBC的创建

前言

该文章旨在通过Scala语言实现JDBC的创建,以熟悉Scala语言的使用。

一、JDBC的创建过程

1.初始化连接
1.1 配置驱动

在pom.xml中打入以下依赖,向项目中打入MySQL JDBC驱动

<!-- MySQL 驱动 -->
<dependency>
  <groupId>com.mysql</groupId>
  <artifactId>mysql-connector-j</artifactId>
  <version>8.0.33</version>
</dependency>

该语句用于加载MySQL JDBC驱动。

Class.forName("com.mysql.cj.jdbc.Driver")
1.2 创建连接对象

参数:url,username,password

2. 初始化执行器

执行器的创建需要依赖连接对象,因此先初始化连接再初始化执行器。

2.1 创建执行器对象

参数:sql,parameters

2.2 初始化执行器参数
3. 执行操作并返回结果
  • DML:影响数据库的表行数
  • DQL:List
  • Exception:异常

二、Scala JDBC的基本设计思路

JDBC的创建实际上就是==包含了两个操作步骤和一个多返回类型设计的==小型化任务。

1. 操作步骤设计
def jdbc(url:String,username:String,password:String)(sql:String,params:Seq[Any]=null):Unit{

}
  • 多操作过程可以写成柯里化的形式,不仅实现了参数分组,同时还隐含了一种参数间的依赖关系

  • params不一定会有,并且可能同时包含多种不同的数据类型。

    因此可以通过可变参数T*或者序列Seq[T]的方式进行表示。

    同时,默认情况下不传参,因此指定一个默认值为null

    • Any*
    • Seq[Any]
2. 解决结果差异化

结果类型包括:

  • DML:影响数据库的表行数
  • DQL:List
  • Exception:异常

JDBC的结果类型包含了两种正常类型和一种异常类型,自带的OptionEitherTry都无法满足这种需求,我们的解决方式如下:

  1. 首先定义了一个名为ResultType的枚举类型,它包含三个值:EX,DQLDML
  2. 定义了一个抽象类Three,它包含了一个类型为ResultType.Value的构造参数,==这个参数用来表示具体的结果类型==。此处选择抽象类是因为需要传递一个构造参数,这种设计==允许在继承Three的子类中具体化不同类型的结果处理==(差异化处理)。
  3. 三个样例类(Ex,DML,和 DQL)继承自抽象类 Three,每个样例类都对应一个 ResultType 的值,并封装了与其类型相关的数据。
object ResultType extends Enumeration{
  val EX,DQL,DML = Value
}

abstract class Three(val rst:ResultType.Value)

case class Ex(throwable: Throwable) extends Three(ResultType.EX){
  def ex = throwable
}

case class DML(affectedRows:Int) extends Three(ResultType.DML){
  def updated = affectedRows
}

case class DQL(set: ResultSet) extends Three(ResultType.DQL){
  /**
     * 为什么要将(f:ResultSet=>T)独立为一个方法的参数?
     * 减少不必要的类型约束,不需要每次创建DQL对象都需要指定泛型。
     * */
  def generate[T](f:ResultSet=>T)(implicit ct:ClassTag[T])={
    val buffer:ArrayBuffer[T] = ArrayBuffer()
    // 遍历结果集(包含由一次查询返回的所有行),用f将结果集的每一行转化为一个实体
    while (set.next()) {
      buffer.append(f(set))
    }
    buffer.toArray
  }
}
3.实现jdbc方法并输出结果
  • 基类通过asInstanceOf[T]的方法实现向具体子类的转化
  • id = rst.getInt(1)这类语句是通过字段序号代替了字段名称
    image.png
def jdbc(url: String, username: String, password: String)(sql: String, params: Seq[Any] = null): Three = {
  def conn(): Connection = {
    // 1.1 装载驱动
    Class.forName("com.mysql.cj.jdbc.Driver")
    // 1.2 创建连接对象
    val conn: Connection = DriverManager.getConnection(url, username, password)
    conn
  }

  def pst(conn: Connection): PreparedStatement = {
    // 2.1 创建执行对象
    val pst: PreparedStatement = conn.prepareStatement(sql)
    // 2.2 设置sql配置为(序号,参数)的格式
    if (null != params && params.nonEmpty) {
      params.zipWithIndex.foreach {
        // 设置执行对象对应的SQL语句`?`对应的占位符。
        case (param, index) => pst.setObject(index + 1, param)
      }
    }
    pst
  }

  try {
    val connect: Connection = conn
    val statement: PreparedStatement = pst(connect)
    // 过程级增删改查(数据记录):INSERT DELETE UPDATE SELECT
    // 对象级增删改查(对象——表、视图、索引):CREATE DROP ALTER SHOW
    sql match {
      case sql if sql.matches("SELECT|select") => DQL(statement.executeQuery())
      case sql if sql.matches("INSERT|insert|DELETE|delete|UPDATE|update") => DML(statement.executeUpdate())
      // 处理SQL语句异常
      case _ => Ex(new SQLException(s"illegal sql command:$sql"))
    }
  } catch {
    // 其他异常
    case e: Exception => Ex(e)
  }
}

def main(args: Array[String]): Unit = {
  val dql: DQL = jdbc(
    url = "jdbc:mysql://single01:3306/test_db_for_bigdata",
    username = "root",
    password = "123456"
  )(
    sql = "SELECT * FROM test_table1_for_hbase_import LIMIT 20"
  ).asInstanceOf[DQL]

  // 将结果集对应的字段设置为样例类,自动生成getter方法
  case class Test(id: Int, name: String, age: Int, gender: String, phone: String)

  // 将结果集的每一行转化为一个Test对象
  val tests: Array[Test] = dql.generate[Test](rst => Test(
    id = rst.getInt(1),
    name = rst.getString(2),
    age = rst.getInt(3),
    gender = rst.getString(4),
    phone = rst.getString(5)
  ))
  tests.foreach(println)
}

三、代码汇总与结果

1. 代码
package recovery

import java.sql.{Connection, DriverManager, PreparedStatement, ResultSet, SQLException}
import scala.collection.mutable.ArrayBuffer
import scala.reflect.ClassTag

object JDBCTest2 {
  object ResultType extends Enumeration{
    val EX,DQL,DML = Value
  }

  abstract class Three(val rst:ResultType.Value)

  case class Ex(throwable: Throwable) extends Three(ResultType.EX){
    def ex = throwable
  }

  case class DML(affectedRows:Int) extends Three(ResultType.DML){
    def updated = affectedRows
  }

  case class DQL(set: ResultSet) extends Three(ResultType.DQL){
    /**
     * 为什么要将(f:ResultSet=>T)独立为一个方法的参数?
     * 减少不必要的类型约束,不需要每次创建DQL对象都需要指定泛型。
     * */
    def generate[T](f:ResultSet=>T)(implicit ct:ClassTag[T])={
      val buffer:ArrayBuffer[T] = ArrayBuffer()
      // 遍历结果集(包含由一次查询返回的所有行),用f将结果集的每一行转化为一个实体
      while (set.next()) {
        buffer.append(f(set))
      }
      buffer.toArray
    }
  }

  def jdbc(url: String, username: String, password: String)(sql: String, params: Seq[Any] = null): Three = {
    def conn(): Connection = {
      // 1.1 装载驱动
      Class.forName("com.mysql.cj.jdbc.Driver")
      // 1.2 创建连接对象
      val conn: Connection = DriverManager.getConnection(url, username, password)
      conn
    }

    def pst(conn: Connection): PreparedStatement = {
      val pst: PreparedStatement = conn.prepareStatement(sql)
      if (null != params && params.nonEmpty) {
        params.zipWithIndex.foreach {
          // 设置执行对象对应的SQL语句`?`对应的占位符。
          case (param, index) => pst.setObject(index + 1, param)
        }
      }
      pst
    }

    try {
      val connect: Connection = conn
      val statement: PreparedStatement = pst(connect)
      // 过程级增删改查(数据记录):INSERT DELETE UPDATE SELECT
      // 对象级增删改查(对象——表、视图、索引):CREATE DROP ALTER SHOW
      sql match {
        case sql if sql.matches("SELECT|select") => DQL(statement.executeQuery())
        case sql if sql.matches("INSERT|insert|DELETE|delete|UPDATE|update") => DML(statement.executeUpdate())
        case _ => Ex(new SQLException(s"illegal sql command:$sql"))
      }
    } catch {
      case e: Exception => Ex(e)
    }
  }

    def main(args: Array[String]): Unit = {
    val result = jdbc(
      url = "jdbc:mysql://single01:3306/test_db_for_bigdata",
      username = "root",
      password = "123456"
    )(
      sql = "SELECT * FROM test_table1_for_hbase_import LIMIT 20;"
    )

    result match {
      case dql: DQL =>
        case class Test(id: Int, name: String, age: Int, gender: String, phone: String)
        val tests: Array[Test] = dql.generate[Test](rst => Test(
          id = rst.getInt(1),
          name = rst.getString(2),
          age = rst.getInt(3),
          gender = rst.getString(4),
          phone = rst.getString(5)
        ))
        tests.foreach(println)

      case ex: Ex =>
        println("Error occurred: " + ex.ex.getMessage)
    }
  }
2.结果

image.png
image.png

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
1月前
|
SQL 分布式计算 Java
大数据-96 Spark 集群 SparkSQL Scala编写SQL操作SparkSQL的数据源:JSON、CSV、JDBC、Hive
大数据-96 Spark 集群 SparkSQL Scala编写SQL操作SparkSQL的数据源:JSON、CSV、JDBC、Hive
33 0
|
6月前
|
数据采集 JSON 数据处理
一步步实现知乎热榜采集:Scala与Sttp库的应用
使用Scala和Sttp库,结合代理IP,本文阐述了爬取并处理知乎热榜数据的方法。首先,确保安装Scala和SBT,然后在`build.sbt`引入Sttp等相关依赖。代码中,设置代理服务器信息、User-Agent和Cookie,发送GET请求获取数据。解析JSON数据后,归类和统计不同类型条目的数量,例如文章和问题。运行示例输出归类和统计结果,为数据分析提供基础。
一步步实现知乎热榜采集:Scala与Sttp库的应用
|
Oracle 关系型数据库 Java
分享一个 Oracle RAC 模式下客户端建立JDBC初始连接时因ONS造成应用启动时卡顿30秒问题的排查分析案例
分享一个 Oracle RAC 模式下客户端建立JDBC初始连接时因ONS造成应用启动时卡顿30秒问题的排查分析案例
|
设计模式 Oracle 关系型数据库
设计模式之桥接模式 附JDBC的源码应用解析
设计模式之桥接模式 附JDBC的源码应用解析
149 0
|
Java Scala 开发者
Scala 下界介绍和应用实例 | 学习笔记
快速学习 Scala 下界介绍和应用实例
Scala 下界介绍和应用实例 | 学习笔记
|
Scala 开发者
Scala 上界介绍和应用实例2 | 学习笔记
快速学习Scala上界介绍和应用实例2
Scala 上界介绍和应用实例2 | 学习笔记
|
Java Scala 开发者
Scala 泛型介绍和应用实例2 | 学习笔记
快速学习 Scala 泛型介绍和应用实例2
Scala 泛型介绍和应用实例2 | 学习笔记
|
SQL 缓存 监控
JDBC 在性能测试中的应用
我们能否绕开 http 协议,直接测试数据库的性能?是否觉得从数据库中导出 CSV 文件来构造压测数据很麻烦?怎样在压测结束后做数据清理?能不能通过数据库中的插入(删除)记录对压测请求做断言?使用阿里云性能测试工具 PTS 可以轻松解决上述问题。
JDBC 在性能测试中的应用
|
SQL 缓存 Java
JDBC 在性能测试中的应用
什么是 JDBC JDBC(Java DataBase Connectivity,Java 数据库连接)是一种用于执行 SQL 语句的 Java API,可以为多种关系数据库提供统一访问,它由一组用 Java 语言编写的类和接口组成。JDBC 提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。 简单地说,JDBC 可做三件事:与数据库建立连接、发送操作数据库的语句并处理结果。
162 0
|
Java Scala
Scala学习笔记(六) Scala的偏函数和偏应用函数
Scala学习笔记(六) Scala的偏函数和偏应用函数
250 0