In my app I have different Users and Items, so each user can pick many items.
In the tutorial I have learned about #ManyToMany annotation.
#Entity
public class Item extends Model {
...
#ManyToMany(cascade = CascadeType.REMOVE)
public List<User> users = new ArrayList<User>();
But second option I can think of is to define a separate class for User-to-Item relation so I can add additional information like date and time.
#Entity
public class ItemUserRel extends Model {
#Id
public Long id;
public User user;
public Item item;
//additional information
public Date date;
...
Which of both options is better design and why?
I faced a similar issue a while ago. I also had to deal with a model User and the model Group. My requirements were:
A user can have n readable and n writable Groups. These permissions must be stored in a third table (not in User and not in Group table). But also additional properties like authorisedBy and 'authorisedOn'. So #ManyToMany did not worked for, because I had no real control of it. Also the additional properties makes it hard to map via JPA.
Perhaps other designs are possible but I (still) think that introducing a new class UserGroup would be best. This class has #ManyToOne relation to a single User.
I end up defining these three models:
User
Group - General information about the group model
UserGroup - Containing additional fields like: permissions, authorisedBy, authorisedOn etc.
On my User model, I would have getter getUserGroups() but also getPersonalGroup() which is basically one (personal) instance of Group in getUserGroups() but where the createdBy and authorisedBy is the same user.
I found this design much more maintainable by me and more clear. Also this design helped me to create a comfortable user interface, where the administrator can manage and change permissions for UserGroups.
Perhaps more useful information
Mapping many-to-many association table with extra column(s)
How Do I Create Many to Many Hibernate Mapping for Additional Property from the Join Table?
Related
I've always thought what could've been the best way to deal with this problem in Spring/Hibernate. Imagine we have an Entity like House:
public class House {
Long id;
String name;
String address;
List<Person> people;
}
Where Person is another Entity. Now, if we want to show a list of every house that is stored, we just want some details, id, name and address, we don't want the entire data to be fetched because we are not showing the people in the list.
When we enter a detail view, however, we are interested in showing the people as well.
What's the best way to fetch the people just when you want them, taking care of the LazyInitializationException as well?
There are a variety of options. I just highlight the one I use:
JOIN FETCH
If you need the people eager loaded you can create a query like:
select h from House h fetch join people p
If you think this will produce to many queries go for entity graphs.
ENTITY GRAPH
Definition:
#Entity
#NamedEntityGraph(name = "graph.House.people",
attributeNodes = #NamedAttributeNode("people"))
public class House {
Usage with Spring Data JPA:
#EntityGraph(value = "graph.House.people", type = EntityGraphType.LOAD)
List<House> findById(Long id);
But there are more. Please read: https://thoughts-on-java.org/5-ways-to-initialize-lazy-relations-and-when-to-use-them/
You can use the Entity graph in the repository. Just use #EntityGraph to define an ad-hoc entity graph on your method.
#EntityGraph(attributePaths = {"people"})
List<House> findAll();
Here, you can define attributes which you want to fetch as attributePaths.
Call this when you want to show people otherwise call findAll().
And you can use lazy fetching for people also, whenever you want people just call the getter of it then JPA will automatically fetch people in another query.
I have been designing spring rest apis using standard MVC architecture like domain layer as POJOs and repositories to fetch domain data from db tables. So far these entities were isolated so the design acted as separate RestController, Service, and Repository flow for each entity.
I have been looking to understand the best practice when it comes to association in domain objects i.e., ORM. For example, lets take following pseudocode to illustrate domain classes (only for the purpose to express the design in question. I have not provided complete classes):
public class Customer {
#Column
private int id;
#Column;
private String name;
#OneToMany
private List<Order> orders;
//...getters setters
}
public class Order {
#Column
private int id;
#Column;
private String orderNumber;
#OneToMany
private List<Product> products;
#ManyToOne
private Customer customer;
//...getters setters
}
public class Product {
#Column
private int id;
#Column;
private String productName;
#ManyToOne
private Order order;
//...getters setters
}
The dilemma I have from designing perspective. I have following approaches that very well all be incorrect:
Define one RestController for customer and provide all the api resources like /customers, /customers/id/orders, /customers/id/orders/id/products etc. Have one Service that takes care of working with these domains. Have separate JPARepository for EACH domain. The "keep it simple" thing here is that I have separete repository for each domain so I just have to provide query methods in corresponding Repository class in order to find details for a specific domain i.e., fetch orders for a given customer Id. However, that makes me think killing the purpose of using ORM model because I am fetching individual domains through their Repository classes. This option will make all 3 repository classes wired in the service class and that also I think is not a good design. 3 might looks okay here but I have 6 to 7 domains in the ORM graph in my actual requirements so that would mean autowiring 6 repositoris in one service class.
One RestController and one Service class as in above option but the Repository class is single too. The Repository is created only for Customer domain. In this way I retrieve Customers with other domaims lazy loaded. This is to fulfil a GET request of "/customers". To fulfil a GET request of "/customers/id/orders" I will again use Customer Repository, retrieve customer for the given Id and then return list of Orders. Further, for a GET request of "/customers/id/orders/id/products" , I will require writing a manual data fetching mechanism in Customer domain so that it takes care of retrieving list of products for a given customerId and orderId. This way I use one Repository, satisfying the purpose of using ORM but then adding manual fetching data methods in Customer domain. Another negative I see is that I need to get complete list of orders in a customer domain even if I have customerId and orderId available. I would have fetched one single order based on customerId and orderId has I used a separate repository for Order.
Both are incorrect and there exists a better approach.
I have looked through spring docs for repository and hibernate docs for ORM. I went through multiple tutorials for one-to-many mappings with spring data rest but I found mixed approaches in different tutorials.
This question will look duplicate to you as I have read multiple posts on stackoverflow regarding this design concern but none of the answers give me a reasoning for the trade offs and options I mentioned above. Hence, I am reposting this question.
It is a mixed approach. e.g. in your case the product entity need not have a #ManyToOne relation with the Order. Imagine if your product is part of 1 million orders! How many time will you query a product to find orders? You will query findOrdersByProduct(Product) rather than findProductByOrder(Order)
think w.r.t your usecase. Sometimes it makes sense to have one directional mapping if you will never fetch the information other than from the owner of the relationship
Think about the amount of data that you will fetch (including the joins) if you query an entity.
e.g if i am fetching an organization do i need to fetch all its employees? your system will go for a toss (lazy loading will save you most of the time but if you have an Angular then it will bind and fetch the entire model). But it does make sense to have many to one relationship with an org from the employee entity.
I have following three entity classes.
#Entity
public class User {
#Id
#Column(nullable = false)
#GeneratedValue(strategy= GenerationType.AUTO)
private Integer id;
}
#Entity
public class LanguageProficiencyLevel {
#Id
#Column(nullable = false)
#GeneratedValue(strategy= GenerationType.AUTO)
private Integer id;
private String name; // A1, A2, B1 ... etc
}
#Entity
public class Language {
#Id
#Column(nullable = false)
#GeneratedValue(strategy= GenerationType.AUTO)
private Integer id;
private String name; //English, Chinese ect ...
}
Currently in the database, I have around 20 languages saved in Language table and 6 language proficiency levels A1, A2, B1, B2, C1, C2 saved in LanguageProficiencyLevel table.
Now I have the following relationship among the entity classes.
A User can know more than one languages with one proficiency level and A language with one proficiency level is known by many users.
So for example, A user may know English and his English proficiency may be C1, Again same user may also know Spanish and his Spanish proficiency may be B1.
Here I understand, User and Language has many to many relation. But I don't understand how to relate LanguageProficiencyLevel with User or Language.
Also how should I save this in database? My idea is to make one join table (LanguageSkill) with column names as user_id, language_id and languageProficiencyLevel_id and this table row will be inserted when a user is created. I am not sure if this the way to implement it. Please give me an idea how to do this and what should be the configuration for this.
User and Language will have many to many relation as you said. And, Language and LanguageProficiencyLevel will have one to one relation.
So, you have to create a mapping table that will have have many to one relation to all the 3 tables.
Refer this link to create mapping table with multiple columns.
Your relations between entity objects would be like:
A user can have multiple languages and language can have multiple users. So its a Many-to-Many relationship between User-Language.
A language can have only one proficiency at a time but a proficiency can have multiple languages. So LanguageProficiency to Language would be a one-to-many relation.
Relation between user and language proficiency is also many-to-many.
Here is a link how you can go about your database design for many-to-many relations.
How to implement a many-to-many relationship in PostgreSQL?
After creating the database design you can probably use some reverse engineering tool(https://www.javacodegeeks.com/2013/10/step-by-step-auto-code-generation-for-pojo-domain-java-classes-and-hbm-using-eclipse-hibernate-plugin.html) to create the hibernate pojo classes. I would recommend to use a tool to do this rather than taking things in hands to avoid unnecessary issues.
So now if you look carefully... your Entity classes of User, Language and LanguageProficiency would be something like this.
Hope this is useful.
You should absolutely create another table LanguageSkill, like you said.
Language and Proficiency are so-called base data - they will have comparatively few entries and will be independent of users. Neither of them should be mapped into User.
A User should then have a #OneToMany relation to LanguageSkill, which represents his knowledge of a particular language. LanguageSkill has a #ManyToOne to both Language and Proficiency (and User).
Skipping LanguageSkill would result in data duplication in your schema, or at least in a schema that is hard to read with all the jointables.
Also, it would mix concerns - data that is relatively stable (Language, Proficiency) and data that will change often (a person's knowledge of a language).
Being new to Google Cloud Datastore I would like to make sure that I am on a right track.
What I need:
many-to-many relationship
the relationship itself has to hold data describing the relation
strong consistency is required both ways:
from user entity to all the data entities that this user has permissions to
from data entity to all the users that have permission to it
This is what I came up with:
#Entity
public class User {
#Id String userId;
}
#Entity
public class PermissionIndex {
#Id long id;
#Parent Key<User> user;
List<Ref<Permission>> permissions;
}
#Entity
public class Permission {
#Id long id;
boolean writePermission;
boolean managePermission;
#Load Ref<Data> data; //So that Data entities can be retrieved with strong
//consistency after looking up Permission entities
//for a specific User entity
#Load Ref<User> user; //So that User entities can be retrieved with strong
//consistency after looking up Permission entities
//for a specific Data entity
}
#Entity
public class DataIndex {
#Id long id;
#Parent Key<Data> data;
List<Ref<Permission>> permissions;
}
#Enti.
public class Data {
#Id String dataId;
#Id String actualData;
}
If I understand right with this implementation the only way to filter Data Entities of a specific user is to get all Permission entities and filter Data entities in memory, am I right?
Is there a better way to implement it still fulfilling the requirements?
UPDATE:
In my understanding this implementation will allow me to implement logic that will retrieve data assuring strong consistency (having user id - ancestor query to retrieve all Permission entities and then using get_by_key to retrieve the Data entities).
I am wondering if I should approach it in a different way - since I do not have a lot of expierience with datastore/objectify.
There's an important conceptual misunderstanding inherent to the question: Relationships are not strongly or eventually consistent. Queries are.
If you perform a get-by-key operation, the result will be strongly consistent. If you perform a non-ancestor filter query, the result will eventually consistent. Rephrasing this:
If you navigate your object graph using get-by-key operations, you will see strong consistency. If you navigate your object graph using non-ancestor query filters, you will see eventual consistency.
If you need strong consistency, structure your data so that your queries can be satisfied with get-by-key operations or with ancestor queries.
Your logic seems sound... but for a relational database.
This kind of logic doesn't hold true in the HRD that is the datastore. There are obviously ways to go around this, and you have figured them out with the way you described.
For consistency, your only chance is to use ancestor queries. The datastore is eventually consistent, only with "get_by_key" or with an ancestor query can you "force" consistency.
If you want something closer to SQL, maybe consider cloud GQL?
I have a Product entity and table and would like the database design to allow finding a product by different keywords on top of its name, that is, like using a thesaurus e.g. product name "HDR-TD20V" should also be found by keywords "camcorder", "camera", "video camera", etc. Note that this same mechanics can be used to locate the same record from different input languages e.g. looking for "camara de video" (Spanish) or "videokamera" (German) should also find the same record.
Assuming that I am using Hibernate-search i.e. Lucene I have the following two design choices:
De-normalized approach: Product table has a keywords column that contain comma separated keywords for that product. This clearly violates the First Normal Form "... the value of each attribute contains only a single value from that domain.". However, this would integrate nicely with Hibernate-search.
Normalized approach: Define a Keyword entity table i.e. Keyword(id,keyword,languageId) and the many-to-many association ProductKeyword(productId,keywordId) but the integration with Hibernate-Search is not so intuitive anymore ... unless e.g. I create a materialized view i.e. select * from Product p, Keyword k, ProductKeyword pk where p.id=pk.productId and k.id=pk.keywordId and index this materialized view.
I would of course prefer the choice 2 but I am not sure how Hibernate-search would optimally cover this use-case.
Something like this should work:
#Indexed
public class Product {
#Id
private long id;
#ManyToMany
#IndexedEmbedded
Set<Keyword> keywords;
// ...
}
public class Keyword {
#Id
private long id;
// only needed if you want a bidirectional relation
#ManyToMany
#ContainedIn
Set<Product> products;
// ...
}
I am leaving out options for lazy loading etc. How exactly the JPA mapping looks like depends on the user case