Permalink
Find file Copy path
a3d72c9 Jul 24, 2018
2 contributors

Users who have contributed to this file

@seratch @riverpuro
235 lines (208 sloc) 8.66 KB
package skinny.orm.feature
import scalikejdbc._
import skinny.Pagination
import skinny.orm.SkinnyMapperBase
import skinny.orm.feature.includes.IncludesQueryRepository
/**
* Provides #find something APIs.
*/
trait FinderFeature[Entity] extends FinderFeatureWithId[Long, Entity]
trait FinderFeatureWithId[Id, Entity]
extends SkinnyMapperBase[Entity]
with NoIdFinderFeature[Entity]
with ConnectionPoolFeature
with AutoSessionFeature
with AssociationsFeature[Entity]
with JoinsFeature[Entity]
with IdFeature[Id]
with IncludesFeatureWithId[Id, Entity] {
/**
* Default ordering condition.
*/
override def defaultOrdering: SQLSyntax = primaryKeyField
/**
* Finds a single entity by primary key.
*/
def findById(id: Id)(implicit s: DBSession = autoSession): Option[Entity] = {
implicit val enableAsIs = ParameterBinderFactory.asisParameterBinderFactory
implicit val repository = IncludesQueryRepository[Entity]()
appendIncludedAttributes(extract(withSQL {
selectQueryWithAssociations.where.eq(primaryKeyField, idToRawValue(id)).and(defaultScopeWithDefaultAlias)
}).single.apply())
}
/**
* Finds all entities by several primary keys.
*/
def findAllByIds(ids: Id*)(implicit s: DBSession = autoSession): List[Entity] = {
implicit val enableAsIs = ParameterBinderFactory.asisParameterBinderFactory
implicit val repository = IncludesQueryRepository[Entity]()
appendIncludedAttributes(extract(withSQL {
selectQueryWithAssociations.where.in(primaryKeyField, ids.map(idToRawValue)).and(defaultScopeWithDefaultAlias)
}).list.apply())
}
override def findAll(
orderings: Seq[SQLSyntax] = defaultOrderings
)(implicit s: DBSession = autoSession): List[Entity] = {
implicit val repository = IncludesQueryRepository[Entity]()
appendIncludedAttributes(extract(withSQL {
val sql = selectQueryWithAssociations.where(defaultScopeWithDefaultAlias)
if (orderings.isEmpty) sql else sql.orderBy(sqls.csv(orderings: _*))
}).list.apply())
}
override def findAllWithPagination(pagination: Pagination, orderings: Seq[SQLSyntax] = defaultOrderings)(
implicit s: DBSession = autoSession
): List[Entity] = {
if (hasManyAssociations.size > 0) {
findAllWithLimitOffsetForOneToManyRelations(pagination.limit, pagination.offset, orderings)
} else {
findAllWithLimitOffset(pagination.limit, pagination.offset, orderings)
}
}
override def findAllWithLimitOffset(limit: Int = 100, offset: Int = 0, orderings: Seq[SQLSyntax] = defaultOrderings)(
implicit s: DBSession = autoSession
): List[Entity] = {
if (hasManyAssociations.size > 0) {
findAllWithLimitOffsetForOneToManyRelations(limit, offset, orderings)
} else {
implicit val repository = IncludesQueryRepository[Entity]()
appendIncludedAttributes(extract(withSQL {
val sql = selectQueryWithAssociations.where(defaultScopeWithDefaultAlias)
(if (orderings.isEmpty) sql else sql.orderBy(sqls.csv(orderings: _*))).limit(limit).offset(offset)
}).list.apply())
}
}
def findAllWithLimitOffsetForOneToManyRelations(limit: Int = 100,
offset: Int = 0,
orderings: Seq[SQLSyntax] = defaultOrderings)(
implicit s: DBSession = autoSession
): List[Entity] = {
// find ids for pagination
val ids: List[Any] = withSQL {
if (orderings.isEmpty) singleSelectQuery.limit(limit).offset(offset)
else {
singleSelectQuery
.orderBy(orderings.headOption.getOrElse(defaultOrdering))
.limit(limit)
.offset(offset)
}
}.map(_.any(defaultAlias.resultName.field(primaryKeyFieldName))).list.apply()
if (ids.isEmpty) {
Nil
} else {
implicit val enableAsIs = ParameterBinderFactory.asisParameterBinderFactory
implicit val repository = IncludesQueryRepository[Entity]()
appendIncludedAttributes(extract(withSQL {
val sql = selectQueryWithAssociations.where(
sqls.toAndConditionOpt(
defaultScopeWithDefaultAlias,
Some(sqls.in(defaultAlias.field(primaryKeyFieldName), ids))
)
)
if (orderings.isEmpty) sql else sql.orderBy(sqls.csv(orderings: _*))
}).list.apply())
}
}
override def findBy(where: SQLSyntax)(implicit s: DBSession = autoSession): Option[Entity] = {
implicit val repository = IncludesQueryRepository[Entity]()
appendIncludedAttributes(extract(withSQL {
selectQueryWithAssociations
.where(sqls.toAndConditionOpt(Some(where), defaultScopeWithDefaultAlias))
}).list.apply()).headOption
}
override def findAllBy(where: SQLSyntax, orderings: Seq[SQLSyntax] = defaultOrderings)(
implicit s: DBSession = autoSession
): List[Entity] = {
implicit val repository = IncludesQueryRepository[Entity]()
appendIncludedAttributes(extract(withSQL {
val sql = selectQueryWithAssociations
.where(sqls.toAndConditionOpt(Some(where), defaultScopeWithDefaultAlias))
if (orderings.isEmpty) sql else sql.orderBy(sqls.csv(orderings: _*))
}).list.apply())
}
override def findAllByWithLimitOffset(where: SQLSyntax,
limit: Int = 100,
offset: Int = 0,
orderings: Seq[SQLSyntax] = defaultOrderings)(
implicit s: DBSession = autoSession
): List[Entity] = {
if (hasManyAssociations.size > 0) {
findAllByWithLimitOffsetForOneToManyRelations(where, limit, offset, orderings)
} else {
implicit val repository = IncludesQueryRepository[Entity]()
appendIncludedAttributes(extract(withSQL {
val sql = selectQueryWithAssociations
.where(sqls.toAndConditionOpt(Some(where), defaultScopeWithDefaultAlias))
if (orderings.isEmpty) sql.limit(limit).offset(offset)
else sql.orderBy(sqls.csv(orderings: _*)).limit(limit).offset(offset)
}).list.apply())
}
}
def findAllByWithLimitOffsetForOneToManyRelations(
where: SQLSyntax,
limit: Int = 100,
offset: Int = 0,
orderings: Seq[SQLSyntax] = defaultOrderings
)(
implicit s: DBSession = autoSession
): List[Entity] = {
// find ids for pagination
val ids: Seq[Any] = withSQL {
lazy val allowedForDistinctQuery: Seq[SQLSyntax] = {
columns.map(column => SQLSyntax.createUnsafely(s"${defaultAlias.tableAliasName}.${column}", Nil)).toIndexedSeq
}
val baseQuery = {
val columnsToFetch: Seq[SQLSyntax] = Seq(sqls"distinct ${defaultAlias.field(primaryKeyFieldName)}") ++ {
// in this case, intentionally orderings are empty
if (orderings.isEmpty) Seq.empty
else orderingsForDistinctQuery(orderings, allowedForDistinctQuery).map(removeAscDesc)
}
selectQueryWithAdditionalAssociations(
select(columnsToFetch: _*).from(as(defaultAlias)),
belongsToAssociations ++ includedBelongsToAssociations,
hasOneAssociations ++ includedHasOneAssociations,
hasManyAssociations ++ includedHasManyAssociations.toSet
)
}
val query = baseQuery.where(sqls.toAndConditionOpt(Some(where), defaultScopeWithDefaultAlias))
if (orderings.isEmpty) query.limit(limit).offset(offset)
else
query
.orderBy(sqls.csv(orderingsForDistinctQuery(orderings, allowedForDistinctQuery): _*))
.limit(limit)
.offset(offset)
}.map(_.any(1)).list.apply()
if (ids.isEmpty) {
Nil
} else {
implicit val enableAsIs = ParameterBinderFactory.asisParameterBinderFactory
implicit val repository = IncludesQueryRepository[Entity]()
appendIncludedAttributes(extract(withSQL {
val sql = selectQueryWithAssociations
.where(
sqls.toAndConditionOpt(
Option(where),
defaultScopeWithDefaultAlias,
Some(sqls.in(defaultAlias.field(primaryKeyFieldName), ids))
)
)
if (orderings.isEmpty) sql else sql.orderBy(sqls.csv(orderings: _*))
}).list.apply())
}
}
private[this] def removeAscDesc(s: SQLSyntax): SQLSyntax = {
SQLSyntax.createUnsafely(
s.value
.replaceFirst(" desc$", "")
.replaceFirst(" asc$", "")
.replaceFirst(" DESC$", "")
.replaceFirst(" ASC$", ""),
s.parameters
)
}
private[this] def orderingsForDistinctQuery(orderings: Seq[SQLSyntax],
allowedForDistinctQuery: Seq[SQLSyntax]): Seq[SQLSyntax] = {
orderings.filter { o =>
allowedForDistinctQuery.exists(_.value == removeAscDesc(o).value)
}
}
}