Multiple DAOs with dependencies (foreign key) - java

I am creating an application with database access. I do not use JPA nor JooQ or other frameworks for reasons (and do not want to. Also this is not important for my question). So I use JDBC and write plain sql.
My model looks like this:
public class Contact{
AddressBook addressBok;
String name;
...
}
I now created 2 DAOs, one for Contact and AddressBook. My Contact table has a foreign key to the AddressBook table (address_book_id).
I have a Service Class (e.g. ContactService) which would read each object using the corrsponding DAO and combine it to one Contact.
The Problem now is: I have the address_book_id in the ResultSet in the ContactDAO. How do I pass it over to the service class which then reads the correspondig AddressBook using the AddressBookDAO? As the model is shared, it is not a good solution to put a String addressBookId to Contact as clients using this model may do not know anything about the database.
I know these questions, but there is no answer on how to do it:
Using DAOs with composite Objects
Can a DAO call DAO?

The best practice is to use POJO domain object per each table where you can save your relationship fields like address_book_id. So you will have tree independent classes Contact, Address, AddressBook and independent DAO ContractDAO, AddressDAO, AddressBookDAO. Your ContactService will manipulate with these 6 objects to load, save, modify related data.

you can use the decorator pattern to wrap the entity(model class) that you want to add a foreign key to it, like this:
how to use:
for example: save(insert into db) a contact (here where I found the problem last time, so i fixed it by this solution)--> problem:how to insert a contact without knowing the fk address_book_id, in the args of save
public class ContactDAO extends DAO<Contact>{
// ....
#Override
public int save(IEntity contact){
ForeignKeyWrapper ctFk = (ForeignKeyWrapper)contact;
int address_book_id = ctFk.getFK();
Contact ct = (Contact)ctFk.getEntity();
String name = ct.getName();
// retrieve other fields of contact here
//use some sql to insert the contact in the db, now you have also the fk
String insertQuery = "INSERT INTO CONTACT(name,..other fields of contact.. , address_book_id) VALUES("+
name + "," +
/*other fields*/
address_book_id + ")";
//execute it here and fetch the id of the inserted row
}
// ....
}
public class clientClass{
//....
public static void main(String[] args) {
IEntity contact = new Contact(/* init fields*/);
IEntity addressBook = new AddressBook(/* init fields*/);
ForeignKeyWrapper ctFKW = new ForeignKeyWrapper(contact);
//link contact to addressBook (e.g: when creating a contact to insert it into the db)
ctFKW.setFK(addressBook.getId());
ContactDAO ctDAO = new ContactDAO(/*connection*/);
ctDAO.save(ctFKW);
}
//....
}
you can add a builder class to build your contact objects and link their foreign keys to addressBook objects Ids, to encapsulate the building logic, and simplify the process

Related

I have two entity they are connected by one to one mapping i want to save only one entity data and get the existing id of another entity and map

I have two entity they are connected by OnetoOne mapping i want to save only one entity data and get the data of another entity and store its primary key in our table how can i do please help?
#Entity
class Vehicle
{
#Id
#GeneratedValue(statergy=GenerationType.IDENTITY)
private int id;
#OneToOne
#JoinColumn(name="device_id",referencedColumnName = "id")
private Device deviceId;
}
If you mean you want to save a Vehicle in the database and have the Foreign Key be not null (meaning the Vehicle you want to save in DB, will have a Device mapped to it), you can do that by:
finding the Device in the database, then create a new Vehicle object (leave id as null, bcs it will be auto generated when you save it in the db).
After that just use a setter to set the Device into the Vehicle.
(ex: vehicle.setDevice(theDeviceObjecYouGotFromTheDatabase)).
A way to implement it would be this:
Note: A VehicleDTO would be recommended, but I simplified it. Also I used some weird names for the objects just to be more clear.
public Vehicle saveVehicle(Vehicle vehicleToBeSaved, Long deviceId) {
Device deviceThatWasInDb = this.deviceRepository.findById(deviceId)
.orElseThrow(() -> {
throw new EntityNotFoundException("Device with this id was not in the db");
});
// assuming that the vehicleToBeSaved has null id, you just need to use a setter to set the device field
vehicleToBeSaved.setDevice(deviceThatWasInDb);
Vehicle vehicleAfterBeingSaved = this.vehicleRepository.save(vehicleToBeSaved);
return vehicleAfterBeingSaved;
}
I assumed that we are in Service layer, and you already have created the VehicleRepository & DeviceRepository.
Hope this helps.

Spring Boot JPA: Mapping one entity to multiple (a lot) tables with same columns

