From one of the 'recommendations with Neo4j' tutorials I have downloaded and imported the product catalog data set. I'm creating a Spring Neo4j project using Object Graph Mapping.
For now I have created a #NodeEntity for both Product and Category. In order to quickly validate if everyting is ok used a #PostConstruct method for a ProductService and a CategoryService to get a product and a category from the db.
What I notice is that if I query the product, then get the product's category, and then all the products in the category the set does not contain all products, but only the product I started the query with.
However, if I query the category itself directly it does contain all products.
The graph model is as follows:
The subset of data I'm querying on is:
The Product entity defined as:
#NodeEntity
public class Product {
#Id
private String sku;
private String name;
#Relationship(type = "IN_CATEGORY")
private Category category;
#Convert(PriceConverter.class)
private BigDecimal price;
}
The Category entity is defined as:
#NodeEntity
public class Category {
#Id #GeneratedValue
private Long id;
private String name;
#Relationship(type = "PARENT_CATEGORY")
private Category parent;
#Relationship(type = "IN_CATEGORY", direction = Relationship.INCOMING)
private Set<Product> products = new HashSet<>();
}
For both I have created a Repository class.
If I query the CategoryRepository' withcategoryRepository.findByName("Desks")` and print the result this category has three products, as expected.
If I query the ProductRepository for the "Height Adjustable Standing Desk" and print it's category information it is Category "Desks", but it only containts a single product (the Height Adjustable Standing Desk) and not the other two products.
private void showInfo(final Category category) {
System.out.printf("Name:%s%n", category.getName());
System.out.printf("Parent: %s%n", category.getParent());
System.out.printf("Products:%s%n", category.getProducts());
}
I would have expected that the set would have been lazily evaluated into the full set of products. Do I need to force it to do so? When do additional nodes get loaded into a #NodeEntity, and how are you sure the complete subgraph for a certain node is loaded?
Edit:
The documentation contains the following quote:
For graph to object mapping, the automatic transitive loading of related entities depends on the depth of the horizon specified on the call to Session.load(). The default depth of 1 implies that related node or relationship entities will be loaded and have their properties set, but none of their related entities will be populated.
Which suggests that the session object should be used to load more data, but I don't know which session object.
Your analysis is correct. The default load depth in Spring Data Neo4j (and the underlying OGM) is 1. When you load the product, you will get its category, but not other products, as these are 2 hops in the graph away from the original product. If you want to fetch all the related products, I can think of 2 possible approaches.
Having obtained the product category from the product, query the category repository with its id. This will return the list of products with that category.
Set the query depth on the original product request to 2. The default Spring Data repository methods allow you to specify the query depth. This will then return everything related to that product up to 2 hops away from it in the graph.
There is only one way to load the "complete graph" for an entity, and that is to set the query depth to -1. If your graph model is not particularly dense, this may work for you. However, it might cause performance problems in other circumstances. Also, this technique is not compatible with loading only those entities that exist in your domain model. In other words, if the graph contains nodes and relationships you don't want, setting the query depth to -1 will blindly include all of these in the query, only to discard them again before returning those that do match your domain. Again, depending on the match between your domain model and the underlying graph, this may or may not be a problem.
Please refer to https://neo4j.com/docs/ogm-manual/current/migration/#_performance_and_unlimited_load_depth for more details
Related
I am implementing one to one (one Employee<-> one Mobile) relationship in hibernate as follows. This code works fine, but as this is one to one relationship, assigning same mobile number to emp1 and emp2 should have created problem (it violates relationship) but code is accepting and adding 2 emps with same mobile (Confirmed from Database tables). Why is hibernates one to one relationship like one mobile<->many employees?
My Code:
#Entity
public class Employee {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
#OneToOne
private Mobile mobile;
//...rest of the code
}
#Entity
public class Mobile {
#Id #GeneratedValue
private int id;
private long number;
//...rest of the code
}
Test Client main(...){
Mobile mobile = new Mobile(9999999999L);
Employee emp1 = new Employee("Raja");
Employee emp2 = new Employee("Raja");
emp1.setMobile(mobile);
emp2.setMobile(mobile);// VIOLATING 1-1 RELATIONSHIP
//...REST OF THE COMMON CODE
session.save(mobile);
session.save(emp1);
session.save(emp2);
session.getTransaction().commit();
}
DATABASE SHOWS BOTH EMP RECORDS WITH SAME MOBILE NUMBER (VIOLATION OF 1-1)
For one to one relations, you should always make sure that you have a unique constraint on your database (either generated by hibernate or manually created).
Hibernate won't check it because it would require to collect extra data every time to do the check. The database can do it more efficiently.
To do the check, hibernate would have to do an extra query. And if the database is configured correctly that extra query would cost time and resources without any gain.
If you don't have unique constraints and you define the relation bidirectional, you can get even more trouble.
Hibernate will save the conflicting records without complaining as you already discovered. And it would become impossible for hibernate to use the relation starting from the object on the other side (getting the Employee via the Mobile in your case). If mobile would be configured to get it's related employee eagerly, it would become impossible to get the mobile in memory after both employee's where saved.
I have a 3 layers model in my J2EE using EJB application: Cart, which has many LineItems, each having many Books (Book doesn't necessarily refers to a Line Item, it's not 2-directional).
Cart(1) <--> (M) LineItem (1) --> (M) Book
I wish to have it all eager loaded, i.e. when I extract the Cart it should also load all its Line Items and all of those Books with minimal number of SQL queries (I'm using a relational DB, e.g. MySQL). It can be done with 3 queries, one for each type of object. Setting "FetchType.EAGER" cause all objects to be loaded, however it has "2+n" calls: 1 query for the cart (obviously), another query for the Line Items, but then had to go on n queries for books, where n is the number of line items.
I used to work with Ruby on Rails, where using eager load (using includes) would do what I need. Can I do it also with J2EE?
(note: join might be an option, but I wish the entities to be populated automatically from the query, although I think the join is less comfortable).
Sample of my code:
#Entity
public class Cart implements Serializable {
#OneToMany(cascade=ALL, mappedBy="cart", fetch = FetchType.EAGER)
private List<LineItem> lineItems;
}
#Entity
public class LineItem implements Serializable {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="cart_id", referencedColumnName = "id")
private Cart cart;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="book_id", referencedColumnName = "id")
private Book book;
}
#Entity
public class Book implements Serializable {
...
}
Here is an example of the SQL queries where the Cart has 3 Line Items:
SELECT id, name FROM carts WHERE (id = 19)
SELECT id, quantity, book_id, cart_id FROM line_items WHERE (cart_id = 19)
SELECT id, description, name, price FROM books WHERE (id = 4)
SELECT id, description, name, price FROM books WHERE (id = 3)
SELECT id, description, name, price FROM books WHERE (id = 1)
Standard JPA provides join fetch, which marks a relation to be fetched eagerly, as if it was marked eager via annotation. In your case, it is only necessary to join fetch lineItems, as book will be eagerly loaded with each LineItem in single query.
With JPA 2.1, you may use Entity graph - you don't need to modify your query, you just attach a descriptor to your query that defines which relations should be eagerly fetched.
If you want to optimize to the smallest amount of queries possible, you might want to use batch fetching, which is available in some JPa providers. But beware, there is no standardized way to turn this on - I just linked to how to do it with EclipseLink.
I have two entities.
#Entity
public class Recipe {
#Id
private Long id;
private List<Step> steps;
}
#Entity
public class Step {
#Id
private Long id;
private String instruction;
}
And the following Clound Endpoint
#ApiMethod(
name = "insert",
path = "recipe",
httpMethod = ApiMethod.HttpMethod.POST)
public Recipe insert(Recipe recipe) {
ofy().save().entities(recipe.getSteps()).now(); //superfluous?
ofy().save().entity(recipe).now();
logger.info("Created Recipe with ID: " + recipe.getId());
return ofy().load().entity(recipe).now();
}
I'm wondering how do I skip the step where I have to save the emebedded entity first. The Id of neither entity is set. I want objectify to automatically create those. But if don't save the embedded entity I get an exception.
com.googlecode.objectify.SaveException: Error saving com.devmoon.meadule.backend.entities.Recipe#59e4ff19: You cannot create a Key for an object with a null #Id. Object was com.devmoon.meadule.backend.entities.Step#589a3afb
Since my object structure will get a lot more complex, I need to find a way to skip this manual step.
I presume you are trying to create real embedded objects, not separate objects stored in the datastore and linked. Your extra save() is actually saving separate entities. You don't want that.
You have two options:
Don't give your embedded object an id. Don't give it #Entity and don't give it an id field (or at least eliminate #Id). It's just a POJO. 90% of the time, this is what people want with embedded objects.
Allocate the id yourself with the allocator, typically in your (non-default) constructor.
Assuming you want a true embedded entity with a real key, #2 is probably what you should use. Keep in mind that this key is somewhat whimsical since you can't actually load it; only the container object can be looked up in the datastore.
I suggest going one step further and never use automatic id generation for any entities ever. Always use the allocator in the (non-default) constructor of your entities. This ensures that entities always have a valid, stable id. If you always allocate the id before a transaction start, it fixes duplicate entities that can be created when a transaction gets retried. Populating null ids is just a bad idea all around and really should not have been added to GAE.
The concept of the embedded is that the embedded content is persisted inside the main entity.
Is this the behaviour you are trying to configure?
The default behaviour of a Collection (List) of #Entity annoted class is to refer them instead of embed them. As you current configuration, the List<Step> variable does not have any annotation to override the default configuration, which is a different entity related to another one.
The error you are getting is because Objectify, when it saves the recipe entity, is trying to get the key of each step to create the relationship (and save them in the recipe entity), but if the entity step is not saved yet on the datastore, does not have a key
If you are trying to persist the steps inside the recipe entity, you need to setup objectify like this
#Entity
public class Recipe {
#Id
private Long id;
private List<Step> steps;
}
public class Step {
private Long id;
private String instruction;
}
As you can see, I removed the #Id annotation (an embedded Entity does not require an ID because is inside another entity) and the #Entity from the Step class. With this configuration, Objectify save the step entities inside the recipe entity
Source: https://code.google.com/p/objectify-appengine/wiki/Entities#Embedded_Object_Native_Representation
I have a class of users that includes a list of books objects. Once user's record is created all the books that he borrows will added to the list of books in his object. Once user removes his profile the record and history of the borrowed books need to be kept.
Many registered users may frequently borrow books and delete their profiles. To make the information retrieval faster (for example, retrieve a list of active users) I do not want to keep the record of deleted profiles in the same table that I keep records of active profiles.
Currently once user wants to delete his profile I put that object (userObj) in deactiveUserObj and remove the user's record from User table and try to save the deactiveUserObj to keep the record of deactivated record in that table but it throws the following exception.
My options: I know that I can create another table and keep the id of active users there or have an extra column of type boolean for user class and make it false to indicate the record is deleted. However I am not sure which of these three approaches is better or if there is another approach that I have not considered.
User class
#Entity
public class User{
private long id;
private List<Book> books;
...
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
public List<Book> getBooks() {
return books;
}
....
Book class
#Entity
public class Book{
private long id;
private string name;
...
}
Deleted Object class
#Entity
public class DeactiveUsers{
private long id;
private date deactiveSince;
private List<Book> books;
...
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
public List<Book> getBooks() {
return books;
}
....
}
MyCode
1) code to retrieve an object of user class goes here
2) code to copy object of user class into deletedObject goes here
3) 3.1 - session.delete(userObj);
3.2 - session.saveOrUpdate(deactiveUsersObj);
The code runs into following error
SEVERE: org.hibernate.ObjectDeletedException: deleted object would be re-saved by
cascade (remove deleted object from associations): [com.project.Book#1]
First: The reason for your error is you have cascade set to ALL on User.getBooks(), which will delete its books when you delete the User. Then, in the same session, you attempt to re-add the deleted objects when you add the DeactivatedUser back (which contains the same Books -- while you don't show your code for constructing a DeactivatedUser from a User, I presume you are making a shallow copy of Books, as you should). Change cascade to SAVE_UPDATE.
Second: I highly recommend keeping them all in the same table with a flag field that indicates if they are deleted or not.
Do not optimize prematurely. Unless you've actually benchmarked your code and found a bottleneck in database queries related to active vs. inactive users, there's no benefit to over-complicating your schema and business logic. Most SQL servers are very efficient, it is unlikely that you will find a bottleneck there for your application.
Doing it this way, an easy way to improve performance is to simply create an index on the deleted flag field.
With Hibernate you will be able to easily select active vs. deleted users via a Criteria.
Additionally, and most importantly, by not having a separate User and DeactiveUser object, you won't have to maintain the two objects in parallel, and you won't have to duplicate code that can operate on both types.
I think the best way to describe the problem is by using an example. I will keep it as simple as possible by removing unneeded details of the implementation.
Say there is a book store. The store keeps track of all books, customers, and orders using a backend database to store all the data, and a Java front-end to present them to the manager of the store.
The database contains the following relations:
Book ( id, title, author )
Customer ( id, name, tel, address )
Order ( id, date, custId, bookId )
On the other side, the Java interface uses JDBC driver to connect to the database and retrieve the data. The application consists of the following classes:
Book
BooksDataLoader
BooksTableModel
BooksView
Customer
CustomersDataLoader
CustomersTableModel
CustomersView
Order
OrdersDataLoader
OrdersTalbeModel
OrdersView
These classes use respective design guidelines and you can use the following source code as reference:
public class Book {
private String id;
private String title;
private String author;
/*
* Builder pattern is used so constructor should be hidden. Book objects
* are built in the BooksDataLoader SwingWorker thread.
*/
private Book() {}
}
public class BooksDataLoader extends SwingWorker<List<Book>, Book> {
private final BooksTableModel booksModel;
private final List<Book> books = new ArrayList<Book>();
}
public class BooksTableModel extend AbstractTableModel {
private final String columnNames = { "Book ID", "Book Title", "Book Author" };
private final List<Book> books = new ArrayList<Book>();
}
public class BooksView extends JPanel {
private final JTable booksTable;
private final BooksTableModel booksModel;
}
I am using the Builder pattern to implement the classes Book, Customer, and Order. The instances of these classes are built using data retrieved by the database inside a SwingWorker thread and are published to the view using an AbstractTableModel. So, actually the application consists of the following views (JPanels): BooksView, CustomersView, and OrdersView, each of which contains a single JTable with columns as shown below:
BooksView.booksTable: Book ID | Book Title | Book Author
CustomersView.customersTable: Customer ID | Customer Name
OrdersView.ordersTable: Order ID | Date | Customer Name | Book Title | Book Author
The problem appears when we try to resolve an instance variable which represents a foreign key in the database, to the data it links. For example, the OrdersTableModel has a List structure of all Order objects found in the database, however the columns 3, 4, and 5 of the of the OrdersView table cannot be directly accessed from an Order object since it only contains ids for the book and the customer, and not the actual data. One solution I tried was to create a static HashMap inside each of the Book, Customer, and Order classes in order to keep track of all retrieved objects but it leads to data duplication since we already have a List structure of the retrieved objects in the table model of each view.
I am looking for an efficient and extensible (object-oriented) design solution/recommendation.
Thank you in advance.
You should definitely use ORM like Hibernate or EclipseLink or whatever technology fits you. Currently JPA2 is the common standard implemented by every such tool. You define the mapping between your object and db model by using annotations or xml files.
These tools also offer ways to generate your database schema according to your object model (even the other way is possible if you have legacy schemes).
I recommend you not to make use of jpa criteria api since its design is quite flawed. There are a number of frameworks out there that help you build your queries. QueryDSL is one that seems really nice to me. I used the specification pattern (which I actually implemented using criteria api under the hood) for abstracting query construction. See http://martinfowler.com/apsupp/spec.pdf and http://adrianhummel.wordpress.com/2010/07/02/composed-specifications-using-jpa-2-0/ for first references.
And do some search on DAO pattern and repositories (a term coming from domain driven design).
Well this is a typical issue when mapping OO design on relational database tables.
Let's take as example your OrdersTableModel:
Order ID | Date | Customer Name | Book Title | Book Author
the last three columns are database foreign key ids instead of the values you want to show.
To manage this correctly you have 2 possible solution:
FIRST:
desgin the Order class like this
public class Order{
private String id;
private Date date;
private Customer customer;
private Book book;
private Author author;
get and set methods
}
Note that customer is of type CUSTOMER, book of type BOOK...
Now suppose you query the db to retreive a list of orders.
From the rows returned you have to build a list of Objects Order. Of course the db return foreign key ids for customer, book and author so:
Query table orders of db
For each row build and Orders object, fill id and date with the values of the rows
Take the foreign key id for customer. Build a new query on customer db table and take the right customer based on the id. Build a new Customer id filling its values whit the results of this second query. Assign the Customer object to the field customer of the object Orders
Same for Book and author
Add the object Order to a list
Now you have a list of suppose 10 orders
Iterate on it and fill the order table you display.
To display for example Customer field you will have
listOrders[i].getCustomer().getName(). // listOrders[i] is an Order object. getCusotmer returns a customer object. getName is a Customer's method that return the String name.
Same for book and Author
SECOND APPROACH
design Order Class like this:
public class Order{
private String id;
private Date date;
private int customer;
private int book;
private int author;
get and set methods
}
Note now the customer etc are INT fields. The hold the int reference key retreived from db
Again you query table orders.
For each row build an Order object. Fill the customer etc. values simply with the id of the db.
Now you want to display a list of orders.
Iterate on the list.
when displayng customer use
listOrders[i].getCustomer().getName().
NOTE as customer field is an int reference key the geCustomer should
Execute e query on db customer table to retreive the correct customer based on the id
Build a Customer object filling its fields
REturn the object
So the differences beetwen the two appracches:
The first build a complete Order object that contains also Customer object etc. When need to display something, you have all what you need.
Second approach build a light Order object. When need to display for example the data of the customer whe need to query the db (this is called lazy loading)
I suggest you to consider using an ORM wich really helps you mapping OO design on DB and helps you build queries that return directly objects instead of "ids"