未验证 提交 fa49a683 编写于 作者: 梦境迷离's avatar 梦境迷离 提交者: GitHub

add ResultSetTransformer (#248)

* add ResultSetTransformer
上级 2c5dba61
......@@ -34,6 +34,7 @@
- 通用的宏操作API的封装。
- 对象转换器(零依赖,类型安全)。
- JDBC `ResultSet`对象转样例类。
```scala
"org.bitlap" %% "smt-common" % "<VERSION>"
......
version in ThisBuild := "0.8.1-SNAPSHOT"
version in ThisBuild := "0.9.0-SNAPSHOT"
......@@ -16,6 +16,7 @@ lazy val scalatestVersion = "3.2.14"
lazy val scalaLoggingVersion = "3.9.5"
lazy val log4jVersion = "2.19.0"
lazy val scalaCollectionCompatVersion = "2.8.1"
lazy val h2 = "2.1.214"
lazy val commonSettings =
Seq(
......@@ -57,7 +58,10 @@ lazy val `smt-common` = (project in file("smt-common"))
.settings(commonSettings)
.settings(
name := "smt-common",
crossScalaVersions := List(scala213, scala212, scala211)
crossScalaVersions := List(scala213, scala212, scala211),
libraryDependencies += (
"com.h2database" % "h2" % h2 % Test
)
)
.settings(Publishing.publishSettings)
.settings(paradise())
......
......@@ -186,6 +186,15 @@ abstract class AbstractMacroProcessor(val c: blackbox.Context) {
def resolveClassTypeName[T: WeakTypeTag]: TypeName =
c.weakTypeOf[T].typeSymbol.name.toTypeName
/** Get the `TypeName` of the class generic type.
*
* @tparam T
* Type of the case class.
* @return
*/
def getGenericTypes[T: WeakTypeTag]: List[Type] =
c.weakTypeOf[T].dealias.typeArgs
/** Get the list of case class constructor parameters and return the column index and parameter type that zip as a
* `FieldZipInformation`.
*
......
/*
* Copyright (c) 2022 bitlap
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.bitlap.common.internal
import org.bitlap.common.jdbc._
import scala.reflect.macros.whitebox
/** @author
* 梦境迷离
* @version 1.0,6/15/22
*/
class ResultSetTransformerMacro(override val c: whitebox.Context) extends AbstractMacroProcessor(c) {
import c.universe._
private val valuesTermName = TermName("values")
private val typeMappingTermName = TermName("typeMapping")
private val columnSizeTermName = TermName("columnSize")
protected val packageName = q"_root_.org.bitlap.common.jdbc"
def applyImpl[T <: GenericRow: WeakTypeTag]: Expr[ResultSetTransformer[T]] = {
val className = resolveClassTypeName[T]
val genericTypes = getGenericTypes[T]
val fieldValues = genericTypes.zipWithIndex.map { case (tpe, i) =>
q"$valuesTermName($i).asInstanceOf[$tpe]"
}
// scalafmt: { maxColumn = 400 }
val tree = q"""
new $packageName.ResultSetTransformer[$className[..$genericTypes]] {
override def toResults(resultSet: java.sql.ResultSet, $typeMappingTermName: (java.sql.ResultSet, Int) => _root_.scala.IndexedSeq[Any] = super.getColumnValues): _root_.scala.Seq[$className[..$genericTypes]] = {
val $columnSizeTermName = resultSet.getMetaData.getColumnCount
val result = _root_.scala.collection.mutable.ListBuffer[$className[..$genericTypes]]()
while (resultSet.next()) {
val $valuesTermName = $typeMappingTermName(resultSet, $columnSizeTermName)
assert($columnSizeTermName == ${fieldValues.size})
result += ${className.toTermName}(..$fieldValues)
}
result.toSeq
}
}
"""
exprPrintTree[ResultSetTransformer[T]](force = false, tree)
}
}
/*
* Copyright (c) 2022 bitlap
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.bitlap.common.jdbc
import org.bitlap.common.internal.ResultSetTransformerMacro
import java.sql._
trait ResultSetTransformer[T <: GenericRow] {
protected def getColumnValues(resultSet: ResultSet, size: Int): IndexedSeq[Any] = {
val metadata = resultSet.getMetaData
1 to size map { idx =>
val tpe = metadata.getColumnType(idx)
val name = metadata.getColumnName(idx)
tpe match {
case Types.VARCHAR => resultSet.getString(name)
case Types.BIGINT => resultSet.getLong(name)
case Types.TINYINT => resultSet.getByte(name)
case Types.SMALLINT => resultSet.getShort(name)
case Types.BOOLEAN => resultSet.getBoolean(name)
case Types.INTEGER => resultSet.getInt(name)
case Types.DOUBLE => resultSet.getDouble(name)
case Types.TIMESTAMP => resultSet.getTimestamp(name)
case _ => resultSet.getObject(name)
}
}
}
def toResults(resultSet: ResultSet, typeMapping: (ResultSet, Int) => IndexedSeq[Any] = getColumnValues): Seq[T]
}
object ResultSetTransformer {
def apply[T <: GenericRow]: ResultSetTransformer[T] = macro ResultSetTransformerMacro.applyImpl[T]
}
/*
* Copyright (c) 2022 bitlap
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.bitlap.common.jdbc
/** @author
* 梦境迷离
* @version 1.0,2022/10/21
*/
sealed trait GenericRow
final case class GenericRow1[T1](col1: T1) extends GenericRow
final case class GenericRow2[T1, T2](col1: T1, col2: T2) extends GenericRow
final case class GenericRow3[T1, T2, T3](col1: T1, col2: T2, col3: T3) extends GenericRow
final case class GenericRow4[T1, T2, T3, T4](col1: T1, col2: T2, col3: T3, col4: T4) extends GenericRow
final case class GenericRow5[T1, T2, T3, T4, T5](col1: T1, col2: T2, col3: T3, col4: T4, col5: T5) extends GenericRow
final case class GenericRow6[T1, T2, T3, T4, T5, T6](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6
) extends GenericRow
final case class GenericRow7[T1, T2, T3, T4, T5, T6, T7](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6,
col7: T7
) extends GenericRow
final case class GenericRow8[T1, T2, T3, T4, T5, T6, T7, T8](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6,
col7: T7,
col8: T8
) extends GenericRow
final case class GenericRow9[T1, T2, T3, T4, T5, T6, T7, T8, T9](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6,
col7: T7,
col8: T8,
col9: T9
) extends GenericRow
final case class GenericRow10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6,
col7: T7,
col8: T8,
col9: T9,
col10: T10
) extends GenericRow
final case class GenericRow11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6,
col7: T7,
col8: T8,
col9: T9,
col10: T10,
col11: T11
) extends GenericRow
final case class GenericRow12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6,
col7: T7,
col8: T8,
col9: T9,
col10: T10,
col11: T11,
col12: T12
) extends GenericRow
final case class GenericRow13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6,
col7: T7,
col8: T8,
col9: T9,
col10: T10,
col11: T11,
col12: T12,
col13: T13
) extends GenericRow
final case class GenericRow14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6,
col7: T7,
col8: T8,
col9: T9,
col10: T10,
col11: T11,
col12: T12,
col13: T13,
col14: T14
) extends GenericRow
final case class GenericRow15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6,
col7: T7,
col8: T8,
col9: T9,
col10: T10,
col11: T11,
col12: T12,
col13: T13,
col14: T14,
col15: T15
) extends GenericRow
final case class GenericRow16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6,
col7: T7,
col8: T8,
col9: T9,
col10: T10,
col11: T11,
col12: T12,
col13: T13,
col14: T14,
col15: T15,
col16: T16
) extends GenericRow
final case class GenericRow17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6,
col7: T7,
col8: T8,
col9: T9,
col10: T10,
col11: T11,
col12: T12,
col13: T13,
col14: T14,
col15: T15,
col16: T16,
col17: T17
) extends GenericRow
final case class GenericRow18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6,
col7: T7,
col8: T8,
col9: T9,
col10: T10,
col11: T11,
col12: T12,
col13: T13,
col14: T14,
col15: T15,
col16: T16,
col17: T17,
col18: T18
) extends GenericRow
final case class GenericRow19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6,
col7: T7,
col8: T8,
col9: T9,
col10: T10,
col11: T11,
col12: T12,
col13: T13,
col14: T14,
col15: T15,
col16: T16,
col17: T17,
col18: T18,
col19: T19
) extends GenericRow
final case class GenericRow20[
T1,
T2,
T3,
T4,
T5,
T6,
T7,
T8,
T9,
T10,
T11,
T12,
T13,
T14,
T15,
T16,
T17,
T18,
T19,
T20
](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6,
col7: T7,
col8: T8,
col9: T9,
col10: T10,
col11: T11,
col12: T12,
col13: T13,
col14: T14,
col15: T15,
col16: T16,
col17: T17,
col18: T18,
col19: T19,
col20: T20
) extends GenericRow
final case class GenericRow21[
T1,
T2,
T3,
T4,
T5,
T6,
T7,
T8,
T9,
T10,
T11,
T12,
T13,
T14,
T15,
T16,
T17,
T18,
T19,
T20,
T21
](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6,
col7: T7,
col8: T8,
col9: T9,
col10: T10,
col11: T11,
col12: T12,
col13: T13,
col14: T14,
col15: T15,
col16: T16,
col17: T17,
col18: T18,
col19: T19,
col20: T20,
col21: T21
) extends GenericRow
final case class GenericRow22[
T1,
T2,
T3,
T4,
T5,
T6,
T7,
T8,
T9,
T10,
T11,
T12,
T13,
T14,
T15,
T16,
T17,
T18,
T19,
T20,
T21,
T22
](
col1: T1,
col2: T2,
col3: T3,
col4: T4,
col5: T5,
col6: T6,
col7: T7,
col8: T8,
col9: T9,
col10: T10,
col11: T11,
col12: T12,
col13: T13,
col14: T14,
col15: T15,
col16: T16,
col17: T17,
col18: T18,
col19: T19,
col20: T20,
col21: T21,
col22: T22
) extends GenericRow
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user`
(
`id` int(10) NOT NULL AUTO_INCREMENT,
`username` varchar(64) NOT NULL COMMENT '用户名',
`password` varchar(128) NOT NULL COMMENT '密码',
`sign` varchar(255) DEFAULT NULL COMMENT '签名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
insert into t_user(username, password, sign)
values ('边月', 'jZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI=', '');
insert into t_user(username, password, sign)
values ('侯梅希', 'jZae727K08KaOmKSgOaGzww/XVqGr/PKEgIMkjrcbJI=', '');
\ No newline at end of file
/*
* Copyright (c) 2022 bitlap
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package org.bitlap.common
import org.bitlap.common.jdbc._
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import java.sql.ResultSet
import java.sql.DriverManager
import java.sql.Types
/** @author
* 梦境迷离
* @version 1.0,2022/10/21
*/
class RowTransformerTest extends AnyFlatSpec with Matchers {
Class.forName("org.h2.Driver")
// TODO need bitlap server
"RowTransformerTest simple case" should "ok for GenericRow2" in {
val statement = DriverManager
.getConnection(
"jdbc:h2:mem:zim?caseSensitive=false;MODE=MYSQL;TRACE_LEVEL_FILE=2;INIT=RUNSCRIPT FROM 'classpath:test.sql'"
)
.createStatement()
statement.execute(s"""select * from T_USER""".stripMargin)
val rowSet: ResultSet = statement.getResultSet
// default type mapping
val ret1 = ResultSetTransformer[GenericRow4[Int, String, String, String]].toResults(rowSet)
assert(ret1.size == 2)
println(ret1)
statement.execute(s"""select * from T_USER""".stripMargin)
val rowSet2: ResultSet = statement.getResultSet
// custom type mapping
val ret2 = ResultSetTransformer[GenericRow4[Int, String, String, String]].toResults(
rowSet2,
(resultSet, columnSize) => {
val metadata = resultSet.getMetaData
1 to columnSize map { idx =>
val tpe = metadata.getColumnType(idx)
val name = metadata.getColumnName(idx)
tpe match {
case Types.VARCHAR => resultSet.getString(name)
case Types.BIGINT => resultSet.getLong(name)
case Types.TINYINT => resultSet.getByte(name)
case Types.SMALLINT => resultSet.getShort(name)
case Types.BOOLEAN => resultSet.getBoolean(name)
case Types.INTEGER => resultSet.getInt(name)
case Types.DOUBLE => resultSet.getDouble(name)
case Types.TIMESTAMP => resultSet.getTimestamp(name)
case _ => resultSet.getObject(name)
}
}
}
)
println(ret2)
assert(ret2.size == 2)
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册