/mybatis-scala-core/src/main/scala/org/fdmtech/mybatis/scala/ResultMap.scala

https://github.com/mnesarco/Mybatis-For-Scala · Scala · 396 lines · 126 code · 23 blank · 247 comment · 0 complexity · 9097079bd95d027af1903b53e0445a50 MD5 · raw file

  1. /*
  2. * Copyright 2011 Frank David Martinez M.
  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. package org.fdmtech.mybatis.scala
  17. import scala.collection.mutable.ListBuffer
  18. import scala.xml.Node
  19. /** Base class for all mappings
  20. */
  21. abstract class ResultMapping
  22. /** Base class for constructor arguments
  23. */
  24. abstract class BaseArg extends ResultMapping
  25. /** Constructor formal argument mapping.
  26. *
  27. * @param column The column name from the database, or the aliased column label.
  28. * This is the same string that would normally be passed to resultSet.getString(columnName).
  29. * @param javaType A fully qualified Java class name, or a type alias.
  30. * MyBatis can usually figure out the type if you’re mapping to a JavaBean.
  31. * However, if you are mapping to a HashMap, then you should specify the javaType
  32. * explicitly to ensure the desired behaviour.
  33. * @param jdbcType The JDBC Type from the list of supported types.
  34. * The JDBC type is only required for nullable columns upon insert, update or delete.
  35. * This is a JDBC requirement, not an MyBatis one. So even if you were coding JDBC directly,
  36. * you’d need to specify this type – but only for nullable values.
  37. * @param typeHandler Using this property you can override the default type handler on a mapping-by-mapping basis.
  38. * The value is either a fully qualified class name of a TypeHandler implementation, or a type alias.
  39. */
  40. case class Arg(
  41. column : String = null,
  42. javaType : Type[_] = null,
  43. jdbcType : JdbcType = JdbcType.UNDEFINED,
  44. typeHandler : Type[_ <: TypeHandler] = null,
  45. select : Statement = null,
  46. resultMap : ResultMap[_] = null
  47. )
  48. extends BaseArg
  49. /** Constructor formal argument mapping with ID flag.
  50. *
  51. * @param column The column name from the database, or the aliased column label.
  52. * This is the same string that would normally be passed to resultSet.getString(columnName).
  53. * @param javaType A fully qualified Java class name, or a type alias.
  54. * MyBatis can usually figure out the type if you’re mapping to a JavaBean.
  55. * However, if you are mapping to a HashMap, then you should specify the javaType
  56. * explicitly to ensure the desired behaviour.
  57. * @param jdbcType The JDBC Type from the list of supported types.
  58. * The JDBC type is only required for nullable columns upon insert, update or delete.
  59. * This is a JDBC requirement, not an MyBatis one. So even if you were coding JDBC directly,
  60. * you’d need to specify this type – but only for nullable values.
  61. * @param typeHandler Using this property you can override the default type handler on a mapping-by-mapping basis.
  62. * The value is either a fully qualified class name of a TypeHandler implementation, or a type alias.
  63. */
  64. case class IdArg(
  65. column : String = null,
  66. javaType : Type[_] = null,
  67. jdbcType : JdbcType = JdbcType.UNDEFINED,
  68. typeHandler : Type[_ <: TypeHandler] = null
  69. )
  70. extends BaseArg
  71. /** These are the most basic of result mappings.
  72. * Both id, and result map a single column value
  73. * to a single property or field of a simple data type (String, int, double, Date, etc.)
  74. *
  75. * @param property The field or property to map the column result to.
  76. * If a matching JavaBeans property exists for the given name, then that will be used.
  77. * Otherwise, MyBatis will look for a field of the given name.
  78. * In both cases you can use complex property navigation using the usual dot notation.
  79. * For example, you can map to something simple like: “username”,
  80. * or to something more complicated like: “address.street.number”.
  81. * @param column The column name from the database, or the aliased column label.
  82. * This is the same string that would normally be passed to resultSet.getString(columnName).
  83. * @param javaType A fully qualified Java class name, or a type alias.
  84. * MyBatis can usually figure out the type if you’re mapping to a JavaBean.
  85. * However, if you are mapping to a HashMap, then you should specify the javaType
  86. * explicitly to ensure the desired behaviour.
  87. * @param jdbcType The JDBC Type from the list of supported types.
  88. * The JDBC type is only required for nullable columns upon insert, update or delete.
  89. * This is a JDBC requirement, not an MyBatis one. So even if you were coding JDBC directly,
  90. * you’d need to specify this type – but only for nullable values.
  91. * @param typeHandler Using this property you can override the default type handler on a mapping-by-mapping basis.
  92. * The value is either a fully qualified class name of a TypeHandler implementation, or a type alias.
  93. */
  94. case class Result(
  95. property : String = null,
  96. column : String = null,
  97. javaType : Type[_] = null,
  98. jdbcType : JdbcType = JdbcType.UNDEFINED,
  99. typeHandler : Type[_ <: TypeHandler] = null
  100. ) extends ResultMapping
  101. /** These are the most basic of result mappings.
  102. * Both id, and result map a single column value
  103. * to a single property or field of a simple data type (String, int, double, Date, etc.)
  104. *
  105. * The only difference between the two is that id will flag the result as an identifier
  106. * property to be used when comparing object instances. This helps to improve general performance,
  107. * but especially performance of caching and nested result mapping (i.e. join mapping).
  108. *
  109. * @param property The field or property to map the column result to.
  110. * If a matching JavaBeans property exists for the given name, then that will be used.
  111. * Otherwise, MyBatis will look for a field of the given name.
  112. * In both cases you can use complex property navigation using the usual dot notation.
  113. * For example, you can map to something simple like: “username”,
  114. * or to something more complicated like: “address.street.number”.
  115. * @param column The column name from the database, or the aliased column label.
  116. * This is the same string that would normally be passed to resultSet.getString(columnName).
  117. * @param javaType A fully qualified Java class name, or a type alias.
  118. * MyBatis can usually figure out the type if you’re mapping to a JavaBean.
  119. * However, if you are mapping to a HashMap, then you should specify the javaType
  120. * explicitly to ensure the desired behaviour.
  121. * @param jdbcType The JDBC Type from the list of supported types.
  122. * The JDBC type is only required for nullable columns upon insert, update or delete.
  123. * This is a JDBC requirement, not an MyBatis one. So even if you were coding JDBC directly,
  124. * you’d need to specify this type – but only for nullable values.
  125. * @param typeHandler Using this property you can override the default type handler on a mapping-by-mapping basis.
  126. * The value is either a fully qualified class name of a TypeHandler implementation, or a type alias.
  127. */
  128. case class Id(
  129. property : String = null,
  130. column : String = null,
  131. javaType : Type[_] = null,
  132. jdbcType : JdbcType = JdbcType.UNDEFINED,
  133. typeHandler : Type[_ <: TypeHandler] = null
  134. ) extends ResultMapping
  135. /** The association element deals with a "has-one" type relationship.
  136. * You specify the target property, the column to retrieve the value from, the javaType of the property
  137. * (which MyBatis can figure out most of the time), the jdbcType if necessary and a typeHandler
  138. * if you want to override the retrieval of the result values.
  139. * Where the association differs is that you need to tell MyBatis how to load the association.
  140. *
  141. * MyBatis can do so in two different ways:
  142. * - Nested Select: By executing another mapped SQL statement that returns the complex type desired.
  143. * - Nested Results: By using nested result mappings to deal with repeating subsets of joined results.
  144. *
  145. * @param property The field or property to map the column result to.
  146. * If a matching JavaBeans property exists for the given name, then that will be used.
  147. * Otherwise, MyBatis will look for a field of the given name.
  148. * In both cases you can use complex property navigation using the usual dot notation.
  149. * For example, you can map to something simple like: “username”,
  150. * or to something more complicated like: “address.street.number”.
  151. * @param column The column name from the database, or the aliased column label.
  152. * This is the same string that would normally be passed to resultSet.getString(columnName).
  153. * @param javaType A fully qualified Java class name, or a type alias.
  154. * MyBatis can usually figure out the type if you’re mapping to a JavaBean.
  155. * However, if you are mapping to a HashMap, then you should specify the javaType
  156. * explicitly to ensure the desired behaviour.
  157. * @param jdbcType The JDBC Type from the list of supported types.
  158. * The JDBC type is only required for nullable columns upon insert, update or delete.
  159. * This is a JDBC requirement, not an MyBatis one. So even if you were coding JDBC directly,
  160. * you’d need to specify this type – but only for nullable values.
  161. * @param typeHandler Using this property you can override the default type handler on a mapping-by-mapping basis.
  162. * The value is either a fully qualified class name of a TypeHandler implementation, or a type alias.
  163. * @param select The reference to another mapped statement that will load the complex type required by this property mapping.
  164. * The values retrieved from columns specified in the column attribute will be passed to the target select
  165. * statement as parameters.
  166. * Note: To deal with composite keys, you can specify multiple column names to pass to the nested select statement
  167. * by using the syntax column=”{prop1=col1,prop2=col2}”.
  168. * This will cause prop1 and prop2 to be set against the parameter object for the target nested select statement.
  169. * @param resultMap This is the reference to a ResultMap that can map the nested results of this association into
  170. * an appropriate object graph. This is an alternative to using a call to another select statement.
  171. * It allows you to join multiple tables together into a single ResultSet.
  172. * Such a ResultSet will contain duplicated, repeating groups of data that needs to be decomposed
  173. * and mapped properly to a nested object graph. To facilitate this, MyBatis lets you “chain” result maps together,
  174. * to deal with the nested results.
  175. */
  176. case class Association(
  177. property : String = null,
  178. column : String = null,
  179. javaType : Type[_] = null,
  180. jdbcType : JdbcType = JdbcType.UNDEFINED,
  181. typeHandler : Type[_ <: TypeHandler] = null,
  182. select : Statement = null,
  183. resultMap : ResultMap[_] = null,
  184. notNullColumn : String = null
  185. ) extends ResultMapping
  186. /** The collection element works almost identically to the association, but it deals with "has-many" type relationship.
  187. *
  188. * @param property The field or property to map the column result to.
  189. * If a matching JavaBeans property exists for the given name, then that will be used.
  190. * Otherwise, MyBatis will look for a field of the given name.
  191. * In both cases you can use complex property navigation using the usual dot notation.
  192. * For example, you can map to something simple like: “username”,
  193. * or to something more complicated like: “address.street.number”.
  194. * @param column The column name from the database, or the aliased column label.
  195. * This is the same string that would normally be passed to resultSet.getString(columnName).
  196. * @param javaType A fully qualified Java class name, or a type alias.
  197. * MyBatis can usually figure out the type if you’re mapping to a JavaBean.
  198. * However, if you are mapping to a HashMap, then you should specify the javaType
  199. * explicitly to ensure the desired behaviour.
  200. * @param jdbcType The JDBC Type from the list of supported types.
  201. * The JDBC type is only required for nullable columns upon insert, update or delete.
  202. * This is a JDBC requirement, not an MyBatis one. So even if you were coding JDBC directly,
  203. * you’d need to specify this type – but only for nullable values.
  204. * @param typeHandler Using this property you can override the default type handler on a mapping-by-mapping basis.
  205. * The value is either a fully qualified class name of a TypeHandler implementation, or a type alias.
  206. * @param select The reference to another mapped statement that will load the complex type required by this property mapping.
  207. * The values retrieved from columns specified in the column attribute will be passed to the target select
  208. * statement as parameters.
  209. * Note: To deal with composite keys, you can specify multiple column names to pass to the nested select statement
  210. * by using the syntax column=”{prop1=col1,prop2=col2}”.
  211. * This will cause prop1 and prop2 to be set against the parameter object for the target nested select statement.
  212. * @param resultMap This is the reference to a ResultMap that can map the nested results of this association into
  213. * an appropriate object graph. This is an alternative to using a call to another select statement.
  214. * It allows you to join multiple tables together into a single ResultSet.
  215. * Such a ResultSet will contain duplicated, repeating groups of data that needs to be decomposed
  216. * and mapped properly to a nested object graph. To facilitate this, MyBatis lets you “chain” result maps together,
  217. * to deal with the nested results.
  218. * @param ofType This attribute is necessary to distinguish between the JavaBean (or field) property type
  219. * and the type that the collection contains.
  220. */
  221. case class Collection(
  222. property : String = null,
  223. column : String = null,
  224. javaType : Type[_] = null,
  225. jdbcType : JdbcType = JdbcType.UNDEFINED,
  226. ofType : Type[_] = null,
  227. typeHandler : Type[_ <: TypeHandler] = null,
  228. select : Statement = null,
  229. resultMap : ResultMap[_] = null,
  230. notNullColumn : String = null
  231. ) extends ResultMapping
  232. /** A concrete mapping used by a discriminator case.
  233. */
  234. case class Case(value : String, resultMap : ResultMap[_] = null, resultType : Type[_] = null)
  235. /** Sometimes a single database query might return result sets of many different (but hopefully somewhat related)
  236. * data types. The discriminator element was designed to deal with this situation, and others, including class
  237. * inheritance hierarchies. The discriminator is pretty simple to understand, as it behaves much like a switch
  238. * statement in Java. A discriminator definition specifies column and javaType attributes.
  239. * The column is where MyBatis will look for the value to compare. The javaType is required to ensure the proper
  240. * kind of equality test is performed (although String would probably work for almost any situation).
  241. *
  242. * @param property The field or property to map the column result to.
  243. * If a matching JavaBeans property exists for the given name, then that will be used.
  244. * Otherwise, MyBatis will look for a field of the given name.
  245. * In both cases you can use complex property navigation using the usual dot notation.
  246. * For example, you can map to something simple like: “username”,
  247. * or to something more complicated like: “address.street.number”.
  248. * @param column The column name from the database, or the aliased column label.
  249. * This is the same string that would normally be passed to resultSet.getString(columnName).
  250. * @param javaType A fully qualified Java class name, or a type alias.
  251. * MyBatis can usually figure out the type if you’re mapping to a JavaBean.
  252. * However, if you are mapping to a HashMap, then you should specify the javaType
  253. * explicitly to ensure the desired behaviour.
  254. * @param jdbcType The JDBC Type from the list of supported types.
  255. * The JDBC type is only required for nullable columns upon insert, update or delete.
  256. * This is a JDBC requirement, not an MyBatis one. So even if you were coding JDBC directly,
  257. * you’d need to specify this type – but only for nullable values.
  258. * @param typeHandler Using this property you can override the default type handler on a mapping-by-mapping basis.
  259. * The value is either a fully qualified class name of a TypeHandler implementation, or a type alias.
  260. * @param cases A list of mapping cases
  261. */
  262. case class Discriminator(
  263. column : String = null,
  264. javaType : Type[_] = null,
  265. jdbcType : JdbcType = JdbcType.UNDEFINED,
  266. typeHandler : Type[_ <: TypeHandler] = null,
  267. cases : List[Case] = List()
  268. )
  269. /** While properties will work for most Data Transfer Object (DTO) type classes,
  270. * and likely most of your domain model, there may be some cases where you want
  271. * to use immutable classes. Often tables that contain reference or lookup data
  272. * that rarely or never changes is suited to immutable classes. Constructor
  273. * injection allows you to set values on a class upon instantiation, without
  274. * exposing public methods. MyBatis also supports private properties and private
  275. * JavaBeans properties to achieve this, but some people prefer Constructor injection.
  276. * The constructor element enables this.
  277. *
  278. * In order to inject the results into the constructor,
  279. * MyBatis needs to identify the constructor by the type of its parameters.
  280. * Java has no way to introspect (or reflect) on parameter names.
  281. * So when creating a constructor element, ensure that the arguments are in order,
  282. * and that the data types are specified.
  283. */
  284. case class Constructor(val args : BaseArg*)
  285. /** The resultMap element is the most important and powerful element in MyBatis.
  286. * It’s what allows you to do away with 90% of the code that JDBC requires to retrieve data from ResultSets,
  287. * and in some cases allows you to do things that JDBC does not even support.
  288. * In fact, to write the equivalent code for something like a join mapping for a complex statement could probably
  289. * span thousands of lines of code. The design of the ResultMaps is such that simple statements don’t
  290. * require explicit result mappings at all, and more complex statements require no more than is absolutely
  291. * necessary to describe the relationships.
  292. *
  293. * @param resultType The class of the result
  294. * @param parent A reference to a parent resultMap (resultMap inheritence)
  295. */
  296. class ResultMap[Result : Manifest](val parent : ResultMap[_] = null) {
  297. var fqi : FQI = null
  298. val resultType = Type[Result]
  299. val mappings = new ListBuffer[ResultMapping]
  300. var constructor : Constructor = null
  301. var discriminator : Discriminator = null
  302. /** Maps a single value to a single property.
  303. * For a more specific documentation @see [[org.fdmtech.mybatis.scala.Result]]
  304. */
  305. def result(
  306. property : String = null,
  307. column : String = null,
  308. javaType : Type[_] = null,
  309. jdbcType : JdbcType = JdbcType.UNDEFINED,
  310. typeHandler : Type[_ <: TypeHandler] = null) = {
  311. mappings += Result(property,column,javaType,jdbcType,typeHandler)
  312. this
  313. }
  314. /** Maps a single value to a single property with ID Flag.
  315. * For a more specific documentation @see [[org.fdmtech.mybatis.scala.Id]]
  316. */
  317. def id(
  318. property : String = null,
  319. column : String = null,
  320. javaType : Type[_] = null,
  321. jdbcType : JdbcType = JdbcType.UNDEFINED,
  322. typeHandler : Type[_ <: TypeHandler] = null) = {
  323. mappings += Id(property,column,javaType,jdbcType,typeHandler)
  324. this
  325. }
  326. /** Maps a complex value to a single property, "has-one" relationship.
  327. * For a more specific documentation @see [[org.fdmtech.mybatis.scala.Association]]
  328. */
  329. def association[T : Manifest](
  330. property : String = null,
  331. column : String = null,
  332. jdbcType : JdbcType = JdbcType.UNDEFINED,
  333. typeHandler : Type[_ <: TypeHandler] = null,
  334. select : Statement = null,
  335. resultMap : ResultMap[_] = null,
  336. notNullColumn : String = null) = {
  337. mappings += Association(property,column,Type[T],jdbcType,typeHandler,select,resultMap,notNullColumn)
  338. this
  339. }
  340. /** Maps a complex value to a collection property, "has-many" relationship.
  341. * For a more specific documentation @see [[org.fdmtech.mybatis.scala.Collection]]
  342. */
  343. def collection[T : Manifest](
  344. property : String = null,
  345. column : String = null,
  346. collectionType : Type[_] = null,
  347. jdbcType : JdbcType = JdbcType.UNDEFINED,
  348. typeHandler : Type[_ <: TypeHandler] = null,
  349. select : Statement = null,
  350. resultMap : ResultMap[_] = null,
  351. notNullColumn : String = null) = {
  352. mappings += Collection(property,column,collectionType,jdbcType,Type[T],typeHandler,select,resultMap,notNullColumn)
  353. this
  354. }
  355. /** Maps results to a constructor arguments.
  356. * For a more specific documentation @see [[org.fdmtech.mybatis.scala.Constructor]]
  357. */
  358. def constructor(args : BaseArg*) : this.type = {
  359. constructor = Constructor(args : _*)
  360. this
  361. }
  362. /** For a more specific documentation @see [[org.fdmtech.mybatis.scala.Discriminator]]
  363. */
  364. def discriminator(
  365. column : String = null,
  366. javaType : Type[_] = null,
  367. jdbcType : JdbcType = JdbcType.UNDEFINED,
  368. typeHandler : Type[_ <: TypeHandler] = null,
  369. cases : List[Case] = List()) : this.type = {
  370. discriminator = Discriminator(column,javaType,jdbcType,typeHandler,cases)
  371. this
  372. }
  373. }