我似乎错过了JPA的标准API及其类型安全性。请考虑以下代码:
@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;
}
然后使用the Java EE Tutorial中的页面构建一些代码来运用该类:
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”子句以使用Integer而不是String(“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();
所以问题是:我可以使条件API查询更安全,因此我只能将字符串传递给“from”子句吗?
答案 0 :(得分:8)
类型安全性仅限于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的类型安全漏洞。我不知道为什么JPA API创建者选择了这些方法的通配符版本。
答案 1 :(得分: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()
和in()
- 子句只有一个值之间存在差异,但这会根据您的静态类型模型实现严格的输入。大多数数据库引擎会以相同的方式使用一个值优化=
和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()
- 包装器或切换到in()
,您可能需要更toLong()
的{{1}} ...函数作为CriteriaBuilder
- 对象不是非常兼容的,通常最好让数据库处理整数宽度的变化。
答案 2 :(得分:0)
cq.where(booelan,String) Java提交“将数字隐式类型转换为字符串”。 直接输入整数来初始化方法String类型参数,因此它是一个合法的字符串。 围绕语言中唯一可能发生问题的地方和方式。您将只需要首先检查地址语法的有效性,并考虑如何处理这样的问题。