Kenster
Kenster

Reputation: 25399

Get spring-data-mongodb to honor getter/setter without backing field?

I have a general-purpose POJO:

public class Thing {
    private String name;
    private String etc;

    public String getName() {
        return name;
    }
    // other getters and setters
}

I'm using Spring 4.3.9 and Spring-data-mongodb 1.10.4. I want to store instances of this POJO in Mongodb, but I have some constraints:

  1. I can't add Spring annotations to the base class (but I can subclass Thing and annotate that).
  2. I want to use the name field as the Mongodb unique ID (mainly to avoid creating a separate unique index for it).
  3. I want to (redundantly) store the name field as an actual field named "name", so that other consumers of the collection don't have to know that "name" is stored in the _id.

I started out trying this:

public class SpringThing extends Thing {
    @Id
    @Override
    public String getName() {
        return super.getName();
    }
    @Override
    public void setName(String name) {
        super.setName(name);
    }
}

This causes spring to use the value of name for _id, but of course it doesn't store a field named "name" in Mongodb. The documentation says that spring will use a "property or field" named "id" or annotated with @Id. So I tried defining a redundant getter/setter which accesses the name field:

public class SpringThing extends Thing {
    @Id
    public String getId() {
        return super.getName();
    }
    public void setId(String id) {
        super.setName(id);
    }
}

Unfortunately, spring ignores getId and setId here, and stores the object with an autogenerated ID. I also tried creating redundant getters/setters annotated with @Field("name"), but spring seems to ignore any getter/setter pair without an actual field.

Adding an actual ID field and storing a copy of the name there does work:

public class SpringThing extends Thing {
    @Id
    private String id;

    @Override
    public void setName(String id) {
        this.id = id;
        super.setName(id);
    }
}

But it requires defining a pointless field named "id".

Is there a more reasonable way to get what I want? Is what I'm trying to do reasonable to begin with?

Upvotes: 4

Views: 4614

Answers (1)

Kenster
Kenster

Reputation: 25399

Thanks to a hint by @mp911de, I ended up creating a subclass of Thing that looks like this:

@TypeAlias("thing")
@Document(collection = "things")
public class SpringThing extends Thing {

    @Id
    @AccessType(Type.PROPERTY)
    @JsonIgnore
    public String getId() {
        return super.getName();
    }

    public void setId(String taskName) {
        super.setName(taskName);
    }
}
  • The @TypeAlias annotation overrides the name which spring would use for the type, to cover up the fact that I've created a subclass just to add annotations.
  • @Id says that this is the getter for _id.
  • @AccessType says to access this field through the getter and setter rather than by directly accessing the field. This is what I needed; without it, spring looks for a private member variable named something like id.
  • @JsonIgnore is the Jackson (JSON library that we're using) annotation to prevent including the id field when serializing these objects to JSON.

Upvotes: 9

Related Questions