3-column join table generated for simple ManyToMany relationship - java

I'm using these entities with JPA+Hibernate:
#Entity
public class Game {
#Id
#GeneratedValue
private long id;
#ManyToMany
#JoinTable(name="Game_admins")
private Set<User> admins = new HashSet<User>();
...
#Entity
public class User {
#Id
#GeneratedValue
private long id;
...
Thus far everything works well. Hibernate generates a join table called Game_admins with two colums, Game_id and admins_id.
However, if I add this to User:
#ManyToMany(mappedBy="admins")
private Set<Game> adminForGames = new HashSet<Game>();
The join table suddenly gets three columns, one of which is adminForGames_id. I don't need or want that, and I don't think it should be generated since I specify mappedBy. What am I doing wrong?

I believe you need to set the inverseJoin attriubtes
#ManyToMany
#JoinTable(name="Game_admins", joinColumns = {
#JoinColumn(name="admins_id")
},
inverseJoinColumns = {
#JoinColumn(name="Game_id")
}
private Set<User> admins = new HashSet<User>();

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.

JPA mapping, many to many with one extra column in the relation table

I have 3 sql tables:
Account (ID (BIGINT),...)
Card (ID (BIGINT),...)
RelationAccountCard (ACCOUNT (BIGINT), CARD(BIGINT), QUANTITY(int))
One account can have multiple cards, one card can have multiple account
#Data
#Entity
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToMany
#JoinTable(
name = "RELATION_ACCOUNT_CARD",
joinColumns = #JoinColumn(name = "ACCOUNT"),
inverseJoinColumns = #JoinColumn(name = "CARD"))
private Set<Card> cardsOwned = new HashSet<>();
}
#Data
#Entity
public class Card {
#Id
private long id;
#JsonIgnore
#ManyToMany(mappedBy = "cardsOwned")
private java.util.Set<Account> accounts;
}
This code above is working if the field "QUANTITY" in relationAccountCard didn't exist. I would like to make the code works knowing that I added the "QUANTITY" field. I tried for hours yesterday without success, do you have an idea of how to do this?
Basically I need to replace private Set<Card> cardsOwned; by private Set<RelationAccountCard> relationAccountCards; but I don't know how to
The solution was to delete the composite primary key. To make (card,account) unique, and to add an ID.
Once it was done, I simply followed the answer of this stackoverflow post:
Mapping many-to-many association table with extra column(s)

Automatically persisting reverse relationship in JPA

I have an entity User, that can have exactly one Company. I have a Company, that can be assigned to multiple User objects.
Currently if I want to persist a User, I need to get the Company (as it may exist without any User being assigned to it) and assign it. Further more I have to add the User manually to the Company using Company#addUser. Afterwards I save run CompanyRepository.save(company) (which should suffice to persist the User, too, I think, because I am using cascade = CascadeType.PERSIST).
Is there a way to say, that if I get the User and assign a Company to it, the "back-reference" is dealt with automatically? Or do I always have to get the Company and use Company#addUser to add that reference?
My entities look like this (I omitted more properties and reduced it to the most important properties and methods):
Company.java
package com.portal.user.persistence;
(imports omitted)
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder (toBuilder = true)
#Entity
#Table (name = "companies")
public class Company {
#Id
#GeneratedValue (generator = "uuid")
#GenericGenerator (name = "uuid", strategy = "uuid2")
#Column (name = "id")
private String id;
#Column (name = "ucid")
private String ucid;
#OneToMany (fetch = FetchType.LAZY, mappedBy = "company", cascade = CascadeType.PERSIST)
private List<User> users;
public void addUser(#NonNull User user) {
if (users == null) {
users = new ArrayList<>();
}
users.add(user);
}
public void removeUser(#NonNull User user) {
users.remove(user);
}
}
User.java
package com.portal.user.persistence;
(imports omitted)
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder (toBuilder = true)
#Entity
#Table (name = "users")
public class User {
#Id
#GeneratedValue (generator = "uuid")
#GenericGenerator (name = "uuid", strategy = "uuid2")
#Column (name = "id")
private String id;
#ManyToOne (cascade = CascadeType.PERSIST)
private Company company;
}
There are a lot of answers to your question, based on the implementation you would like to achieve.
The first way is to remove the #OneToMany relation in Company and the user list. In this way you would only have to manage one side of the relation, and when you need to search for all users in a company you could use a custom query performing a left join on users and companies tables.
The second way, keeping both side of the relation, is to implement a method 'setCompany' inside the User class like the following:
public void setCompany(Company c){
c.addUser(this);
this.company = c;
}
However in my experience, the first solution fits better since less relations will lead to a lot less work to do later on, especially regarding DTO conversion and deletion of elements from the DB.

Hibernate and multiple OneToOne without creating new columns

I have the following situations with multiple OneToOne reletanships:
#Table(name = "User")
public class User {
#OneToOne(mappedBy = "settingColumnName")
private Settings setting;
}
#Table(name = "Account")
public class Account {
#OneToOne(mappedBy = "settingColumnName")
private Settings setting;
}
#Table(name = "Settings")
public class Settings{
#OneToOne()
#JoinColumn(name = "userColumnName")
private User user;
#OneToOne()
#JoinColumn(name = "accountColumnName")
private Account account;
}
Now, the issue here is that I have to create and save each model independently, because they are created as a result of StreamEvent capturing. Also, Hibernate will create automatically userColumnName and accountColumnName. What I would really need to do is to have something this:
Is this possible to implement with Hibernate? Could someone provide an example?
Do
#JoinColumn(name="userColumnName", insertable=false,updatable=false),
#JoinColumn(name="accountColumnName", insertable=false,updatable=false),
And Add two more fields in Settings Entity for these tow column and Map with same Column

JPA and a novice's issue with relationship-mapping

I am trying to get the following type of mapping to work
Table event has the following columns:
id (PK)
prodgroup
errandtype
table errandtype : errandtype
table prodgroup: prodgroup
I have corresponding JPA classes
#Entity
#Table(name="event")
public class MyEvent {
#Id
int id;
// what mapping should go here?
Prodgroup prodgroup;
// what mapping should go here?
ErrandType errandtype;
}
#Entity
public class Prodgroup {
#Id
private String prodgroup;
}
#Entity
public class ErrandType {
#Id
private String errandtype;
}
Ok so questions are marked as comments in the code but I'll try to be explicit anyway.
In the above example I want my Prodgroup and ErrandType fields in the MyEvent class to be set to corresponding Prodgroup and Errandtype instances
I have tried #OneToOne relationships with #joincolumns and with mappedby attribute, but I just can't get it working and I've lost all sense of logical approach. My grasp of JPA entity mapping is clearly weak.
So can anyone bring some clarity?
It should be:
#Entity
#Table(name="event")
public class MyEvent {
#Id
int id;
// what mapping should go here?
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "prodgroup_id", insertable = true, updatable = true)
Prodgroup prodgroup;
// what mapping should go here?
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "errandtype_id", insertable = true, updatable = true)
ErrandType errandtype;
}
#Entity
public class Prodgroup {
#Id
private String prodgroup;
}
#Entity
public class ErrandType {
#Id
private String errandtype;
}
FetchType Eager means the object will be always loaded (would be "Lazy" by default if not specified).
CascadeType.ALL means mearge/persist/remove will be also done to linked tables.
Sebastian
Your table columns event.prodgroup and event.errandtype are foreign keys to respective tables (prodgroup, errandtype). So you need #ManyToOne association (because many events may share one prodgroup or errantype).

Categories

Resources