I have a question that,
Let's consider we have a app in which we are adding many categories (ex: shopping,study,public, etc..) at runtime , there is no limit of adding categories since we adding it at runtime while we use the app and storing in a RoomDatabase having one Entity class and Dao.
-> now my question arises here , In that categories(ex: shopping,study,public, etc..) there are lot of different items we will store , so how can we use Roomdatabase to store all these categories item .
so that when we will open the shopping category so it will show only shopping list and so on for other categories
Is it possible to save all data of these categories? ,since we are adding many categories and there sub data then how it is possible to make that much of DAO and ENTITY CLASS to save that much of sub data.
-> If we will use SHARED PREFENCES . then we can store all categories sub data into a unique keys(unique key can be a categories name). so if we add more and more categories then it will store data to categories unique name.
but how can we do in roomDatabase. Is it possible? if yes then how....?
i hope, i explained my question well
Dao Interface
#Dao
interface Dao {
#Insert
fun insert(studentName: StudentsEntity): Long
#Insert
fun stdDetailsInsert(stdDetails: StudentDetailsEntity)
#Transaction
#Query("Select * from Student")
fun getAllStudent(): MutableList<WholeDataStudent>
#Query("Select * from Student")
fun getStudentName(): MutableList<StudentsEntity>
}
Entity Class
#Entity(tableName = "Student")
data class StudentsEntity(
val stdName: String
) {
#PrimaryKey(autoGenerate = true)
var stdId: Int = 0
}
#Entity
data class StudentDetailsEntity(
val detailsId: Int,
val stdAddress: String,
val phoneNo: String
) {
#PrimaryKey(autoGenerate = true)
var id: Int = 0
}
data class WholeDataStudent(
#Embedded val std: StudentsEntity,
#Relation(
parentColumn = "stdId",
entityColumn = "detailsId",
entity = StudentDetailsEntity::class
)
val stdDetails: List<StudentDetailsEntity>
)
For your items in your different categories, can these items use the same Item Entity class template?
I can think of solutions for both scenarios.
Without further information, let me give you an idea for supporting adding categories (and corresponding items) dynamically and a common item class for all items.
Room (based on SQL) offers several ways of to define data relationships.
As far as I understand your problem:
one category can have many items
but each item belongs only to one category
If that holds true, then you have a one-to-many relationship in your data.
What Akash Bisht was pointing at: you should have another entity for your categories.
There, at minimum you need to store information such as the ID (primary key) and the name (string) of that category.
Then you can create categories dynamically and add them to this table. This can be as many categories as you want.
Then when creating your items inside this category, you just need to add the ID reference of the corresponding category in the Item entity, e.g. as a variable like categoryId
On the other hand you could also just store the category name directly in the Item entity. But this has the disadvantage that when you want to change, for example, your categories name, then you have to update all your items.
To then just get the items for one specific category, as you stated as requirements, you have at least 2 options:
write a SQL query that only selects items of a specific category
or you query a list of all your items and then filter them by a category
If your data for different categories is of types that can be stored in shared preferences (as you have mentioned). You also can handle that with this approach.
Related
I am using spring data jpa
I have two tables person, order
Both have relationship like one to many from person to order
order(person_id) -> person(id)
create table person (id int primary key, name varchar(20));
create table order (id int primary key, name varchar(20), person_id foreign key references person(id));
Consider i have schema in place for both tables and there is no data yet.
I need to insert this data -
Person {name: "person1"}
Order {name: "order1", person_id: <corresponding to person1 record>}
Does inserting the data related by foreign key needs two calls to db?
saving the person
take the primary key from the saved person entity (step 1) then save Order?
Person person = new Person("person1");
Person person = personRepository.save(person);
Order order = new Order("order1");
order.setPersonId(person.getId());
orderRepository.save(order);
Or is there any alternative to save data to two tables using single call to db?
Retrieving the data
If i need to retrieve the person along with orders, will spring data jpa give the result in single db call or need to extract data from two tables separately?
Person person = personRepository.findByName("person1").get(); \\for eg: consider name is unique here
List<Order> orders = orderRepository.findByPersonId(person.getId());
or any alternative in single db call?
Giving clarity to these questions is really appreciated.
Thanks for the answers in advance.
Have a look at cascade types. By adding a cascade type, you could save both objects with just 1 repository call.
#Entity
public class Order {
// other fields...
#OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
private List<Order> orders;
}
#Entity
public class Person {
// other fields...
#ManyToOne
private Person person;
}
Order order = new Order();
Person person = new Person();
person.setOrders(List.of(order));
order.setPerson(person);
personRepository.save(person); // <--- since save action on person cascades, it will also save the order.
Make sure that the objects are linked to each other before saving (the 2 rows above the repository call in the example above)
Regarding fetching data
If you call e.g. the personRepository and it has orders linked to it, you can either access them by configuring eager fetch (not recommended) or by wrapping your method in a Transactional annotation and access the orders programmatically instead.
#Transactional
public void doSomething() {
Person person = personRepository.findById(1);
List<Order> orders = person.getOrders();
}
Note that from a db perspective, in both scenarios with saving and fetching data, the same number of queries will be executed as when calling with separate repositories, but you reduce the code needed to do so and its a bit easier to work inside a transaction and only focus on the java object instead of having to call multiple repositories, especially as your db data model grows.
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
As part of my program, I'm using relational tables which hold information such as - user role, job category etc. Each table may have slightly differing fields - for example:
User Role Table has the following fields:
id (auto-generated)
role (eg Planner, Admin etc)
role_description (description of above role)
enabled (toggle this role on/off)
Job Category Table:
id (auto-generated)
category (eg Service, Maintenance etc)
category_description (description of above)
category_group (categories are grouped into management areas)
...
enabled (toggle category on/off)
The lists can be changed by the end user so I need to provide an admin section to enable new roles/categories to be added.
I had thought of creating a routine where I pass the entity class of the role/category etc and have it generate an array which can be used to populate the admin section but have only been able to do this for the 1st two columns - eg id/role or id/category.
With the fields differing between each entity, is there a way that I can do this? Or will I have to create a method in each of the entities - such as getRoleList and getCategoryList etc?
Thanks.
After a bit of experimenting, I've decided to implement this in the following way.
I've added methods to my database helper class that will read the list and populate an array. I'll have to create a separate method for each entity but I've decided this would be necessary due to the differences between the classes.
I'm not 100% sure that this is the most efficient way of accomplishing this but it does what I need (for now).
One of the methods:
public static UserRole[] getUserRoleList(String order, Boolean reverseOrder) throws SQLException {
Session session = openSession();
List<UserRole> list;
if (!reverseOrder) {
// obtain list and sort by provided field in ascending order
list = session.createCriteria(UserRole.class).addOrder(Order.asc(order)).list();
} else {
// sort descending
list = session.createCriteria(UserRole.class).addOrder(Order.desc(order)).list();
}
// return UserRole[]
return list.toArray((UserRole[]) Array.newInstance(UserRole.class, list.size()));
}
The rest of the methods will be pretty much identical (substituting the entity/class names). The only difference would be adding another argument for some entities (enabled Boolean, so I can return only items in the list which are enabled).
Edit:
Since posting the above, I changed my mind and moved to a generic method to obtain lists, passing in the entity class as below:
public static List getList(Class entity, String order, Boolean reverseOrder, Boolean enabled) {
// stripped for brevity...
list = session.createCriteria(entity)
.add(Restrictions.eq("enabled", true))
.addOrder(Order.asc(order)).list();
// stripped more...
return list;
}
Casting when calling the method:
List<User> userList = DatabaseHelper.getList(User.class);
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"
If I have 3 tables, with the expected normal columns : Customer, CustomerProductLinker and Product.
And I want in my Java code to do this :
Customer customer = myService.getCustomer(id); //calls hibernate session
List<Product> customerProducts = customer.getProducts();
What would my 3 entities look like and the respective collections within each, specifically the getProducts() method ? Or is it better to use HQL and a named query for this ?
I am creating the databse tables from the java code (using the create option in hibernate conf), so the table desgin can be altered if preferred.
Try #ManyToMany relationship using #JoinTable. A customer has a set (or a list) of products. A product has a set (or a list) of customers.
#Entity
public class Customer {
#ManyToMany(cascade= CascadeType.ALL)
#JoinTable(name="customer_product",
joinColumns={#JoinColumn(name="customer_id")},
inverseJoinColumns={#JoinColumn(name="product_id")})
private Set<Product> products = new HashSet<Product>();
...
#Entity
public class Product {
#ManyToMany
#JoinTable(name="customer_product",
joinColumns={#JoinColumn(name="product_id")},
inverseJoinColumns={#JoinColumn(name="customer_id")})
private Set<Customer> customers = new HashSet<Customer>();
...
I would set up the entities like wannik suggested. Try to keep it simple. If you start using named queries you are doing more work and you are just covering an specific case.