受@konqi 回答的启发,我想出了一个类似的解决方案。
这个想法是创建User_Name 和User_Email 实体,它们将保留迄今为止创建的所有用户的姓名和电子邮件。不会有亲子关系。为方便起见,我们还将保留用户的姓名和电子邮件属性;我们正在用存储空间换取更少的读/写。
@Entity
public class User {
@Id public Long id;
@Index public String name;
@Index public String email;
// other properties...
}
@Entity
public class User_Name {
private User_Name() {
}
public User_Name(String name) {
this.name = name;
}
@Id public String name;
}
@Entity
public class User_Email {
private User_Email() {
}
public User_Email(String email) {
this.email = email;
}
@Id public String email;
}
现在通过检查唯一字段在事务中创建用户:
User user = ofy().transact(new Work<User>() {
@Override
public User run()
{
User_Name name = ofy().load().key(Key.create(User_Name.class, data.username)).now();
if (name != null)
return null;
User_Email email = ofy().load().key(Key.create(User_Email.class, data.email)).now();
if (email != null)
return null;
name = new User_Name(data.username);
email = new User_Email(data.email);
ofy().save().entity(name).now();
ofy().save().entity(email).now();
// only if email and name is unique create the user
User user = new User();
user.name = data.username;
user.email = data.email;
// fill other properties...
ofy().save().entity(user).now();
return user;
}
});
这将保证这些属性的唯一性(至少我的测试经验证明了这一点:))。通过不使用Ref<?>s,我们可以保持数据紧凑,从而减少查询。
如果只有一个唯一属性,最好将其设为主实体的@Id。
还可以将用户的@Id设置为email或者姓名,新种类的数量减一。但我认为为每个独特的属性创建一个新的实体类型会使意图(和代码)更加清晰。