I have a lot of (like 60+) tables which have the same schema and similar names:
log_2020_07_01
log_2020_07_02
... and so on.
They have the same columns: id, site, size. Each of the table contains around 2 million rows.
I've read Hibernate and tables with same data/columns but with different table names which suggests to use hibernate alone. I hope after seven years maybe there's something new we could do with JPA.
In JPA, is it possible to just write one entity class and let the code to handle which table to use?
E.G.,
for(int i=0;i<60;i++) {
// read from the i-th table.
}
First of all, we could use a middleware to make the sharding transparent to users for middle/large projects.
Here is a quick work around for my small project (and I'm the only developer working it):
Step 1, create an inteceptor:
public class MySqlInterceptor extends EmptyInterceptor {
private String entityName;
#Setter
private int tableId;
protected MySqlInterceptor() {
super();
}
public MySqlInterceptor(String entityName) {
this.entityName = entityName;
}
#Override
public String onPrepareStatement(String sql) {
// Here is the trick.
String modifiedSql = sql.replaceAll(entityName, entityName + tableId);
log.debug("{}", modifiedSql);
return modifiedSql;
}
}
Step 2, hock up the interceptor:
MySqlInterceptor mySqlInterceptor = new MySqlInterceptor("temp");
mySqlInterceptor.setTableId(tableId);
session = entityManagerFactory.unwrap(SessionFactory.class).withOptions().interceptor(mySqlInterceptor).openSession();
Explanation:
Hibernate is using JDBC to communicate with the database. The interceptor will change the table name in the sql from an entity's name (in my case it's temp) to a real table name (temp1, temp2, ...) at runtime.
P.S., be careful when you use multi-thread.

Spring Batch Writer for Parent Child Table Data Load in Batch

Being new to Spring Batch, I am making a good progress by exploring some POCs. One of the POCs I am currently working on is about reading data from csv file and loading into database tables which have parent child relationships.
CSV file now contains customers and orders info as below.
custname1, custaddress1, custzip1, orderdate11, ordervalue11, ordertax11
custname1, custaddress1, custzip1, orderdate12, ordervalue12, ordertax12
custname2, custaddress2, custzip2, orderdate21, ordervalue21, ordertax21
custname2, custaddress2, custzip2, orderdate22, ordervalue22, ordertax22
These data will be loaded into DB tables Customer, Order where CustId and OrderId will be auto generated by Oracle Sequences and table Order has CustId as a foreign key for maintaining 1 to many relationship ( 1 Customer can have multiple Orders )
FlatFileItemReader, FieldSetMapper, ItemWriter are the interfaces I am employing in this POC and I am using JdbcTemplate for DB operations.
I have following questions. Please clarify.
1) How to design my Reader bean?
The model class Customer has its members, getters and setters methods. Same with model class Order.
Should Customer class ( reading data from CSV file ) contain a member for Order class and corresponding getter, setter methods in order to represent Parent - Child Relationship?
2) How to retrieve primary key which I am currently creating it within the INSERT sql for Customer using sequence.nextval?
3) How to store all the Primary Keys from Parent data bulk load and then use them for foreign key in Child table data load ?
Thanks.
Consider persisting the data as Hibernate entities for this case. If you still want to use JdbcTemplate, here are some suggestions. This is a good example that i found for this - http://www.javaworld.com/article/2458888/spring-framework/open-source-java-projects-spring-batch.html
1) How to design my Reader bean?
The reader should just read the given given lines into a corresponding single bean (lets call it CustomerOrder). In the reader do not try to map to a parent-child bean structure.
2) How to retrieve primary key which I am currently creating it within
the INSERT sql for Customer using sequence.nextval?
Use SimpleJdbcInsert. In the ItemWriter, for each CustomerOrder bean, check if the Customer name is already there (I am assuming each Customer has unique name and address). Check this sample (note I have not executed/compiled it. It is just an illustration)
public class CustomerOrderWriter implements ItemWriter<CustomerOrder>
{
private static final String GET_CUSTOMER = "select * from CUSTOMER where name = ? and address =?";
private static final String INSERT_CUSTOMER = "insert into CUSTOMER (name,address) values (?,?)";
private static final String INSERT_ORDER = "insert into ORDER (customer_id, date, value, tax) values (?,?,?,?)";
#Autowired
private JdbcTemplate jdbcTemplate;
#Override
public void write(List<? extends CustomerOrder> customerOrders) throws Exception
{
//Process Write for each CSV row
for( CustomerOrder custOrd : customerOrders )
{
//Check if a customer exists
List<Customer> custList = jdbcTemplate.query(GET_CUSTOMER, new Object[] {custOrd.getName(), custOrd.getAddress() }, new RowMapper<Customer>() {
#Override
public Customer mapRow( ResultSet resultSet, int rowNum ) throws SQLException {
Customer c = new Customer ();
c.setId( resultSet.getInt( 1 ) );
return c;
}
});
//If customer already exists, just get the ID and insert the Order
if( custList.size() > 0 )
{
//Since the customer is found, insert the Order table here with the customer ID.
}
else
{
//Insert the customer, using "SimpleJdbcInsert", get the newly inserted customer Id and then insert the Order table
}
}
}
}
3) How to store all the Primary Keys from Parent data bulk load and
then use them for foreign key in Child table data load ?
If you follow design in answer 2, you don't need to do this.

