11

JPAのcriteriaAPIとその型安全性で何かを見逃しているようです。次のコードを検討してください。

@Entity
@Access(FIELD)
class User(

  @Id
  Long id;

  @Column(unique=true)
  String email;

  String password;
}

ここにメタモデルがあります:

@StaticMetamodel(User.class)
public static class User_ {
  public static volatile SingularAttribute<User, Long> id;
  public static volatile SingularAttribute<User, String> email;
  public static volatile SingularAttribute<User, String> password;
}

次に、Java EEチュートリアルのページを使用して構築された、クラスを実行するためのコード:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> user = cq.from(User.class);
cq.select(user);
cq.where(cb.equal(user.get(User_.email), "john@google.com")); //this line is my problem

TypedQuery<User> q = em.createQuery(cq);
List<User> allUsers = q.getResultList();

assertEquals(1, allUsers.size());

正常に動作します。ただし、 "where"句を変更して、文字列ではなく整数( "john@google.com")を使用すると、コードがコンパイルされないことが予想されました。それでも、正常にコンパイルされます。

基準APIはタイプセーフであると考えられていましたか?これは、標準のJPQLを使用した場合、以下よりもタイプセーフになることはほとんどありません。つまり、上記のコードのメタモデルの目的は何ですか?私はそれから何も得ていません。

User u = em.createQuery("select u from User u where u.email = :email", User.class)
           .setParameter("email", "john@google.com")
       .getSingleResult();

したがって、問題は次のとおりです。「from」句に文字列のみを渡すことができるように、条件APIクエリをよりタイプセーフにすることはできますか?

4

3 に答える 3

10

Expression<T>型の安全性は、メタモデルで定義されている正確な型ではなく、インターフェイスのジェネリック型の上限に制限されています。したがって、 CriteriaBuilder.equal(Expression<?> x, java.lang.Object y)型の引数を取るためExpression<?>、比較のために任意のオブジェクトを渡すことができます。

他のCriteriaBuiler方法は、よりタイプセーフです。たとえばCriteriaBuilder.ge(Expression<? extends java.lang.Number> x, Expression<? extends java.lang.Number> y)、数字のみを許可します。ただし、たとえば整数フィールドを浮動小数点数と比較することができます。

これ以上のことはできません。メソッドはCriteriaBuilder.equal(Expression<T> x, T y)、Tがメタモデルのフィールドタイプであるようなものである必要があります。

もちろん、これは基準APIの型安全性の穴です。JPAAPIの作成者がこれらのメソッドのワイルドカードバージョンを選択した理由はわかりません。

于 2012-08-21T21:59:39.350 に答える
3

厳密な型照合が本当に必要な場合は、in()を使用でき、クエリは次のようになります。

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> user = cq.from(User.class);
cq.select(user);
cq.where(cb.in(user.get(User_.email)).value("john@google.com"));

TypedQuery<User> q = em.createQuery(cq);
List<User> allUsers = q.getResultList();

assertEquals(1, allUsers.size());

またはあなたも書くことができます

cq.where(cb.in(user.get(User_.email)).value(cq.literal("john@google.com")));

equal()使用と値が1つしかない-clauseには違いがありますがin()、これにより、静的型モデルに従って厳密な型付けが実現されます。ほとんどのデータベースエンジンは、同じように1つの値で最適=化されます。in()

切り替えたくない場合は、このような関数を記述して、コンパイラーがタイプミスの可能性を警告するようにすることもできますequal()in()

static <T> Predicate equal(CriteriaBuilder cb, Expression<T> left, T right) {
    return cb.equal(left, right);
}

static <T> Predicate equal(CriteriaBuilder cb, Expression<T> left, Expression<T> right) {
    return cb.equal(left, right);
}

最後に、独自のequal()-wrapperを使用するか、それに切り替えるかどうかにかかわらず、-objectsはキャスト互換性が低く、通常はデータベースに整数幅の変更を処理させる方がよいため、より頻繁に、...関数in()が必要になる場合があります。 。toLong()CriteriaBuilderNumber

于 2014-02-20T23:06:38.100 に答える
0

cq.where(booelan、String)Javaは、「文字列への数値の暗黙的な型キャスト」をコミットします。 整数は、メソッドの文字列型引数を初期化するために直接入力されるため、正当な文字列です。問題が発生する可能性のある言語での唯一の場所と方法。最初にアドレス構文の有効性を確認し、そのような問題が発生した場合の処理​​方法を検討する必要があります。

于 2012-08-21T20:17:45.817 に答える