Skip to content

Commit a453411

Browse files
benedekimiroslavpojerlsulak
authored
#35: Refactor the core for more logical structure (#36)
* massive Core classes refactoring * impact on Slick module too ----- Co-authored-by: miroslavpojer <[email protected]> Co-authored-by: Ladislav Sulak <[email protected]>
1 parent 863d7e7 commit a453411

File tree

24 files changed

+814
-273
lines changed

24 files changed

+814
-273
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright 2022 ABSA Group Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package za.co.absa.fadb
18+
19+
import scala.concurrent.ExecutionContext.Implicits.global
20+
import scala.concurrent.Future
21+
import scala.language.higherKinds
22+
23+
/**
24+
* A basis to represent a database executor
25+
*/
26+
trait DBEngine {
27+
28+
/**
29+
* A type representing the (SQL) query within the engine
30+
* @tparam T - the return type of the query
31+
*/
32+
type QueryType[T] <: Query[T]
33+
34+
/**
35+
* The actual query executioner of the queries of the engine
36+
* @param query - the query to execute
37+
* @tparam R - return the of the query
38+
* @return - sequence of the results of database query
39+
*/
40+
protected def run[R](query: QueryType[R]): Future[Seq[R]]
41+
42+
/**
43+
* Public method to execute when query is expected to return multiple results
44+
* @param query - the query to execute
45+
* @tparam R - return the of the query
46+
* @return - sequence of the results of database query
47+
*/
48+
def execute[R](query: QueryType[R]): Future[Seq[R]] = run(query)
49+
50+
/**
51+
* Public method to execute when query is expected to return exactly one row
52+
* @param query - the query to execute
53+
* @tparam R - return the of the query
54+
* @return - sequence of the results of database query
55+
*/
56+
def unique[R](query: QueryType[R]): Future[R] = {
57+
run(query).map(_.head)
58+
}
59+
60+
/**
61+
* Public method to execute when query is expected to return one or no results
62+
* @param query - the query to execute
63+
* @tparam R - return the of the query
64+
* @return - sequence of the results of database query
65+
*/
66+
67+
def option[R](query: QueryType[R]): Future[Option[R]] = {
68+
run(query).map(_.headOption)
69+
}
70+
}
71+

core/src/main/scala/za/co/absa/fadb/DBFunction.scala

Lines changed: 157 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,57 @@
1616

1717
package za.co.absa.fadb
1818

19+
import za.co.absa.fadb.naming_conventions.NamingConvention
20+
1921
import scala.concurrent.Future
2022

