赞
踩
该文章旨在通过Scala语言实现JDBC的创建,以熟悉Scala语言的使用。
在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")
参数:url,username,password
执行器的创建需要依赖连接对象,因此先初始化连接再初始化执行器。
参数:sql,parameters
JDBC的创建实际上就是包含了两个操作步骤和一个多返回类型设计的小型化任务。
def jdbc(url:String,username:String,password:String)(sql:String,params:Seq[Any]=null):Unit{
}
多操作过程可以写成柯里化的形式,不仅实现了参数分组
,同时还隐含了一种参数间的依赖关系
params
不一定会有,并且可能同时包含多种不同的数据类型。
因此可以通过可变参数T*
或者序列Seq[T]
的方式进行表示。
同时,默认情况下不传参,因此指定一个默认值为null
。
结果类型包括:
JDBC的结果类型包含了两种正常类型和一种异常类型,自带的Option
、Either
或Try
都无法满足这种需求,我们的解决方式如下:
ResultType
的枚举类型,它包含三个值:EX
,DQL
和DML
。Three
,它包含了一个类型为ResultType.Value
的构造参数,这个参数用来表示具体的结果类型。此处选择抽象类是因为需要传递一个构造参数,这种设计允许在继承Three
的子类中具体化不同类型的结果处理(差异化处理)。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 } }
asInstanceOf[T]
的方法实现向具体子类的转化id = rst.getInt(1)
这类语句是通过字段序号代替了字段名称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) }
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) } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。