ManyToMany table with extra column Hibernate - java

I'm having an auto generated table by hibernate for the relationship between a User and Chat (a chat can have multiple users and a user can have multiple chats):
==User Model==
#Entity
public class User implements Serializable{
#Id
#GeneratedValue
private int userId;
private String username
==Chat Model==
#Entity
public class Chat implements Serializable{
#Id
#GeneratedValue
private int chatId;
private String subject;
#ManyToMany
private List<User> users;
#ManyToOne
private User created;
This generates a new table called Chat_User with the ID's of the user and the chat. Now I need another field (lastSeen) to be added in this generated table. How can this be realized? For now I have a new model that look's like the one below, but it is not working:
#Entity #Table(name = "Chat_User", catalog = "pdl") public class ChatUser implements Serializable {
#Id
#ManyToOne
#JoinColumn(name="users_userId", nullable=false)
private User user;
#Id
#ManyToOne
#JoinColumn(name="Chat_chatId", nullable=false)
private Chat chat;
#Temporal(javax.persistence.TemporalType.TIMESTAMP)
private Date lastSeen;
It will throw an exception: Unknown column 'lastSeen' in 'field list'. When I manually create this in the database it works somehow, but then it creates multiple entries (one with the lastSeen as value NULL and one with the correct value). Please help.

You would need to create an Embedable Class and use the Association override to override the join table. Click here for a link to sample code by mkyong . Let me know if you need any more help.

Related

Java entity modeling with table that relates to several others

I have a doubt about how the modeling of my entity would be. Come on, I have a table in the database that serves to save documents from my system, this table has the columns id, fk_id (element foreign key), fk_table (entity name) and file_name (stores the name of my file) .
I did a lot of research before posting my question here, but I didn't find anything related to it, what would my entities, user, patient and doctor?
DB:
id
fk_id
fk_table
file_name
1
21
user
test1.jpg
2
32
doctor
test2.pdf
3
61
user
test10.pdf
4
100
patient
test5.jpg
Class:
public class User{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String LastName;
// What would a one-to-many relationship look like?
}
public class patient{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
// What would a one-to-many relationship look like?
}
You can use #Where. But be aware that #Where is a Hibernate annotation. It's not in the JPA standard.
For example in the User entity: (I assume that your table is mapped to an entity called Document)
#Where( clause = "fk_table = 'user'")
#JoinColumn(name = "fk_id")
#OneToMany
private List<Document> documents = new ArrayList<>( );
The following is based only on standard JPA annotations. The idea is to create an inheritance hierarchy for the documents table. The base is:
#Entity
#Table(name = "XX_DOCUMENT")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "fk_table")
public abstract class BaseDocument {
#Id
#GeneratedValue(strategy=SEQUENCE)
private Long id;
#Column(name = "file_name")
private String fileName;
}
Here we define that all entities extending this will go to the same table, with the fk_table column to discriminate. The entities extending it are defined as follows:
#Entity
#DiscriminatorValue("doctor")
public class DoctorDocument extends BaseDocument {
#ManyToOne
#JoinColumn(name = "fk_id")
private Doctor doctor;
}
#Entity
#DiscriminatorValue("patient")
public class PatientDocument extends BaseDocument {
#ManyToOne
#JoinColumn(name = "fk_id")
private Patient patient;
}
// and so on
The interesting thing is that we are reusing the column fk_id to point to the right table. From a small experiment, Hibernate seems to not have problems with it. I would suggest that you manage the DB creation another way just to be safe.
The Doctor, Patient etc need not have a common base class, e.g.:
#Entity
#Table(name = "XX_DOCTOR")
public class Doctor {
#Id
#GeneratedValue(strategy=SEQUENCE)
private Long id;
#OneToMany(mappedBy = "doctor")
private Collection<DoctorDocument> documents = new ArrayList<>();
// any doctor-specific fields
}
#Entity
#Table(name = "XX_PATIENT")
public class Patient {
#Id
#GeneratedValue(strategy=SEQUENCE)
private Long id;
#OneToMany(mappedBy = "patient")
private Collection<PatientDocument> documents = new ArrayList<>();
// any patient-specific fields
}
// and so on
You can read a (doctor, patient, ...)'s documents from the relevant collection. You can even query BaseDocument instances based on any criteria.
You can even go ahead and do more fabcy stuff with the Java code. E.g. define an interface HasDocuments:
public interface HasDocuments<D extends BaseDocument> {
Collection<D> getDocuments();
}
Doctor, Patient, ..., implements this, so they can all be treated the same way.

Adding both object and foreign key to a Spring Data JPA entity

Below is an example of how I am defining my JPA entity. I am a bit confused on how to have both the object and the foreign key specified for ApplicationUser. I initially just had the ApplicationUser user property, but then realized I want to be able to query on the applicationUserId by defining a method in the ProductRepository such as findByApplicationUserId(long applicationUserId) and the repo keeps saying field does not exist. This is why I tried adding an applicationUserId property to the class and now there appears to be interference when running the application.
#Entity
public class Product{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private long applicationUserId;
#ManyToOne
#JoinColumn(name = "application_user_id")
private ApplicationUser user;
}

Different bidirectional #ManyToOne parent for 2 Entities extending some base Entity, but keeping base entity data in one table

This title is closest I could get to what I am trying to do. :)
Let's start with how it should look in database (tables with columns):
Email
- id : PK
- email : String
CompanyEmail
- email_id : FK
- company_id : FK
PersonEmail
- email_id : FK
- person_id : FK
Company
- id : PK
Person
- id : PK
Now let's look at model:
#Entity
public class Company
{
#Id
#GeneratedValue
private Long id;
#OneToMany
private List<CompanyEmail> emails = new ArrayList<>();
}
#Entity
public class Person
{
#Id
#GeneratedValue
private Long id;
#OneToMany
private List<PersonEmail> emails = new ArrayList<>();
}
// Should this be #Entity? Maybe #MappedSuperclass? What strategy to use to be able to extend it?
public class Email // This is base email class/entity
{
#Id
#GeneratedValue
private Long id;
#javax.validation.constraints.Email
private String email;
}
#Entity
public class PersonEmail // This needs to somehow extend Email, so all Email data is also accessible here.
{
#ManyToOne
private Person person;
}
#Entity
public class ComanyEmail // This needs to somehow extend Email, so all Email data is also accessible here.
{
#ManyToOne
private Company company;
}
Now my question is - is it possible in Hibernate (latest) to achieve such structure?
Key points when designing above (what drove me):
Keep ALL emails in one table, for sanity checks (uniqueness).
Have smallest database footprint - above gives just one table for Email and then 2 join tables with 2 FK.
Still be able to keep the model design Hibernate-friendly (basically don't use any special queries, just JPA annotations). This means that both Company and Person can easily LAZY load their specific emails (subclasses), and also those subclassed emails can correspond to them (PersonEmail to Person, and CompanyEmail to Company) - making model bidirectional.
Note: I've also considered creating something like Contactable base class for Comapny and Person which would have Email list, but that doesn't suit my needs (neither class per table, nor same table designs).
My question is - is it possible? Even if I don't get answer with example based on classes I gave, just the fact will give me hope and I will find it.
Edit 1
Should I maybe use discriminator in Email table and then keep FK there?
Email
- id : PK
- email : String
- companyOrPerson : FK
- discriminator : Decides what FK points at
Company
- id : PK
Person
- id : PK
Here I am grabbing straws - I have no idea if such thing is possible, can discriminator decide about parent table (companyOrPerson)? Does bidirectional work here (like I mentioned, maybe I should make some base class Contactable)?
I am open to suggestions on how to do it well.
Favour composition over inheritence. It tends to simplify things.
If you had main entities of
Person
Company
Email
Then a couple of composites
PersonEmail (contains a Person and an Email)
CompanyEmail (contains a Company and an Email)
Would that not fit what you need?
e.g.
#Entity
public class Person {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#OneToMany
#JoinColumn(name="person_id")
private List<PersonEmail> emails = new ArrayList<>();
}
#Entity
public class Company {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#OneToMany()
#JoinColumn(name="company_id")
private List<CompanyEmail> emails = new ArrayList<>();
}
#Entity
public class Email {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#javax.validation.constraints.Email
private String email;
}
#Entity
public class PersonEmail {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="person_id", referencedColumnName="id")
private Person person;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="email_id", referencedColumnName="id")
private Email email;
}
#Entity
public class CompanyEmail
{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="company_id", referencedColumnName="id")
private Company company;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="email_id", referencedColumnName="id")
private Email email;
}