2123
/**
22-
* The most general abstraction of database function representation
23-
* The database name of the function is derives from the class name based on the provided naming convention (in schema)
2424
*
25-
* @param schema - the schema the function belongs into
2625
* @param functionNameOverride - in case the class name would not match the database function name, this gives the
2726
* possibility of override
28-
* @tparam E - the type of the [[DBExecutor]] engine
29-
* @tparam T - the type covering the input fields of the database function
27+
* @param schema - the schema the function belongs into
28+
* @param dBEngine - the database engine that is supposed to execute the function (presumably contains
29+
* connection to the database
30+
* @tparam I - the type covering the input fields of the database function
3031
* @tparam R - the type covering the returned fields from the database function
32+
* @tparam E - the type of the [[DBEngine]] engine
3133
*/
32-
abstract class DBFunction[E, T, R](schema: DBSchema[E], functionNameOverride: Option[String] = None) extends DBFunctionFabric {
34+
abstract class DBFunction[I, R, E <: DBEngine](functionNameOverride: Option[String] = None)
35+
(implicit val schema: DBSchema, val dBEngine: E) extends DBFunctionFabric {
36+
37+
/* alternative constructors for different availability of input parameters */
38+
def this(schema: DBSchema, functionNameOverride: String)
39+
(implicit dBEngine: E) = {
40+
this(Option(functionNameOverride))(schema, dBEngine)
41+
}
42+
43+
/* only one constructor of a class can have default values for parameters*/
44+
def this(schema: DBSchema)
45+
(implicit dBEngine: E) = {
46+
this(None)(schema, dBEngine)
47+
}
48+
49+
def this(dBEngine: E, functionNameOverride: String)
50+
(implicit schema: DBSchema) = {
51+
this(Option(functionNameOverride))(schema, dBEngine)
52+
}
53+
54+
def this(dBEngine: E)
55+
(implicit schema: DBSchema) = {
56+
this(None)(schema, dBEngine)
57+
}
58+
59+
/**
60+
* Function to create the DB function call specific to the provided [[DBEngine]]. Expected to be implemented by the
61+
* DBEngine specific mix-in.
62+
* @param values - the values to pass over to the database function
63+
* @return - the SQL query in the format specific to the provided [[DBEngine]]
64+
*/
65+
protected def query(values: I): dBEngine.QueryType[R]
66+
67+
/**
68+
* Name of the function, based on the class name, unless it is overridden in the constructor
69+
*/
3370
val functionName: String = {
3471
val fn = functionNameOverride.getOrElse(schema.objectNameFromClassName(getClass))
3572
if (schema.schemaName.isEmpty) {
@@ -39,65 +76,149 @@ abstract class DBFunction[E, T, R](schema: DBSchema[E], functionNameOverride: Op
3976
}
4077
}
4178

79+
def namingConvention: NamingConvention = schema.namingConvention
80+
4281
/**
43-
* For the given output it returns a function to execute the SQL query and interpret the results.
44-
* Basically it should create a function which contains a query to be executable and executed on on the [[DBExecutor]]
45-
* and transforming the result of that query to result type.
46-
* @param values - the input values of the DB function (stored procedure)
47-
* @return - the query function that when provided an executor will return the result of the DB function call
82+
* List of fields to select from the DB function. Expected to be based on the return type `R`
83+
* @return - list of fields to select
4884
*/
49-
protected def queryFunction(values: T): QueryFunction[E, R]
85+
override protected def fieldsToSelect: Seq[String] = super.fieldsToSelect //TODO should get the names from R #6
86+
87+
/*these 3 functions has to be defined here and not in the ancestors, as there the query type is not compatible - path-dependent types*/
88+
protected def execute(values: I): Future[Seq[R]] = dBEngine.execute[R](query(values))
89+
protected def unique(values: I): Future[R] = dBEngine.unique(query(values))
90+
protected def option(values: I): Future[Option[R]] = dBEngine.option(query(values))
91+
5092
}
5193

5294
object DBFunction {
5395
/**
5496
* Represents a function returning a set (in DB sense) of rows
55-
*
56-
* @param schema - the schema the function belongs into
5797
* @param functionNameOverride - in case the class name would not match the database function name, this gives the
5898
* possibility of override
59-
* @tparam E - the type of the [[DBExecutor]] engine
60-
* @tparam T - the type covering the input fields of the database function
99+
* @param schema - the schema the function belongs into
100+
* @param dBEngine - the database engine that is supposed to execute the function (presumably contains
101+
* connection to the database
102+
* @tparam I - the type covering the input fields of the database function
61103
* @tparam R - the type covering the returned fields from the database function
104+
* @tparam E - the type of the [[DBEngine]] engine
62105
*/
63-
abstract class DBSeqFunction[E, T, R](schema: DBSchema[E], functionNameOverride: Option[String] = None)
64-
extends DBFunction[E, T, R](schema, functionNameOverride) {
65-
def apply(values: T): Future[Seq[R]] = {
66-
schema.execute(queryFunction(values))
106+
abstract class DBSeqFunction[I, R, E <: DBEngine](functionNameOverride: Option[String] = None)
107+
(implicit schema: DBSchema, dBEngine: E)
108+
extends DBFunction[I, R, E](functionNameOverride) {
109+
110+
def this(schema: DBSchema, functionNameOverride: String)
111+
(implicit dBEngine: E) = {
112+
this(Option(functionNameOverride))(schema, dBEngine)
113+
}
114+
115+
def this(schema: DBSchema)
116+
(implicit dBEngine: E) = {
117+
this(None)(schema, dBEngine)
67118
}
119+
120+
def this(dBEngine: E, functionNameOverride: String)
121+
(implicit schema: DBSchema) = {
122+
this(Option(functionNameOverride))(schema, dBEngine)
123+
}
124+
125+
def this(dBEngine: E)
126+
(implicit schema: DBSchema) = {
127+
this(None)(schema, dBEngine)
128+
}
129+
130+
/**
131+
* For easy and convenient execution of the DB function call
132+
* @param values - the values to pass over to the database function
133+
* @return - a sequence of values, each coming from a row returned from the DB function transformed to scala
134+
* type `R`
135+
*/
136+
def apply(values: I): Future[Seq[R]] = execute(values)
68137
}
69138

70139
/**
71140
* Represents a function returning exactly one record
72-
*
73-
* @param schema - the schema the function belongs into
74141
* @param functionNameOverride - in case the class name would not match the database function name, this gives the
75142
* possibility of override
76-
* @tparam E - the type of the [[DBExecutor]] engine
77-
* @tparam T - the type covering the input fields of the database function
143+
* @param schema - the schema the function belongs into
144+
* @param dBEngine - the database engine that is supposed to execute the function (presumably contains
145+
* connection to the database
146+
* @tparam I - the type covering the input fields of the database function
78147
* @tparam R - the type covering the returned fields from the database function
148+
* @tparam E - the type of the [[DBEngine]] engine
79149
*/
80-
abstract class DBUniqueFunction[E, T, R](schema: DBSchema[E], functionNameOverride: Option[String] = None)
81-
extends DBFunction[E, T, R](schema, functionNameOverride) {
82-
def apply(values: T): Future[R] = {
83-
schema.unique(queryFunction(values))
150+
abstract class DBUniqueFunction[I, R, E <: DBEngine](functionNameOverride: Option[String] = None)
151+
(implicit schema: DBSchema, dBEngine: E)
152+
extends DBFunction[I, R, E](functionNameOverride) {
153+
154+
def this(schema: DBSchema, functionNameOverride: String)
155+
(implicit dBEngine: E) = {
156+
this(Option(functionNameOverride))(schema, dBEngine)
157+
}
158+
159+
def this(schema: DBSchema)
160+
(implicit dBEngine: E) = {
161+
this(None)(schema, dBEngine)
84162
}
163+
164+
def this(dBEngine: E, functionNameOverride: String)
165+
(implicit schema: DBSchema) = {
166+
this(Option(functionNameOverride))(schema, dBEngine)
167+
}
168+
169+
def this(dBEngine: E)
170+
(implicit schema: DBSchema) = {
171+
this(None)(schema, dBEngine)
172+
}
173+
174+
/**
175+
* For easy and convenient execution of the DB function call
176+
* @param values - the values to pass over to the database function
177+
* @return - the value returned from the DB function transformed to scala type `R`
178+
*/
179+
def apply(values: I): Future[R] = unique(values)
85180
}
86181

87182
/**
88183
* Represents a function returning one optional record
89-
*
90-
* @param schema - the schema the function belongs into
91184
* @param functionNameOverride - in case the class name would not match the database function name, this gives the
92185
* possibility of override
93-
* @tparam E - the type of the [[DBExecutor]] engine
94-
* @tparam T - the type covering the input fields of the database function
186+
* @param schema - the schema the function belongs into
187+
* @param dBEngine - the database engine that is supposed to execute the function (presumably contains
188+
* connection to the database
189+
* @tparam I - the type covering the input fields of the database function
95190
* @tparam R - the type covering the returned fields from the database function
191+
* @tparam E - the type of the [[DBEngine]] engine
96192
*/
97-
abstract class DBOptionFunction[E, T, R](schema: DBSchema[E], functionNameOverride: Option[String] = None)
98-
extends DBFunction[E, T, R](schema, functionNameOverride) {
99-
def apply(values: T): Future[Option[R]] = {
100-
schema.option(queryFunction(values))
193+
abstract class DBOptionFunction[I, R, E <: DBEngine](functionNameOverride: Option[String] = None)
194+
(implicit schema: DBSchema, dBEngine: E)
195+
extends DBFunction[I, R, E](functionNameOverride) {
196+
197+
def this(schema: DBSchema, functionNameOverride: String)
198+
(implicit dBEngine: E) = {
199+
this(Option(functionNameOverride))(schema, dBEngine)
200+
}
201+
202+
def this(schema: DBSchema)
203+
(implicit dBEngine: E) = {
204+
this(None)(schema, dBEngine)
205+
}
206+
207+
def this(dBEngine: E, functionNameOverride: String)
208+
(implicit schema: DBSchema) = {
209+
this(Option(functionNameOverride))(schema, dBEngine)
101210
}
211+
212+
def this(dBEngine: E)
213+
(implicit schema: DBSchema) = {
214+
this(None)(schema, dBEngine)
215+
}
216+
217+
/**
218+
* For easy and convenient execution of the DB function call
219+
* @param values - the values to pass over to the database function
220+
* @return - the value returned from the DB function transformed to scala type `R` if a row is returned, otherwise `None`
221+
*/
222+
def apply(values: I): Future[Option[R]] = option(values)
102223
}
103224
}

core/src/main/scala/za/co/absa/fadb/DBFunctionFabric.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,15 @@ package za.co.absa.fadb
2121
* that offer certain implementations. This trait should help with the inheritance of all of these
2222
*/
2323
trait DBFunctionFabric {
24+
25+
/**
26+
* Name of the function the class represents
27+
*/
2428
def functionName: String
2529

30+
/**
31+
* List of fields to select from the DB function.
32+
* @return - list of fields to select
33+
*/
2634
protected def fieldsToSelect: Seq[String] = Seq.empty
2735
}

0 commit comments

Comments
 (0)