Represent sql tables with java classes - java

i've a problem understanding how to represent a sql table into my java web app and ho to handle the various types of relationship.
Let's suppose to have a department table:
CREATE TABLE IF NOT EXISTS `department` (
`name` varchar(15) NOT NULL,
`number` int(11) NOT NULL,
PRIMARY KEY (`number`),
) ENGINE=InnoDB;
A department has a lot of employees and one employee can work for only one department, so there is a 1:N relationship between department and employees. This could be the employees table:
CREATE TABLE IF NOT EXISTS `employee` (
`first_name` varchar(15) NOT NULL,
`last_name` varchar(15) NOT NULL,
`SSN` char(9) NOT NULL,
`address` varchar(30) DEFAULT NULL,
`gender` char(1) DEFAULT NULL,
`salary` decimal(10,0) DEFAULT NULL,
`N_D` int(11) NOT NULL,
PRIMARY KEY (`SSN`),
FOREIGN KEY `N_D` REFERENCES `department`(`number`),
) ENGINE=InnoDB;
No problem so far but let's go to java now. My application will have a DAO to read the selected elements from the database, maybe two class called DepartmentDAO and EmployeeDAO. My question is how i represent correctly the department entity and employee entity? How should i handle the relationship?
I've seen someone using the array for 1 to many relation and a single object for the opposite case:
public class Department{
private String name;
private Long number;
/*1:N*/
private Employee[] employees;
/*getter and setter*/
}
The employee class:
public class Employee{
private String firstName;
private String lastName;
private String SSN;
private String address;
private String gender;
private int salary;
/*N:1*/
private Department department;
/*getter and setter*/
}
It seems okay but how do i read nested objects? In the employee case i could use the join in my query for reading two object but in the other case i've got an array.
I want, for example, visualize the number of employees for a certain department. When i read the department object i should also read N employee objects and the by employees.length i could get the number. Isn't it a bit too expensive? It would be wrong put another attribute in my department class (private int numberOfEmployees;) and read it by using COUNT in sql?
Thanks in advance for the help.

If I got it right, you want to count the employees foreach department, or not?
If so, I recommend using the dao pattern.
In additon to the dao pattern, you can use a Key : Value List or Map like the Hashmap.
The Map should contain as Key the specific department and as value the employee. Besides, you have to synchronize the database and the Java Objects to add pairs to the map.
Summarized, you need to create a Data Management Class which contains a List or Map with a key (department) and a value (employee).
Besides, it must be able to do transactions with the database to synchronize the data.
Therefore, you can use the dao pattern and a HashMap, references below.
I do not know if this is good practicse, but it will work.
Dao Pattern
HashMap
EDIT: As I read your question again, the HashMap should be in your department class, but you can still use the dao pattern for synchronizing and data management.

Related

Why is a entity - value relationship implemented as a back reference in Spring Data JDBC

