質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

解決済

1回答

655閲覧

ジェネリクスで宣言したを関数に渡したい

Mr.meat

総合スコア5

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

0グッド

0クリップ

投稿2021/12/18 01:20

只今勉強のため、kotlinにてCriteriaを用いたDB接続、Select文の実行を行おうとしていたのですが、
Select実行の関数では、Entityクラスを引数にしてコードの文字数を減らしたいと思い、
ジェネリクスで記載しましたが、下記のエラーが発生しました。

Type mismatch: inferred type is CriteriaQuery<out T!!> but CriteriaQuery<CapturedType(out T!!)> was expected

本件について、自分なりに調査をしましたが、未だ解決策を見つけられていません。
参考にしたソースは下記URLになります。
https://freegian.co.jp/blog/springboot-jpa-kotlin

参考にしたサイトから手を加えた箇所はSearch関数のみです。
他のエラーは有りません。

該当のソースコード

kotlin

1class DBConnect(private val repository : RdbRepository){ 2 3 private val SaveRepository = repository 4 5public fun <T> Search( name: String ,EntityName : T/*変更箇所*/, repository: RdbRepository = SaveRepository): List<T>{ 6 return repository.findByCriteria(EntityName!!::class.java) { criteria -> 7 criteria.where { condition -> condition.equal("tagname" , name) 8 }.result() 9 } 10 } 11} 12 13 14

kotlin

1@Repository 2class RdbRepository (@PersistenceContext private val em: EntityManager) { 3 fun <T> findByCriteria(entityClass: Class<T>, func: (RdbCriteria<T>) -> CriteriaQuery<T>): List<T> { 4 return em.createQuery(func(RdbCriteria.build(em, entityClass))).resultList 5 } 6}

kotlin

1class RdbCriteria<T>( 2 private val entityClass: Class<T>, 3 private val builder: CriteriaBuilder, 4 private val query: CriteriaQuery<T>, 5 private val root: Root<T>, 6 private val condition: RdbCriteriaCondition<T>, 7 private val predicates: MutableSet<Predicate> = mutableSetOf() 8) { 9 fun where(func: (RdbCriteriaCondition<T>) -> (RdbCriteriaCondition<T>)): RdbCriteria<T> { 10 this.query.where(*func(condition).build()) 11 return this 12 } 13 fun result(): CriteriaQuery<T> { 14 return result { it } 15 } 16 @Suppress("UNCHECKED_CAST") 17 fun result(extension: (CriteriaQuery<T>) -> CriteriaQuery<T>): CriteriaQuery<T> { 18 return extension(query) 19 } 20 companion object { 21 private const val DefaultAlias = "r" 22 fun <T> build(em: EntityManager, entityClass: Class<T>, alias: String = DefaultAlias): RdbCriteria<T> { 23 val builder = em.criteriaBuilder 24 val query = builder.createQuery(entityClass) 25 val root = query.from(entityClass) 26 root.alias(alias) 27 return RdbCriteria( 28 entityClass = entityClass, 29 builder = builder, 30 query = query, 31 root = root, 32 condition = RdbCriteriaCondition(root = root, builder = builder) 33 ) 34 } 35 } 36}

kotlin

1class RdbCriteriaCondition<T>( 2 private val builder: CriteriaBuilder, 3 private val root: Root<T>, 4 private val predicates: MutableSet<Predicate> = mutableSetOf() 5) { 6 fun build(): Array<Predicate> { 7 return this.predicates.toTypedArray() 8 } 9 private fun add(predicate: Predicate): RdbCriteriaCondition<T> { 10 this.predicates.add(predicate) 11 return this 12 } 13 fun equal(field: String, value: Any?): RdbCriteriaCondition<T> { 14 if (isValid(value)) { 15 val fieldPath: Path<Any> = root.get(field) 16 add(builder.equal(fieldPath, value)) 17 } 18 return this 19 } 20 private fun isValid(value: Any?): Boolean { 21 return when (value) { 22 is String? -> !value.isNullOrBlank() 23 is Optional<*> -> value.isPresent 24 else -> value != null 25 } 26 } 27}

試したこと

kotlinのリファレンスを参照。
他には手詰まりでした。

テスト環境

Kotlin version 1.5.10-release-890 (JRE 16.0.1+9)
IDEは使用しておりません。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

RdbRepository.findByCriteriaの引数どおりに、クラスリテラルをそのまま持ってきたらいいんじゃないでしょうか。

kotlin

1public fun <T> Search( name: String ,EntityName : Class<T> /* <- コレ */, repository: RdbRepository = SaveRepository): List<T>{ 2 return repository.findByCriteria(EntityName /* そのまま渡す */) { criteria -> 3 4 criteria.where { condition -> condition.equal("tagname" , name) 5 }.result() 6 } 7}

呼ぶ側がこんな感じ。

kotlin

1db.Search("tagname", Shop::class.java)

呼ぶ側の「::class.java」をSearch呼ぶ度に書きたくない、と言うのが目的なら、DBConnectクラスをジェネリックにするのはどうですか。

kotlin

1class DBConnect<T>(private val entityClazz: Class<T>) { 2 fun Search( name: String): List<T>{ 3 return repo.findByCriteria(this.entityClazz) 4 } 5} 6 7/// 呼ぶ側 8 9val db = DBConnect(Shop::class.java) // 最初に一度だけ指定 10db.Search("tagname") 11

※示されている元の書き方「EntityName : T/変更箇所/」だと、呼びだし時にentityのインスタンスを作る感じになるんじゃないでしょうか。

kotlin

1db.Search("tagname", Shop()) // <- Shopをnew

静的な型解決のためのジェネリクスという機能は、動的なインスタンスから型情報を引いて解決するような記述は不可能と言うのが僕の理解で、その書き方でエラー回避はムリな気がします。仮に回避できる方法があるとしても、Search呼ぶためだけにムダなインスタンス作らないといけないので、あまりいい関数に思えないです。(意図と違ったらすみません)

※Hibernateまで含めた環境作っての確認はしてません。書きっぷりだけで言ってますので、すいませんがあしからず。

投稿2021/12/18 15:00

umau

総合スコア805

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Mr.meat

2021/12/18 20:57

ご教授いただき、エラーを回避することが出来ました。ありがとうございます。 私がジェネリクスに対する理解が足りていないことを自覚できる良い機会になりました。 確かにSQLへの命令をまとめて、引数にエンティティを入れるほうが関数がスッキリできるのも良い知見をいただけました。 初めての質問で拙い部分もあったとは思いますが、ご回答いただきありがとうございました。 ベストアンサーにさせていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問