Now I have two tables, the first table called StudentBase and has three columns: id, firstname and lastname. The second table called ResearchAssistant and has two columns: id and course. I designed the tables like this because there are different kinds of students and research assistant is one of them. The two table could be joint together with the primary key id.
I'm writing an endpoint /researchAssistant and take following content as request body of POST method.
{
"firstname":"Jack",
"lastname":"Peter",
"course":"MATH"
}
What I want is that saving firstname and lastname into StudentBase table and save course into ResearchAssistant table. And generate a same id for both.
The first idea comes to my mind is building 3 model classes: StudentBase(id, firstname, lastname), ResearchAssistant(id, course) and ResearchAssistantMixed(firstname, lastname, course). I use ResearchAssistantMixed class as the request body class. After getting the data I will seperate it into a new StudentBase object and a ResearchAssistant object, then I store them seperately.
This process seems really stupid and the performance should be quite low. Do you have some better ideas? How does Spring Boot deal with such cases? Thank you!
This is a database problem and not a spring-boot problem. This is how I would approach (I'm assuming you're using some relational DB like MySql and hibernate for ORM):
Database Tables:
student_base
- id (primary key)
- first_name
_ last_name
research_assistant
- id (primary key)
- student_base_id (foreign key referencing id of student_base)
- course
You can now have equivalent entity classes in Java (for hibernate):
#Entity
#Table(name = "student_base")
public class StudentBase {
#Id
#Column(name = "id")
private Integer id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
// getters and setters
}
#Entity
#Table(name = "research_assistant")
public class ResearchAssistant {
#Id
#Column(name = "id")
private Integer id;
#ManyToOne(optional = false)
#JoinColumn(name = "student_base_id")
private StudentBase studentBase;
#Column(name = "course")
private String course;
// getters and setters
}
Now in your DAOs, you don't need to do much, just persist a student_base record and use the returned object to persist a research_assistant record. For example:
StudentBase studentBase = persist(new StudentBase(1, "abc", "xyz");
persist(new ResearchAssistant(1, studentBase, "pqr");
You can (and should) have two separate classes to accept the request object of the post API (don't use entity classes to accept request data).
Related
I am trying to insert a list of entities which have one to one relation to another entity. It is possible that the one to one mapped object would be same for many parent entity. I am expecting that the same child entity is referred in foreign keys of parent, but actually duplicate rows are getting created. Here are my Entities.
#Builder
#Entity
public class PaymentInfoType1 {
#Id
Long id;
LocalDate date;
#Column(precision = 15, scale = 2)
BigDecimal amount;
String reference;
#OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "account", referencedColumnName = "id")
Account account;
}
#Builder
#Entity
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Account {
#Id
Long id;
#EqualsAndHashCode.Include
String name;
#EqualsAndHashCode.Include
String accountId;
}
I am creating a list of PaymentInfoType1 based on the information received from a different system. Each PaymentInfoType1 get created along with its Account, which could have exactly the same info but different objects in realtime.
When i do:
PaymentInfoType1 first = // Created with some logic
Account account1 = // name = sample & accountId = 123
first.setAccount(account1);
PaymentInfoType1 second = // Created with some logic
Account account2 = // name = sample & accountId = 123
second.setAccount(account2);
// Both the above its own account object but the field have exactly same values.
List<PaymentInfoType1> list = List.of(first, second);
repo.saveAll(list);
I was expecting that there will be two rows in PaymentInfoType1 table and one in Account, but found that Account also has two rows. Looks like Equals and HashCode does not have any effect in this case.
How can handle this to not insert duplicate rows when the mapping objects are similar by equals/hashcode.
JPA does nothing with #EqualsAndHashcode (that just generates class methods equals and hashCode).
JPA identifies entities by entity id annotated with #Id (or #EmebeddedId) and this id is also something that can be implemented and checked - and usually also generated (like some db sequence) - in the database level.
If you want to use Account identified by name and accountId on JPA side you need to use #EmbeddedId and #Embeddable and get rid of #Id. This would be something like:
#Embeddable
public class AccountId {
String name;
String accountId; // maybe needs renaming...
}
and then in the Account:
#EmbeddedId
AccountId accountId;
See this for example
I'm new with Spring development and I'm trying to understand how the relation annotations work. I have two entity, User and Country. User has a #ManyToOne relation with Country.
User Entity
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private int id;
#Column(name = "name")
private String name;
#ManyToOne
#JoinColumn(name = "country", foreignKey = #ForeignKey(name = "FK_country"), referencedColumnName = "name")
private Country country;
//Other columns, Constructor, Getter, Setter omitted
}
Country Entity
#Entity
public class Country {
#Id
#Column(nullable=false)
private String name;
#Column(nullable=false)
private String continent;
//Other columns, Constructor, Getter, Setter omitted
}
It creates everything as I want in the database but my problem is when I'm trying to save the data from a form.
My form is taking as input a name and a selection between all the possible countries (previously loaded with a query). Once the name is provided and the country is selected, a row with name and a the id of the country should be written in my MySQL database.
I'm getting the following error:
java.lang.IllegalArgumentException: Parameter value [Spain] did not match expected type [com.model.Country(n/a)]
This is because it requires a Country type but I truly need to add a row with the name and the id associated to Spain. I don't need to create the whole Country object. I don't know what to do and if I'm doing correctly.
Can someone help me out, please?
Thanks in advance.
You need to save the Country class object instead of just country's name, As i could see that Country class is mapped with your User class.
For that fetch the Country Class object from the name/id of the country which you're receiving from your HTML and then save it along with your User details.
Something like this :-
User user = getUserById(user_id); //get user's details or create a new User's Object
Country country = getCountryById(country_id); //get Country's object using id or name you're receiving from your HTML-form
user.setCountry(country); //set Country's object in your User's object
//save user's object
Perhaps you can to try using custom Property Editor or Converter? It will convert string to your Country object.
Spring Custom Property Editor
I'm creating an application where one large aspect is the ability for users to share content with friends. I'm trying to represent this in the object model and I'm having trouble getting the association to work properly. I'm using a mapping table that records the friender and the friendee, both of which are represented by the primary key (id) of the user. A user can have many friends, and also be referenced by other users. This is what the schema looks like:
Users:
int user_id (PK)
varchar(32) email
varchar(64) password
Users_Map:
int users_map_id (PK)
int friendee_id (FK references users(user_id))
int friender_id (FK references users(user_id))
And this is how I have the User entity set up:
#Data
#Entity
#Table(name = "users")
public class User extends AbstractPersistable<Long> {
#Id
#Column(name = "user_id")
private Long id;
#Column
private String email;
#Column
private String password;
#OneToMany
#JoinTable(name = "users_map",
joinColumns = { #JoinColumn(name = "friender_id") },
inverseJoinColumns = { #JoinColumn(name = "friendee_id") })
private List<User> friends;
}
I run into the following error when deploying the application:
org.hibernate.AnnotationException: A Foreign key refering
com.x.webapp.data.entity.User from
com.x.webapp.data.entity.User has the wrong number of
column. should be 2
I've tried quite a few other configurations, including adding a "referencedColumnName" attribute to each #JoinColumn, but they have also yielded errors. I'm also not entirely sure whether the schema I currently have is the best way to go about mapping users together.
I appreciate any help!
Removing the extension of AbstractPersistable fixed the problem - that contained an #Id reference and clashed with the #Id reference I put inside of User.
I am trying to learn Hibernate and I could create some simple CRUD operation using a Single Class and Single Table. I am just reading the Hibernate Doc and some online tutorial.
But I have a problem on how to define this relationship with two tables involved. I basically have an Employee table with this structure.
CREATE TABLE EMPLOYEE
(
EMP_ID VARCHAR(10) NOT NULL,
EMP_FIRST_NAME VARCHAR(30) NOT NULL,
EMP_LAST_NAME VARCHAR(30) NOT NULL,
STATUS_ID INT NOT NULL,
PRIMARY KEY (EMP_ID)
);
The STATUS_ID field references another table. STATUS_DESC can either be 'PERMANENT', 'CONTRACTUAL', 'ON-DEMAND'
CREATE TABLE EMP_STATUS
(
STATUS_ID VARCHAR(10) NOT NULL,
STATUS_DESC VARCHAR(100) ,
PRIMARY KEY (STATUS_ID)
);
I am thinking of having an Entity class like this. Now my goal is to return list of Employee object with status, but I don't know how to go about on doing this.
#Entity
public class Employee{
//other private instance
private EmployeeStatus empStatus;
//getters and setters.
}
public class EmployeeStatus{
private int statusID;
private String statusDesc;
//getters and setters
}
You want to know how to map it? ManyToOne?
Employee.java
#Entity
public class Employee{
//other private instance
#JoinColumn(name = "empStatus", referencedColumnName = "yourColName")
#ManyToOne(optional = false)
private EmployeeStatus empStatus;
//getters and setters.
}
Dont forget to change "referencedColumnName" value...
EmployeeStatus.java
#Entity
public class EmployeeStatus{
#Id //this is your pk?
private int statusID;
private String statusDesc;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "empStatus", fetch = FetchType.LAZY) //or EAGER
private List<Employee> empList;
//getters and setters
}
To create a relationship between two tables you need to decide:
Is the relationship bi-directional? That is, do the statuses know the employees or not? If no then it is uni-directional. In that case you can add the annotation on the Employee class like this:
#ManyToOne
#JoinColumn(name = "status")
private EmployeeStatus empStatus;
And there is a few other options that you may add.
You can do what you are doing, but I would suggest, if the status can only be one of three values, create an Enum with the three values. No need for a separate table.
The downside for this is you need to create a hibernate custom type (the code is on the wiki) to support persisting enums.
A simpler answer is to not use a secondary table, and just save the status as a String on the domain object. You can put business logic on your model to ensure the String is in the list of acceptable values.
If you really want to use a relationship between two entities, then check out the hibernate docs on many-to-one relationships.
You can use HQL to query the entities. Like so
Query q = s.createQuery("from Employee as e where e.empStatus = :status");
q.setParameter("status", status);
List emps= q.list();
I'm currently trying to create a pair of Hibernate annotated classes to load (read only) from a pair of tables in a legacy system. The legacy system uses a consistent (if somewhat dated) approach to keying tables. The tables I'm attempting to map are as follows:
Customer CustomerAddress
-------------------------- ----------------------------
customerNumber:string (pk) customerNumber:string (pk_1)
name:string sequenceNumber:int (pk_2)
street:string
postalCode:string
I've approached this by creating a CustomerAddress class like this:
#Entity
#Table(name="CustomerAddress")
#IdClass(CustomerAddressKey.class)
public class CustomerAddress {
#Id
#AttributeOverrides({
#AttributeOverride(name = "customerNumber", column = #Column(name="customerNumber")),
#AttributeOverride(name = "sequenceNumber", column = #Column(name="sequenceNumber"))
})
private String customerNumber;
private int sequenceNumber;
private String name;
private String postalCode;
...
}
Where the CustomerAddressKey class is a simple Serializable object with the two key fields. The Customer object is then defined as:
#Entity
#Table(name = "Customer")
public class Customer {
private String customerNumber;
private List<CustomerAddress> addresses = new ArrayList<CustomerAddress>();
private String name;
...
}
So, my question is: how do I express the OneToMany relationship on the Customer table?
I may have an answer for my own question. Add the following to Customer:
#OneToMany(mappedBy="customer")
#JoinColumn(name="customerNumber")
List<CustomerAddress> addresses = new ArrayList<CustomerAddress>();
And the following to CustomerAddress:
#ManyToOne
#JoinColumn(name="customerNumber")
protected Customer customer;
This turns out to be a simpler problem to solve than I first thought. Typical.
I assume you have read the Bauer/King Hibernate book, which is extremely bad at explaining how do implement composite primary keys correctly. Don't let yourself be fooled by a bad book: composite primary key are not a property of legacy systems...