OOAD Design issue

I have two tables: tblCustomer, tblProduct:
tblCustomer:
Id: Integer, auto-increament
Name: Varchar(30)
....
tblProduct
Id: Integer, auto-increament
Name: Varchar(50)
customerId: Integer
....
And two classes: Customer, Product:
public class Product
{
private int id;
private int name;
/* Other stuffs */
}
public class Customer
{
private int id;
private String name;
private String phoneNumber;
/* get-set and others stuffs */
public static boolean add(Customer cus) {
/* This is for insert a customer to tblCustomer */
}
public boolean addProduct(Product pd) {
/* This is for insert a product to tblProduct with current customer Id */
}
}
When customer register account, it call:
Customer cus = new Customer(/* ... */);
Customer.add(cus);
and when customer buy a product:
Product pd = new Product(/* ... */);
currentCustomer.addProduct(pd);
But my teacher said it not correct in OOAD (and even OOP) because Customer.addProduct is operate on tblProduct table, is it right? What is good design for this case?
** Update: **
Product haven't a pre-defined yet, when a customer buy a product, the store will make it and delivery to customer, so two same products is rare happen, so is tblCustomerProduct need?
What your teacher probably means is that you need a third table, named something like "CustomerProduct". This table contains the links between customers and which products they've bought.
tblCustomerProduct:
Id: Integer, auto-increament
CustomerId: Varchar(30)
ProductId: Varchar(30)
so, when a customer buys a product, you add this data to the table CustomerProduct. This will remove redundant data and also make deletion alot easier
Your teacher is right. It is not good Design because A class that you create should be cohesive. So that it should be responsible for doing the things that it can do. Here in your product class you have added customer id which is essentially a bad practice because A customer may purchase multiple products. This design will fail in that case. And also Product class doesn't need to know anything about the customer. All it has to know about is itself.
The relation between customer and product is an association which can be expressed as "customer buys product". So that customer id should not be there in product table.
Regarding the currentCustomer.addProduct(pd); method, it is not well suited because it is more suitable to product class rather than customer class.
The simple solution to your problem can be create a new class which can relate product and the customer.
For e.g.
CustomerProduct
customerid
productid
Inside this class you can add method like "AddProductForCustomer" and can write your database logic which will maintain the consistency.
Hope this clears your doubt.
Add a DAO tier that will contain the logical part of the methods save, delete, update, etc.
Here is how I usually do:
basepackage.domain: contains all your entities (data only, no logical part - in your case Product and Customer)
basepackage.dao: contains all your DAOs, only used to access the data, basically one per entity, each one containing methods such as findAll() : List<E>, findOne(K id) : E, save(E e) : void, etc.
basepackage.service: contains all your services, the logical part of the app. The services are the only ones that are calling the DAOs.
basepackage.presentation (or basepackage.web for a webapp): contains the HMI/web services/... implementation.
In the table, the customer is assigned to a product, but in the method you give a customer and add a product.
So I would go for either a method pd.setCustomer(currentCustomer) instead of currentCustomer.addProduct(pd)
Or change the customer field in the product table to a products field in the customer table.

GWT RPC match data for CellTable

I have a GWT 2.4 project using a CellTable.
It has columns like this (actually more):
LastName --- FirstName --- Departments
Smith Tom Research, Management
The names I get from a "User" object which is created on the server from my Database.
The DB looks like this:
users:
userID
firstName
lastName
departments:
departmentID
departmentName
user_in_department:
userID
departmentID
So what is the best way to get my departments show up in the table?
At the moment I fetch the User-list and the Department-list from the server using a RPC.
I thought about a 3rd RPC to get me the user-department relation and then match the names to the users on the client. What would be a good way to match that btw?
But even if I had the departments matched to my users, how would I add that info to the table?
For the names I can just do that:
TextColumn<User> firstNameColumn = new TextColumn<User>() {
#Override
public String getValue(User object) {
return object.getFirstName();
}
};
But as the departments aren't stored in the "User" object, I have no idea how to get them in the correct column and row.
I hope I've explained my issue good enough for you to understand :)
Assuming that your User object has a list of departments like so:
public ArrayList<Department> getDepartments() {
// ...
}
You can create a column to list the departments like so:
TextColumn<User> departmentsColumn = new TextColumn<User>() {
#Override
public String getValue(User object) {
StringBuilder departments = new StringBuilder();
for(int i=0; i < object.getDepartments().size(); i++) {
if (i>0) {
departments.append(",");
}
departments.append(object.getDepartments().get(i).getDepartmentName());
}
return departments.toString();
}
};
In addition to the first answer: since a CellTable is parameterized to display a list of objects of a given type in its rows that object should include the user name as well as a list of departments. You'll have to somehow make that data available through the User object, or create a new one (e.g. UserDepartmentDTO) as the elements in the underlying model.

Categories

Resources