#OneToMany does not create the join table

I'm new to JPA. Suppose I have these two entities:
//Imports
#Entity
#Table(name="article", schema = "sch_client")
public class Article implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int price;
private int amount;
//Getters & setters
}
And
#Entity
#Table(name="purchase", schema = "sch_client")
public class Purchase implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany
private List<Article> listArticle;}
I want to have something like a purchase contains many articles.
My question is: is it possible with only #OneToMany in Purchase class that points to Article class to have the desired relationship (a purchase contains many articles). Or to use a #OneToMany annotation I have to add a #ManyToOne on Article class. If so, why is is mandatory to add the #ManyToOne? any explanation please.
Thanks in advance.
First of all, I have write a misleading title, I will change it to make it more accurate:
Old title : In JPA, is it possible to use #OneToMany without using #ManyToOne?
New title : #OneToMany does not create the join table.
As I said, I'm new to JPA, my problem can appear dumb, I could delete the question, but I decided to keep it in case someone someday will face similar situation, it can help!
The join table of Purchase and Article was created every time I executed the code very normally, but I didn't notice!, I was checking the logs of NetBeans
and didn't see the join table, I was misled by those logs, I think that a join table doesn't appear in the logs (I hope that someone can confirm this information and make an edit of this answer).
I have created Purchase and Article in a new schema named: sch_sales. and the join table was created in public schema (PostgreSQL).
So, to make it more correct I added schema to #JoinTable as shown below, like this I will have all my tables in the same schema.
#Entity
#Table(name="purchase", schema = "sch_sales")
public class Purchase implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany
#JoinTable(name="join_purchase_article", schema = "sch_sales", joinColumns = #JoinColumn(name="sales_fk"), inverseJoinColumns = #JoinColumn(name="article_fk"))
private List<Article> listArticle;
}
UPDATE :
I was having a 3rd table created containing the id of Purchase and Article (a join table) which is obviously not correct.
The normal "behavior" is to have an id_purchase column added in Article, in this page I have find how to have such a result.
To have the desired result, I used the code below:
#Entity
#Table(name="purchase", schema = "sch_sales")
public class Purchase implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany
#JoinColumn(name="id_purchase")
private List<Article> listArticle;
}

