KenavR
KenavR

Reputation: 3899

GAE Datastore generate String key with JPA

Since it isn't possible to use only Long Ids I am trying to use the generated String keys. I have three Classes User, Topic, Comments with User - 1:n - Topic - 1:n -Comments.

Class Comment:

@Entity
public class Comment implements Serializable{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
    private String key;
    @ManyToOne
    private User author;
    @ManyToOne
    private Topic topic;

Class User:

@Entity
public class User implements Serializable{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
    private String key;

    @Unique
    private String username;

Class Topic:

@Entity
public class Topic implements Serializable{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
    private String key;
    @ManyToOne(cascade = CascadeType.ALL)
    private User author;
    @OneToMany(cascade = CascadeType.ALL)
    private List<Comment> comments;

Now when I am trying to save a new User, the following exception occurs

Invalid primary key for User.  Cannot have a null primary key field if the field is unencoded and of type String.  Please provide a value or, if you want the datastore to generate an id on your behalf, change the type of the field to Long. 

Is it possible to let the String ID get generated without manually using the KeyFactory? If yes whats wrong with my code?

thanks

Upvotes: 0

Views: 1317

Answers (2)

DataNucleus
DataNucleus

Reputation: 15577

IIRC IDENTITY strategy is to generate numeric (or Key) ids. If you were using JDO you could use UUID-style ids auto-generated. See https://developers.google.com/appengine/docs/java/datastore/jdo/creatinggettinganddeletingdata#Keys

Upvotes: 1

Zaw Than oo
Zaw Than oo

Reputation: 9935

I use TableGenerator. It is useful whatever you want id style. Let's say even if you would like to get Group id like GRP0000001 and GRP0000500 etc.. You have to use property injection, not field injection in your entity. It is based on your setter ID method. If generated id is 201 by EntityManager generate, entity manager will call setId() in setter injection. If so, the id will be GRP0000201.

My Example:

@Entity
@TableGenerator(name = "GROUP_GEN", table = "ID_GEN", pkColumnName = "GEN_NAME", 
                valueColumnName = "GEN_VAL", pkColumnValue = "GROUP_GEN", allocationSize = 1)
@Access(value = AccessType.FIELD)
public class Group implements Serializable {
    @Transient
    private String id;
    private String name;
    //name getter and setter
    public void setId(String id) {
        if(id != null) {
            this.id = Utils.formatId(id, "GRP", 10);    
        }
    }

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "GROUP_GEN")
    @Access(value = AccessType.PROPERTY)
    public String getId() {
        return id;
    }
}

Utils.java

public class Utils {
    /**
     * E.g.
     * Input: id=523, prefix="AAA", maxLength=15
     * Output: AAA000000000523
     */
    public static String formatId(String id, String prefix, int maxLength) {
        if (!id.startsWith(prefix)) {
            int length = id.length() + prefix.length();
            for (; (maxLength - length) > 0; length++) {
                id = '0' + id;
            }
            id = prefix + id;
        }
        return id;
    }
}

Upvotes: 0

Related Questions