如何使Morphia将字段的值设置为函数调用返回的值?

时间:2014-12-26 22:22:11

标签: java mongodb auto-increment morphia

我正在使用此处描述的策略实现“自动增量”ID: http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/

基本上,seqId字段的值是通过调用实用程序函数来设置的,该函数更新辅助集合上的计数器并返回递增的值。听起来不错。

我的问题在于将此映射与Morphia一起使用。本教程建议执行插入(例如在shell中),如下所示:

db.users.insert(
   {
     seqId: getNextSequence("userid"),
     name: "Sarah C."
   }

我基本上希望做一些事情,例如将POJO seqId字段设置为Morphia在调用save()时将其转换为上面的插入内容。

我的POJO看起来像这样:

@Entity
public class User {

    @Id
    private Long id;

    // THIS IS THE FIELD I WANT TO AUTO-INCREMENT
    private Long seqId;

    private String name;

    ...
}

问题是:如何让Morphia将字段的值设置为函数调用返回的值?

我研究了使用@PrePresist注释来执行此函数调用并获取值,然后在+ _id字段中设置它。这有几个缺点,比如多次调用MongoDB而不只是一个,还有我的模型对象没有对数据存储区的引用这一事实,我宁愿不要混淆这些问题。

这可能吗?有什么建议吗?

我使用最新的Java驱动程序使用MongoDB 2.6.6。

谢谢!

PS:我知道在大型环境中不建议使用自动增量。无论如何我都需要这个特定场景。

1 个答案:

答案 0 :(得分:1)

我将描述对我们有用的解决方案。请注意,这支持类级别的自动增量及其子集 - 因此您可以计算用户或管理员用户(具有管理枚举或其他用户的用户)。

这包含每个自动增量字段的当前值,它基本上是一个引用:

@Entity(noClassnameStored = true)
public class AutoIncrementEntity {

  @Id
  protected String key;

  protected Long value = 1L;

  protected AutoIncrementEntity() {
    super();
  }

  /**
   * Set the key name — class or class with some other attribute(s).
   */
  public AutoIncrementEntity(final String key) {
    this.key = key;
  }

  /**
   * Set the key name and initialize the value so it won't start at 1.
   */
  public AutoIncrementEntity(final String key, final Long startValue) {
    this(key);
    value = startValue;
  }

  public Long getValue() {
    return value;
  }
}

在您的持久性服务中,您可以使用以下命令自动设置/创建自动增量:

public <E extends BaseEntity> ObjectId persist(E entity) {

  // If it's a user and doesn't yet have an ID, set one; start counting from 1000.
  if ((entity instanceof UserEntity) && (((UserEntity) entity).getUserId() == null)) {
    ((UserEntity) entity).setUserId(
      generateAutoIncrement(entity.getClass().getName(), 1000L));
  }

  // Additionally, set an ID within each user group; start counting from 1.
  if ((entity instanceof UserEntity) && (((UserEntity) entity).getRoleId() == null)) {
    ((UserEntity) entity).setRoleId(
      generateAutoIncrement(entity.getClass().getName() + "-" + entity.getRole(), 1L));
  }

  mongoDataStore.save(entity);
  return entity.getId();
}

/**
 * Return a unique numeric value for the given key.
 * The minimum value, set to 1 if nothing specific is required.
 */
protected long generateAutoIncrement(final String key, final long minimumValue){

  // Get the given key from the auto increment entity and try to increment it.
  final Query<AutoIncrementEntity> query = mongoDataStore.find(
        AutoIncrementEntity.class).field("_id").equal(key);
  final UpdateOperations<AutoIncrementEntity> update = mongoDataStore
        .createUpdateOperations(AutoIncrementEntity.class).inc("value");
  AutoIncrementEntity autoIncrement = mongoDataStore.findAndModify(query, update);

  // If none is found, we need to create one for the given key.
  if (autoIncrement == null) {
      autoIncrement = new AutoIncrementEntity(key, minimumValue);
      mongoDataStore.save(autoIncrement);
  }
  return autoIncrement.getValue();
}

最后你的实体:

@Entity(value = "user", noClassnameStored = true)
public class UserEntity extends BaseEntity {

  public static enum Role {
    ADMIN, USER,
  }

  private Role role;

  @Indexed(unique = true)
  private Long userId;

  private Long roleId;

  // Role setter and getter

  public Long getUserId() {
    return userId;
  }
  public void setUserId(Long userId) {
    this.userId = userId;
  }

  public Long getRoleId() {
    return roleId;
  }
  public void setRoleId(Long roleId) {
    this.roleId = roleId;
  }

}

实体中没有具体的内容。所有逻辑都由持久性服务处理。我没有使用@PrePersist,因为你需要将持久性服务放入实体中,这听起来不是一个好主意。