My understanding is that when using objectify, you can generate a datastore key in one of two ways:
Specify a unique ID to an object, which will be used to generate the datastore key (unique ID assigned to data member with annotation #Id)
Don't specify unique ID, so a datastore key will be autogenerated (Annotation #Id on data member still exists, but nothing is assigned to it)
Using option 1, in order to load a specified entity we first obtained a key using Key.create(SomeEntity.class, uniqueID) where uniqueID would be unique.
If I decide to go with option 2, how do I load a desired entity if I had let the datastore autogenerate a key?
With any other parameter? If you create an object without deciding on the key, but provide (for instance) a city, a userName, an email, then you search by city, username, email. Once you have that, you have the object. From there you can get the object's key if you need it.
If your entity #Id field is null and you save it synchronously with .now() then the #Id annotated field (assumed "id" in this example) will be set to an auto-allocated/generated value during the following call.
ofy().save().entity(someEntity).now();
Long myId = someEntity.getId();
Then someEntity.getId() would be a valid generated String/Long that you can use, note somewhere, provide to a user, return from an API etc so that later:
ofy().load().type(Entity.class).id(myId).now();
If you need to save the entity asynchronously (without .now()) then that's possible too, just use the allocateId function from ObjectifyFactory to set the id before saving to ensure a unique id - this is similar to your 1) but a safe way to generate the UniqueId that is assured to be unique.
Searching afterwards for the entry by querying a combination of fields should typically be avoided as depending on your data it may not match a single entry and you'd need to index those fields adding costs.
Related
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
private String externalID; //<--- why we need this?
}
Someone has suggested me to include an external Id field in a class something like that? Any suggestions why that could be?
Not sure, what exactly meant by externalID here, since the case of usage is not clear.
But, I assume a couple of cases:
1. External service
External id may be used to map your entity with some id of another resource from different services. Something, that identifies this entity in another system.
For example: in externalID may be stored person twitter id or bank account id.
2. Security-wise
externalID is used to protect (encapsulate) internal id been visible outside, which may cause some security vulnerabilities.
For example:
In your case, internal id is Integer with GenerationType.AUTO, that means, all entities will have an incremental id: 1, 2, 3, ...
Knowing that someone may analyze your API calls and easily iterate through all your accounts via API, e.g: GET api/person/{id}.
Usually, a different type of IDs is used to solve this problem, like UUID, e.g.: 8b9af550-a4c7-4181-b6ba-1a1899109783. Which can be used as externalID in your case.
So, I assume this is the reason to add additional externalID to your entity.
Note: if your Database supports the usage of UUID (or store it as String), you can simply replace your internal id type with UUID and get rid of externalID here.
It is possible that externalID represents the Primary Key of another table that person is relative to. String is quite arbitrary though, you would generally use an Integer, Long, or UUID to represent a primary key. Might need more context in the question.
The purpose behind an external ID is to link your entity with another representation of it from a system that is decoupled from yours.
For example, if you want the store the Facebook ID for SSO reasons, you would do it through a field that could be called externalId, or something like that. Another example might be that you imported some accounts from another database, and you want to store the Primary Key from the source entity that has been imported.
Otherwise, if that field does not represent anything in your business logic, get rid of it.
Is it possible to set custom generated String ID for a Entity?
i.e
jetbrains.exodus.entitystore.Entity
Say application generates a alerady unique key using Java UUID or some kind of Object ID can it be used as value of EntityId.toString()
EntityId is an internal id used for managing links under the hood. If it doesn't meet your requirements just use your own app-level id as a property assigned to an entity. You can easily find entities by property values (searching by property value).
I'm using Hibernate's Session to retrieve an object from a DB on a varchar primary key, and using session.get(Foo.class, someString); to obtain this object. My problem is someString can be "bob" or "BOB" and the object still gets retrieved (which is fine!), but the Foo.someString property is set to whichever capitalization was provided in the session.get argument, (let's say "BOB"), while the database property holds something different (let's say "Bob"). I actually need the retrieved object to hold whichever capitalization the database holds since I'm using that field for other purposes later on, but the input capitalization is provided by the user, so it could be anything. Instead Foo.someString ends up holding the user provided capitalization, which is not what I need, while all the rest of fields (that aren't ManyToMany collections) are retrieved for me correctly.
In other words, I need to use Hibernate to retrieve an entity Foo with the property Foo.someString set to "Bob", not to the user provided input, which could be "BOB", "boB", or any other capitalization variation.
Thanks.
Does it make sense to create a single entity when it should only contain the #Id value as a String?
#Entity
class CountryCode {
#Id
String letterCode; //GBR, FRA, etc
}
#Entity
class Payment {
CountryCode code;
// or directly without further table: String countryCode;
}
Or would you just use the letterCode as the stringvalue instead of creating the CountryCode entity?
It should later be possible for example to fetch all payments that contain a specific countrycode. This might be possible with both solutions. But which is the better one (why)?
Yes you can if you are using the entity as a lookup. In your example, you may want to add a column for description congaing (France, Great Britain, etc.) for the letter code and a third column whether it is active or not and maybe columns for when inserted and when it was last changed.
It makes sense to create such table to provide consistency of data, that is that no Payment is created with non-existing CountryCode. Having a separate entity (that is table) together with foreign key on Payment allows checking for consistency in database.
Another possible approach is to have check constraint on the code field but this is error prone if codes are added/deleted and/or there are more than one column of this type.
Adding the letterCode the the Payment Class as String Attribute (Or Enum to prevent typo errors) will increase the fetch performance as you do not need to create a join over your CountryCode Table.
Can you describe the pros and cons of including an OID (typically a database row identifier) in a POJO representing an entity in your model?
In fact I'm not talking about issues related to equals/hashcode and so on, I should have described better my problem (my bad :) )...
We've got some of those entity classes which represent business objects (like Product, Catalog and so on...). Sometime they have a 'business id', for example Product can be found by its unique ProductId (which has 3 fields : id, type, repository).
In our database, the Product table has a surrogate primary key column (OID) in addition to the 3 business columns ( id, type, repository) to facilitate foreign keys references and to have less joins clauses.
The Product/ProductId classes are part of the API that we expose to other applications. So for example they can call :
productManager.findProductById(ProductId productId);
The question is, should or should not the OID be included in the Product or in the ProductId class knowing that our clients are expected to use the ProductId identifier.
Pros :
I can use the OID to do another lookup like
Product p = productManager.findProductById(ProductId productId);
Catalog c = productManager.findAllCatalogsContainingProduct(p.getOid());
We're used to lookup a lot in the application by ProductId so this saves each time a roundtrip to the database to avoid to find the OID matching a ProductId.
Cons :
I've just exposed the OID to a client (let's hope he doesn't use it instead of the business key!!)
Can you list other pros and cons?
Database row identifier = Primary key? If so, there is no pro or con, you have to have it otherwise you can't relate the POJO back to its corresponding database row.
To retrieve Products and Catalogs, the standard SQL way is to do a Join. For example, with my DAL I can do:
SearchCriteria sc = new SearchCriteria();
sc.AddBinding("ProductId", productId);
List<Entity> Products = SQL.Read(sc, new Product(new Catalog());
or
List<Entity> Products = SQL.Read(sc, new Catalog(new Product());
This way there is no need to reveal anything to the caller, nor for a roundtrip.
You can run into problems if your implementation of equals() or hashCode() is based off the identifier since it will likely be null initially and then change later once the object is persisted. See below:
http://java.sun.com/javase/6/docs/api/java/util/Set.html
Note: Great care must be exercised if mutable objects are used as set elements. The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set. A special case of this prohibition is that it is not permissible for a set to contain itself as an element.
Let's assume that your implementation of hashCode() is based off the identifier and equals() uses hashCode() in its comparison. If you add the object to a Set and its identifer is null the equals comparisons will perform one way. If you then persist the object in the set, its identifier value will likely change, thus changing the behavior of equals() and hashCode(). This breaks the "contract" of Set as described above.
It's a bit of an edge case but one worth noting.