[英]Create a generic repository with Jooq
We are using JOOQ to map an existing database.我们正在使用 JOOQ 来 map 一个现有的数据库。 Every table (bar 1) in this database shares a similar set of columns:此数据库中的每个表(第 1 条)共享一组相似的列:
resource_id: Int (FK)
id: Int (PK)
identifier: UUID
date_created: DateTime
date_modified: DateTime
There is a general rule in the database for all operations that looks something like this:数据库中的所有操作都有一个通用规则,如下所示:
select *
from table t
join resource r on r.id = t.resource_id
where r.is_archived = false
Using JOOQs generation feature, I can successfully create a repository class that implements the basic query operations:使用 JOOQs 生成功能,我可以成功创建实现基本查询操作的存储库 class:
@Repository
class UserRepository(val dsl:DSLContext): IRepository {
val record = Tables.AUTHENTICATION_USER!! //generated by jooq
val resource = Tables.RESOURCE!! //generated by jooq
val pojo = AuthenticationUser::class.java //generated by jooq
// this is the general query logic that should be applied to all the things
override fun selectQuery(): SelectConditionStep<Record> {
return dsl
.select(record.asterisk())
.from(record)
.join(resource).on(resource.ID.eq(record.RESOURCE_ID))
.where(resource.IS_ARCHIVED.eq(false))
}
override fun asyncCount(): Mono<Int> = Mono.fromCallable {
selectQuery()
.count()
}
fun asyncGetPage(from: Int,
size: Int,
orderBy: SortField<*> = record.DATE_CREATED.asc(),
) = Mono.fromCallable {
selectQuery()
.orderBy(orderBy)
.limit(size)
.offset(from)
.fetchInto(pojo)
.let { Collections.unmodifiableList(it) }
}
Now, this is all well and good, but I am having trouble converting this to a generic implementation that can现在,这一切都很好,但是我无法将其转换为可以
table
of type T
接受类型T
的table
I can get this to work:我可以让它工作:
class GeneralRepository<T: Table<R>, R: Record>(val table: T, val record: R, val dsl:DSLContext) {
fun asyncGetAll(): Mono<List<R>> = Mono.fromCallable {
dsl
.select()
.from(table)
.fetchInto(record::class.java)
}
}
// manually construct it ignoring Springs DI for development
val resourceRepository = GeneralRepository(
Tables.RESOURCE,
ResourceRecord(),
JooqConfiguration().dsl(),
)
The problem comes when I try to do anything with the table T
.当我尝试对table T
做任何事情时,问题就来了。 Since T
is of a generic table type, the compiler has no way of knowing about the fields id
, identifier
, resource_id
, date_created
etc, meaning queries like this cannot compile:由于T
是通用表类型,编译器无法知道字段id
、 identifier
、 resource_id
、 date_created
等,这意味着这样的查询无法编译:
fun asyncGetAllNonArchived() =
dsl
.select()
.from(table)
.where(table.DATE_CREATED > ...)
Question: Is there a way to tell the compiler or declare the generics in such a way that the common fields are available.问题:有没有办法告诉编译器或声明 generics 以使公共字段可用。
EDIT: Thanks to Lukas Eder below for the advice.编辑:感谢下面的 Lukas Eder 的建议。 Adapting that solution we were able to create a generic repository.调整该解决方案,我们能够创建一个通用存储库。
First off, changing the generator strategy to Kotlin let the Auditable
interface act correctly:)首先,将生成器策略更改为 Kotlin 让Auditable
接口正确运行:)
Secondly, we couldn't find a way with the generic class to implement the Table
types in terms of the Record
types, so you have to very slightly extend the signature.其次,我们找不到使用通用 class 的方法来根据Record
类型来实现Table
类型,因此您必须稍微扩展签名。
interface Auditable {
fun RESOURCE_ID(): TableField<Record, Int> =
(this as Table<Record>).field("resource_id") as TableField<Record, Int>
fun ID(): TableField<Record, Int> =
(this as Table<Record>).field("id") as TableField<Record, Int>
fun IDENTIFIER(): TableField<Record, UUID> =
(this as Table<Record>).field("identifier") as TableField<Record, UUID>
fun DATE_CREATED(): TableField<Record, LocalDateTime> =
(this as Table<Record>).field("date_created") as TableField<Record, LocalDateTime>
fun DATE_MODIFIED(): TableField<Record, LocalDateTime> =
(this as Table<Record>).field("date_modified") as TableField<Record, LocalDateTime>
}
open class GeneralRepository<R, T>(val table: T) where R: Record, T: Table<R>, T: Auditable {
val resource = Resource.RESOURCE
fun selectQuery() =
dsl
.select(table.asterisk())
.from(table)
.join(resource).on(resource.ID.eq(table.RESOURCE_ID()))
.where(resource.IS_ARCHIVED.eq(false))
You can then easily implement generic extensions:然后,您可以轻松实现通用扩展:
class UserRepo: GeneralRepository<UserRecord, User>(table = User.USER) {
fun getEmail(email: String) = Mono.fromCallable {
selectQuery().and(table.EMAIL.eq(email))
}
}
You could generate an interface that provides access to these columns:您可以生成一个接口来提供对这些列的访问:
interface Auditable {
fun RESOURCE_ID(): TableField<Record, Int?> =
(this as Table<Record>).field("resource_id") as TableField<Record, Int?>
fun ID(): TableField<Record, Int?> =
(this as Table<Record>).field("id") as TableField<Record, Int?>
fun identifier(): TableField<Record, UUID?> =
(this as Table<Record>).field("identifier") as TableField<Record, UUID?>
fun DATE_CREATED(): TableField<Record, LocalDateTime?> =
(this as Table<Record>).field("date_created") as TableField<Record, LocalDateTime?>
fun DATE_MODIFIED(): TableField<Record, LocalDateTime?> =
(this as Table<Record>).field("date_modified") as TableField<Record, LocalDateTime?>
}
and then attach the interface to all generated tables using a generator strategy :然后使用生成器策略将接口附加到所有生成的表:
<strategy>
<matchers>
<tables>
<table>
<expression>T_.*</expression>
<tableImplements>com.example.Auditable</tableImplements>
</table>
</tables>
</matchers>
</strategy>
It is currently not possible to declare properties in the interface because it's not possible to detect whether an override
modifier is required, see https://github.com/jOOQ/jOOQ/issues/10776 .目前无法在界面中声明属性,因为无法检测是否需要override
修饰符,请参阅https://github.com/jOOQ/jOOQ/issues/10776 。
But this may be good enough for now.但这目前可能已经足够了。 You can then create generic conditions like this:然后,您可以创建如下一般条件:
fun <T> dateCreatedCondition(table: T, dt: LocalDateTime): Condition
where T : Table<*>, T : Auditable = table.DATE_CREATED().gt(dt)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.