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"
Related
I have a scheduler which polls data in every 4 hours and inserts into table based upon certain logic.
I have also used #Transactional annotation and also I am checking every time whether data already exists in the table or not.
If the record does not exist, it will insert. When I am multiple instances of SpringBoot application, each instance runs the scheduler and some data not all get duplicated.
It means I found that table contains duplicate record. The table where I am inserting is an existing table of the application and few columns have not been defined with unique
constraints. Please suggest me how I can maintain unique records in the database table even if scheduler runs from multiple instances. I am using Postgresql and SpringBoot.
So, the direct answer to you question is to have unique identifier for each record in your table. Unique id from external API will be a perfect match. If you don't have one - you can calculate it manually.
Consider an example:
#Entity #Table
public class Person {
private String fieldOne;
private String fieldTwo;
#Column(unique=true)
private String uniqueId;
//in case you have to generate uniqueId manually
public static Person fromExternalApi(String fieldOne, String fieldTwo) {
Person person = new Person();
person.fieldOne = fieldOne;
person.fieldTwo = fieldTwo;
person.uniqueId = fieldOne + fieldTwo;
}
}
Then you will have unique index on DB side based on uniqueId field, and DB prevent you from duplicates.
Important - you can't use
#GeneratedValue(strategy = GenerationType.IDENTITY)
because it will generate new id each time you save object to DB. In you case you can save same object multiple times from different instances.
But in you case I would suggest another solution. The idea is to run scheduled task only once. And this is already answered here
In the basic CRUD operations I have for a particular resource developed using spring boot, I have a new requirement to add/remove custom fields for a resource based on customer requirement (like you have an option to add or delete custom fields in the JIRA application).
GIVEN:
Considering, I have a resource (let's say Accounts):
It few columns (which I exactly showed in the JSON response)
#Entity
#Table(name = "account")
public class Account {
#Id
#GeneratedValue(generator="seqGenerator", strategy = GenerationType.IDENTITY)
#SequenceGenerator(name="seqGenerator", sequenceName="ACCOUNT_SEQ", allocationSize=1)
#Column(name="ID")
private Integer id;
#Column(name="ACCOUNT_OWNER")
private String accountOwner;
#Column(name="RATING")
....
...
....
Exactly similar is the Media file, which is used at the controller level:
public class AccountMedia {
private Integer id;
private String accountOwner;
....
...
....
And the basic CRUD operations are performed with create a resource being media to entity conversion and vice versa.
PROBLEM STATEMENT:
I have a multitenant application (which I handle at the database schema level). Now one of the tenants has come up with a requirement where they want the functionality to add/remove custom fields (like, JIRA has the functionality to add custom fields on the fly).
TRIED SOLUTION:
I created one more table, Accounts_Custom_Data, with columns being:
Accounts_Custom_Data:
ID| Column_name| Column_value
Now lets say tenant wants three extra columns for 1 row of data in Accounts, my data distribution looks like:
Accounts:
ROW1
Accounts_Custom_Data:
ROW1_ID1 | {column_name_1}| {column_name_1_value}
ROW1_ID1 | {column_name_2}| {column_name_2_value}
ROW1_ID1 | {column_name_3}| {column_name_3_value}
And to map it with the controller's response:
I create, AccountsMedia (actual object) and an array named customColumns which has a structure similar to an array of Accounts_Custom_Data. That means, an array inside the actual JSON response.
Wherein, this is working fine for me right now but somehow I am not 100% confident that this approach.
I appreciate the help if someone can point me to the correct approach to tackle such a situation.
Thanks in advance
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.
I have just started reading about ORMLite since I am interested in using it in an Android application.
I would like to have a feeling of how object relations are/should be persisted with this framework.
For example, if I have these classes:
#DatabaseTable(tableName = "bill_items")
class BillItem{...}
#DatabaseTable(tableName = "bills")
class Bill {
#DatabaseField String x;
List<BillItem> billItems = new ArrayList<BillItem>;
public void setItems(List<BillItem> billItems) {
this.billItems = billItems;
}
}
As far as I understand the recommended way to update a Bill object would be with something similar to:
Dao<Bill, String> billDao = DaoManager.createDao(connectionSource, Bill.class);
Bill bill = billDao.queryForId(...);
List<BillItem> billItems = ...; //some new list of items
bill.setX("some new value for X");
bill.setItems(billItems);
//saving explicitly the billItems before saving the bill. Is this correct ?
Dao<BillItem, String> billItemDao = DaoManager.createDao(connectionSource, BillItem.class);
for(BillItem billItem : billItems)
billItemDao.update(billItem);
billDao.update(bill);
Is this the recommended way to update an object which relations have changed ? (specifically relations with a set of persistent objects, as in the code above).
Somehow I had the impression that it should be a better way to do this.
Also, if I want to use this framework, am I suppose to put in my model classes both domain attributes and persistency related attributes (e.g., foreign and primary keys ?). Wondering if there is a way to avoid this mixing of concerns.
Thanks for any help.
Your code is basically correct although there are some things you can do to improve it.
You should use the #ForeignCollectionField annotation to mark the billItems field. This will load the collection when you query for a Bill. See the docs on foreign collections.
#ForeignCollectionField
ForeignCollection<BillItem> billItems;
Instead of doing the updates by hand each time, you can create your own Dao class for Bill that overrides the update update() method and updates the inner objects on its own.
I have two classes, Person and Company, derived from another class Contact. They are represented as polymorphically in two tables (Person and Company). The simplified classes look like this:
public abstract class Contact {
Integer id;
public abstract String getDisplayName();
}
public class Person extends Contact {
String firstName;
String lastName;
public String getDisplayName() {
return firstName + " " + lastName;
}
}
public class Company extends Contact {
String name;
public String getDisplayName() {
return name;
}
}
The problem is that I need to make a query finding all contacts with displayName containing a certain string. I can't make the query using displayName because it is not part of either table. Any ideas on how to do this query?
Because you do the concatenation in the Java class, there is no way that Hibernate can really help you with this one, sorry. It can simply not see what you are doing in this method, since it is in fact not related to persistence at all.
The solution depends on how you mapped the inheritance of these classes:
If it is table-per-hierarchy you can use this approach: Write a SQL where clause for a criteria query, and then use a case statement:
s.createCriteria(Contact.class)
.add(Restrictions.sqlRestriction("? = case when type='Person' then firstName || ' '|| lastName else name end"))
.list();
If it is table per-concrete-subclass, then you are better of writing two queries (since that is what Hibernate will do anyway).
You could create a new column in the Contact table containing the respective displayName, which you could fill via a Hibernate Interceptor so it would always contain the right string automatically.
The alternative would be having two queries, one for the Person and one for the Company table, each containing the respective search logic. You may have to use native queries to achieve looking for a concatenated string via a LIKE query (I'm not a HQL expert, though, it may well be possible).
If you have large tables, you should alternatively think about full-text indexing, as LIKE '%...%' queries require a full table scan unless your database supports full text indexes.
If you change displayName to be a mapped property (set to the name column in Company and to a formula like first||' '||last in Person), then can query for Contract and Hibernate will run two queries both of which now have a displayName. You will get back a List of two Lists, one containing Companies and one containing Persons so you'll have to merge them back together. I think you need to query by the full package name of Contract or set up a typedef to tell Hibernate about it.