Why in this Hibernate mapping it is used #ManyToOne instead #OneToOne?

I am absolutly new in Hibernate development and I have the following problem.
I have 2 entity classes that maps 2 DB tables:
1) The first entity class (the main one) is named KM_ProjectInfo and map a DB table named KM_PROJECT.
2) The second entity class is named KM_ProjectInfoStatus and map a DB table named KM_PROJECT_INFO_STATUS.
So the second one represent a specific field of the first one (a status of the row representd by an instance of the KM_ProjectInfo class). Infact I have something like this:
1) KM_ProjectInfo class:
#Entity
#Table(name = "KM_PROJECT")
public class KM_ProjectInfo implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfo;
#Column(name = "name")
private String name;
#Column(name = "technology")
private String technology;
#ManyToOne
#JoinColumn(name = "idCountry")
private KMCountry country;
#Column(name = "power")
private long power;
#Column(name = "cod")
private String cod;
#ManyToOne
#JoinColumn(name = "idProjectInfoStatus")
private KM_ProjectInfoStatus status;
// GETTERS & SETTERS
}
2) KM_ProjectInfoStatus:
#Entity
#Table(name = "KM_PROJECT_INFO_STATUS")
public class KM_ProjectInfoStatus implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfoStatus;
#Column(name = "foldertech")
private Long foldertech;
#Column(name = "folderproject")
private Long folderproject;
// GETTERS & SETTERS
}
So, as you can see in the previous snippet, the KM_ProjectInfoStatuss is a field of the KM_ProjectInfo because I want that it contains the primary key of this table as foreign key.
In the logic of my application I want that at one row of the KM_PROJECT table (so at one instance of the KM_ProjectInfo entity class) is associated a single row of the KM_PROJECT_INFO_STATUS (one instance of the KM_ProjectInfoStatus entity class) because it represent a specific status for the KM_PROJECT row.
In my code I have:
#ManyToOne
#JoinColumn(name = "idProjectInfoStatus")
private KM_ProjectInfoStatus status;
but I think that is wrong because at one row of my first table it is associated a specific single row of the second table. But maybe I am missing something about how Hibernate work.
Can you help me to understand what I am missing? What it work? Why I have #ManyToOne instead #OneToOne?
Tnx
It all depends on how you want to model things. In terms of Database structure, OneToOne and ManyToOne are implemented in the same way:
One or more JoinColumns which makes a foreign key pointing to the primary key of the other table.
So both solutions correctly map to your database, but it depends if you want to allow several KM_ProjectInfo to point to the same KM_ProjectInfoStatus, or only allow a single one.
Note that, even though you would declare a OneToOne, you could still end up with multiple KM_ProjectInfo pointing to the same KM_ProjectInfoStatus if you don't manipulate Hibernate properly.
Here you did not declare the reverse relationship, but if you did, the declaration would have to be different:
In case of a OneToOne, you would have a KM_ProjectInfo member
In case of a OneToMany (reverse of ManyToOne), you would have a Collection<KM_ProjectInfo> member
From the description it seems you want to have one-to-one relationship. That is the project entity should have its very own status not shared by any other project. You could achieve this by using #OneToOne as below.
#Entity
#Table(name = "KM_PROJECT")
public class KM_ProjectInfo implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfo;
#OneToOne
#JoinColumn(name = "idProjectInfoStatus")
private KM_ProjectInfoStatus status;
}
#Entity
#Table(name = "KM_PROJECT_INFO_STATUS")
public class KM_ProjectInfoStatus implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfoStatus;
#OneToOne(mappedBy="idProjectInfoStatus")
private KM_ProjectInfo project;
}
This way you can have specific status for the KM_PROJECT.
Coming back to #ManyToOne, you will want to have this if you want to share the same status with multiple projects, but that's not what you want in your case. I have tried to explain mappings in simple way here One-to-One mapping.

Categories

Resources