In Spring Data JDBC if an entity (e.g. Customer) has a value (e.g. Address) like in the example here the value has a back reference column (column customer in table address) to the entity in the db schema:
CREATE TABLE "customer" (
"id" BIGSERIAL NOT NULL,
"name" VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE "address" (
"customer" BIGINT,
"city" VARCHAR(255) NOT NULL
);
The problem with this is that if you use that Address value more than once in one entity or even in different entities you have to define an extra column for each usage. Only the primary id of the entity is stored in these columns and otherwise there is no way to distinguish from which entity it is. In my actual implementation I have five of these columns for the Address value:
"order_address" BIGINT, -- backreference for orderAddress to customer id
"service_address" BIGINT, -- backreference for serviceAddress to customer id
"delivery_address" BIGINT, -- backreference for deliveryAddress to customer id
"installation_address" BIGINT, -- backreference for installationAddress to provider_change id
"account_address" BIGINT, -- backreference for accountAddress to payment id
I understand how it works, but I don't understand the idea behind this back reference implementation. So can someone please shed some light on that issue? Thanks!
As to most good questions there are many sides to the answer.
The historical/symmetry answer
When it comes to references between entities Spring Data JDBC supports 1:1 (the one you ask about) and 1:N (lists, sets and maps).
For the latter anything but a back-reference is just weird/wrong.
And with using a back-reference for 1:1 becomes basically the same, simplifying the code, which is a good thing.
The DML process answer
With the back-reference, the process of inserting and deleting becomes much easier: Insert the aggregate root (customer in your example) first, then all the referenced entities. And it continues to work if those entities have further entities. Deletes work the other way round but are equally straight forward.
The dependency answer
Referenced entities in an aggregate can only exist as part of that aggregate. In that sense they depend on the aggregate root. Without that aggregate root there is no inner entity, while the aggregate root very often might just as well exist without the inner entity. It therefore makes sense, that the inner entity carries the reference.
The ID answer
With this design, the inner entity doesn't even need an id. It's identity is perfectly given by the identity of the aggregate root and in case of multiple one-to-one relationships to the same entity class, the back-reference column used.
Alternatives
All the reasons are more or less based on a single one-to-one relationship. I certainly agree that it looks a little weird for two such relationships to the same class and with 5 as in your example it becomes ridiculous. In such cases you might want to look in alternatives:
Use a map
Instead of modelling your Customer class like this:
class Customer {
#Id
Long id;
String name;
Address orderAddress
Address serviceAddress
Address deliveryAddress
Address installationAddress
Address accountAddress
}
Use a map like this
class Customer {
#Id
Long id;
String name;
Map<String,Address> addresses
}
Which would result in an address table like so
CREATE TABLE "address" (
"customer" BIGINT,
"customer_key" VARCHAR(20). NOT NULL,
"city" VARCHAR(255) NOT NULL
);
You may control the column names with a #MappedCollection annotation and you may add transient getter and setter for individual addresses if you want.
Make it a true value
You refer to Address as a value while I referred to it as an entity. If it should be considered a value I think you should map it as an embedded like so
class Customer {
#Id
Long id;
String name;
#Embedded(onEmpty = USE_NULL, prefix="order_")
Address orderAddress
#Embedded(onEmpty = USE_NULL, prefix="service_")
Address serviceAddress
#Embedded(onEmpty = USE_NULL, prefix="delivery_")
Address deliveryAddress
#Embedded(onEmpty = USE_NULL, prefix="installation_")
Address installationAddress
#Embedded(onEmpty = USE_NULL, prefix="account_")
Address accountAddress
}
This would make the address table superfluous since the data would be folded into the customer table:
CREATE TABLE "customer" (
"id" BIGSERIAL NOT NULL,
"name" VARCHAR(255) NOT NULL,
"order_city" VARCHAR(255) NOT NULL,
"service_city" VARCHAR(255) NOT NULL,
"deliver_city" VARCHAR(255) NOT NULL,
"installation_city" VARCHAR(255) NOT NULL,
"account_city" VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
Or is it an aggregate?
But maybe you need addresses on their own, not as part of a customer.
If that is the case an address is its own aggregate.
And references between aggregates should be modelled as ids or AggregateReference. This is described in more detail in Spring Data JDBC, References, and Aggregates

Entity class with a composite primary key doesn't have getter and setter

I have an entity class userdetails which has the username, userid (numeric) and password fields, with username and userid forming a composite primary key. This is negotiable, and possibly unimportant to the main problem.
I have another class, connectiontable, which has userid as the primary key. The sql code used to generate the relevant tables is as follows:
create table usertable
(
userid int NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
username varchar(128) NOT NULL UNIQUE,
password varchar(128) NOT NULL,
CONSTRAINT USER_PK PRIMARY KEY(username, userid)
);
That's the sql code for usertable. The following is for connectiontable
create table connectiontable
(
userid int not null,
username varchar(128) not null,
connections varchar(32670) not null,
CONSTRAINT CONNECTION_PK PRIMARY KEY(username, userid),
CONSTRAINT CONNECTION_FK FOREIGN KEY(username,userid) REFERENCES usertable(username,userid)
);
There are a bunch of other things in connectiontable, but those are irrelevant. I use netbeans 7.2.1 and Jave EE6. I use the 'create entities from database entries' but for some reason, I don't have a getter and setter for either userid or username. They are in connectiontablePK, but I can't seem to make use of that. For example, when I generate the jsf pages, I want to be able to do something like:
Connectiontable con = new Connectiontable();
con.getUsername();
But it complains because it can't find that method in connectiontable.java.
Can anyone advise me why this is the case, and how I can solve it? Thank you.
... You're not posting the Java code, which I suspect would help but:
Anytime in JPA when you have a composite primary key, you have to have an 'embedded' primary-key class. I suspect you have a class definition similar to the following:
#Embeddable
public class UserNameId {
private int userid;
private String username;
}
And then usertable and connectiontable both contain the following (or similar):
#Embedded
#Id
private UserNameId userNameId;
... So you should expect a getter/setter for userNameId, but not the embedded fields, like you expect.

JPA 2: Map Mapping fails for duplicate values

I'm trying to map a HashMap similar to the one that is specified as example 3 in the JavaDoc for #MapKeyJoinColumn (see http://www.objectdb.com/api/java/jpa/MapKeyJoinColumn):
#Entity
public class Student {
#Id int studentId;
...
#ManyToMany // students and courses are also many-many
#JoinTable(name="ENROLLMENTS",
joinColumns=#JoinColumn(name="STUDENT"),
inverseJoinColumns=#JoinColumn(name="SEMESTER"))
#MapKeyJoinColumn(name="COURSE")
Map<Course, Semester> enrollment;
...
}
The generated join table (generated with EclipseLink 2.3) has the following layout:
TABLE enrollments (
student_id bigint NOT NULL,
semester_id bigint NOT NULL,
course_id bigint,
CONSTRAINT enrollments_pkey PRIMARY KEY (student_id, semester_id)
)
Why is the primary key generated for Student and Semester and not for Student and Course? This doesn't make any sense in this case. With this primary key, a Student can participate in only one course per semester. 'student_id' and 'course_id' should be defined as primary key! This would also match the Java map definition (the key must be unique, but the same value may be assigned to different keys)
JPA sees the relationship as being between Student and Semester, as in a traditional #ManyToMany without the #MapKeyJoinColumn, and in traditional #ManyToMany duplicates would not be allowed, and the items are deleted by source/target ids, so the pk/index is desired to be on these.
For a finer level of control of the model, consider mapping the ENROLLMENTS table to an Enrollment Entity instead.
I can see from the Java model how you may desire different, so please log a bug/enhancement for this.

MYSQL stored procedure accessing java object stored as a BLOB

I am storing a Java object as an byte in a blob of a table. The java object is customized object. How can I construct the java object and use it in the stored procedure?
Let the class implement java.io.Serializable so that you can get an InputStream of it which you can store in the DB using CallableStatement#setBinaryStream().
That said, this is usually considered a bad design. If the class is actually a Javabean class, you'd better create a table with columns which represents the Javabean properties. E.g. a public class User { private Long id; private String name; private Integer age; } should be mapped to a table like CREATE TABLE user ( id BIGINT AUTO_INCREMENT, name VARCHAR, age INTEGER )
Edit as a reply on your comment: you thus basically want to store an array as binary object. This is a very bad idea. This way you cannot search for the array's data in the database and the database would also not be portable anymore. Just create a new table which represents each of the array items. Add an extra column to it which represents the ID of the parent object (actually, it should be the PK of the table to which the parent object containing the array is been mapped.
Example:
public class Parent {
private Long id;
private String someData;
private List<Child> children;
// Add/generate public getters/setters.
}
public class Child {
private Long id;
private String someData;
// Add/generate public getters/setters.
}
should be mapped to
CREATE TABLE parent (
id BIGINT NOT NULL AUTO_INCREMENT,
someData VARCHAR,
PRIMARY KEY (id)
);
CREATE TABLE child (
id BIGINT NOT NULL AUTO_INCREMENT,
parent_id BIGINT NOT NULL,
someData VARCHAR,
PRIMARY KEY (id),
FOREIGN KEY (parent_id) REFERENCES parent(id)
);
this way you can just select all with help of a JOIN clause. Check the SQL tutorial at w3schools.com and the vendor-specific SQL documentation for examples.
How can I construct the java object and use it in the stored procedure?
This is not possible, at least not with MySQL. Unlike Oracle which supports Java Stored Procedures, MySQL's stored procedure syntax is based on plain ANSI SQL standard. So I don't see how you could construct a Java object from the stream stored in the BLOB. What you can do is acces to the BLOB, but this won't help you much IMHO.
Actually, I think you are totally on the wrong path here, using a BLOB is not the right way to go (at least not here). If you need to persist objects that have a 1:n relation between them, you need to model your database accordingly.
If your Record class has a one to many relation with the User class, which is my understanding, then you have something like this on the Java side:
public class Record {
private Long id;
private User[];
//...
}
Then you need to create two tables at the database level, one for the records and another for the user(s), and model the relation between them using a foreign key (so you can "attach" a user to a record):
CREATE TABLE record
(
record_id INT NOT NULL,
...,
PRIMARY KEY (record_id)
) TYPE = INNODB;
CREATE TABLE user
(
user_id INT NOT NULL,
record_id INT,
...
PRIMARY KEY (user_id),
INDEX (record_id),
FOREIGN KEY (user_id) REFERENCES record (record_id)
) TYPE = INNODB;
Finally, when persisting a Record instance from Java, you'll need to write state in both tables.

How to make a weak entity using EJB 3.0

suppose I have an entity "Employee", and an entity "Address". An employee has an address associated to him/her. In a relational database sense, the Address table would be considered a weak entity, since an employee address cannot exist if there is no corresponding Employee in the database. Thus, suppose I want to model the following:
Table: Employee
EmployeeID | Name
Table Address
EmployeeID | StreetName
For the Employee table, the primary key is EmployeeID. For the Address table, the primary key is EmployeeID, yet it is also a foreign key to the Employee table. How would you model this in EJB 3.0 using j2ee? When I make an entity class called Employee, I can set the #id annotation above employeeId in order to mark that attribute as the primary key. However when I'm making an Address entity, I want to set the employeeID attribute as both a primary key and a foreign one (relate it to the Employee table). Is there an annotation for this? Hopefully I've made my question clear. Thanks.
db
It's probably not a good idea to map Address as a separate entity - not only is its lifetime controlled by its owner (e.g. Employee) as you said, but you also don't want to share the same Address instance between different employees.
Consider mapping it as #Embeddable instead:
#Embeddable
public class Address {
private String streetName;
...
}
#Entity
public class Employee {
#Embedded
private Address address;
...
}
Note that above will map Address columns to Employee table; if you'd rather keep a separate table you can overwrite that using #JoinTable annotation.

Categories