Closed cmitchell closed 1 year ago
The convention function is here because you have one call for all types. You may want to say that for Orders, this is OrderId and for users it is EmployeeId, etc. Using code in this manner is the easiest way to handle all scenarios.
The requirement for identifier fields is that you'll have a string property, RavenDB explicitly do not support non string identifiers. You can provide converters to & from your id type, but RavenDB will not do that directly. See this post for the reasoning: https://ayende.com/blog/188257-B/avoiding-non-string-identifiers-in-ravendb
@ayende Yes, I understood the reasoning behind needing a String.
My point is that you could provide a more developer friendly way to specify the id field, even on multiple objects.
I.e.
Order {
@RavenId
private String id;
private String address;
etc
}
and then a different Id for Employee
Employee {
@RavenId
private String employeeId;
private String ssn;
etc
}
Internally, you could translate to the field annotated with @RavenID to a Function something like property -> property.getAnnotations().contains("RavenDB" and that would be more inline with what other modern java databases clients do. (And not a lot of work to implement)
We don't want to create ties from your entities to RavenDB. The ideal is pure POJO.
Note that you can achieve the same easily enough using something like:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface RavenId {
}
documentStore.getConventions() .setFindIdentityProperty(
property -> property.isAnnotationPresent(RavenId.class));
The whole point is that you have the full flexibility at your hands, and the complexity is managable.
Hi,
I think I've found a few bugs regarding setting the Identifer field.
I can specify a function that returns a non-string field to be used as the Identifier when I use
findIdentifierProperty
. There are no checks on the function that I provide. In the code below, I tried to say I wanted to use a UUID field type as the identifier (something that is somewhat documented for C#, but not for java -- will submit a different issue ticket for this)Code to reproduce (Using Junit 5 and Spring)
Test Code
Output
I think the heart of the issue is that you're asking the user to know your business logic. They have to know that you're using a Function to inspect property fields (why not have them tell you the field name and you do the function), and at the time that they have to know that the Function must return a String field. I propose the following to address the above situation:
findIdentityProperty
. Requiring the user to pass in a function exposes a lot of your business logic to the user and honestly isn't very intuitive.Replace it with one or both of the following (both hide that you're using Functions while letting the user know that only String fields are appropriate):
(Preferred as it is idiomatic to what other databases do) Implement an
@RavenId
annotation that the user would simply annotate the desired field with. That makes it clear to the user when they look at their model class which field is being used as the Id. Whenstore()
is called, check that one and only one String field is annotated and return an error if the annotated field isn't a String, if there's no annotation AND no field named "Id", or if more than one field is annotated with @Id (unless you support compound ids). Behind the scenes, set the property name of the annotated field as the argument to the function. I'm not saying RavenDB should always do what other databases do, but there are some conventions for some common tasks, like identifier specification are petty much expected. Examples from other databaseshttps://mongodb.github.io/mongo-java-driver/3.7/javadoc/?org/bson/codecs/pojo/annotations/BsonId.html https://docs.spring.io/spring-data/neo4j/docs/current/api/org/springframework/data/neo4j/core/schema/Id.html
(less clear to the user when they are looking at thier own model) Make a
setIdentifierField(String fieldName)
method and have the user pass in the name of their identifier field. Whenstore()
is called, check that the object has a field with the specified name and that it's a string. Behind the scenes, set the property name of the annotated field as the argument to the function.Both suggestions wouldn't require major changes to the underlying code -- you would just make the Function for the user. In both suggestions, in the
store()
method